2 * metadata-verify.c: Metadata verfication support
5 * Mono Project (http://www.mono-project.com)
7 * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com)
10 #include <mono/metadata/object-internals.h>
11 #include <mono/metadata/verify.h>
12 #include <mono/metadata/verify-internals.h>
13 #include <mono/metadata/opcodes.h>
14 #include <mono/metadata/tabledefs.h>
15 #include <mono/metadata/reflection.h>
16 #include <mono/metadata/debug-helpers.h>
17 #include <mono/metadata/mono-endian.h>
18 #include <mono/metadata/metadata.h>
19 #include <mono/metadata/metadata-internals.h>
20 #include <mono/metadata/class-internals.h>
21 #include <mono/metadata/tokentype.h>
22 #include <mono/utils/strenc.h>
28 TODO add fail fast mode
29 TODO add PE32+ support
30 TODO verify the entry point RVA and content.
31 TODO load_section_table and load_data_directories must take PE32+ into account
32 TODO add section relocation support
33 TODO verify the relocation table, since we really don't use, no need so far.
34 TODO do full PECOFF resources verification
35 TODO verify in the CLI header entry point and resources
36 TODO implement null token typeref validation
37 TODO verify table wide invariants for typedef (sorting and uniqueness)
38 FIXME has_cattr coded index / 8 -> Permission table? -- it's decl security
39 FIXME use subtraction based bounds checking to avoid overflows
40 FIXME calc col size using coded_index_desc;
43 #ifdef MONO_VERIFIER_DEBUG
44 #define VERIFIER_DEBUG(code) do { code; } while (0)
46 #define VERIFIER_DEBUG(code)
49 #define INVALID_OFFSET ((guint32)-1)
53 RESOURCE_TABLE_IDX = 2,
54 RELOCATION_TABLE_IDX = 5,
76 COL_TYPE_DEF_OR_REF, /*includes typespec*/
79 COL_HAS_FIELD_MARSHAL,
80 COL_HAS_DECL_SECURITY,
81 COL_MEMBER_REF_PARENT,
83 COL_METHOD_DEF_OR_REF,
88 COL_TYPE_OR_METHOD_DEF,
103 const static unsigned char table_desc [] = {
105 #define MODULE_TABLE_DESC (0)
106 COL_UINT16, /*Generation*/
110 COL_GUID, /*EncBaseId*/
113 #define TYPEREF_TABLE_DESC (MODULE_TABLE_DESC + 6)
115 COL_RES_SCOPE, /*ResolutionScope*/
116 COL_STRING, /*TypeName*/
117 COL_STRING, /*TypeNameSpace*/
120 #define TYPEDEF_TABLE_DESC (TYPEREF_TABLE_DESC + 4)
122 COL_UINT32, /*Flags*/
123 COL_STRING, /*TypeName*/
124 COL_STRING, /*TypeNameSpace*/
125 COL_TYPE_DEF_OR_REF, /*Extends*/
126 COL_FIELD, /*FieldList*/
127 COL_METHOD_DEF, /*FieldList*/
130 #define TABLE_03_DESC (TYPEDEF_TABLE_DESC + 7)
131 /* 0x03 non documented extension */
134 #define FIELD_TABLE_DESC (TABLE_03_DESC + 1)
136 COL_UINT16, /*FieldAttributes*/
138 COL_BLOB, /*Signature*/
141 #define TABLE_05_DESC (FIELD_TABLE_DESC + 4)
142 /* 0x05 non documented extension */
145 #define METHODDEF_TABLE_DESC (TABLE_05_DESC + 1)
148 COL_UINT16, /*ImplFlags*/
149 COL_UINT16, /*Flags*/
151 COL_BLOB, /*Signature*/
152 COL_PARAM, /*ParamList*/
155 #define TABLE_07_DESC (METHODDEF_TABLE_DESC + 7)
156 /* 0x07 non documented extension */
159 #define PARAM_TABLE_DESC (TABLE_07_DESC + 1)
161 COL_UINT16, /*Flags*/
162 COL_UINT16, /*Sequence*/
166 #define INTERFACE_IMPL_TABLE_DESC (PARAM_TABLE_DESC + 4)
167 /* 0x09 InterfaceImpl */
168 COL_TYPE_DEF, /*Class*/
169 COL_TYPE_DEF_OR_REF, /*Interface*/
172 #define MEMBERREF_TABLE_DESC (INTERFACE_IMPL_TABLE_DESC + 3)
174 COL_MEMBER_REF_PARENT, /*Class*/
176 COL_BLOB, /*Signature*/
179 #define CONSTANT_TABLE_DESC (MEMBERREF_TABLE_DESC + 4)
182 COL_UINT8, /*Padding*/
183 COL_HAS_CONSTANT, /*Parent*/
187 #define CUSTOM_ATTRIBUTE_TABLE_DESC (CONSTANT_TABLE_DESC + 5)
188 /* 0x0C CustomAttribute */
189 COL_HAS_CATTR, /*Parent*/
190 COL_CATTR_TYPE, /*Type*/
194 #define FIELD_MARSHAL_TABLE_DESC (CUSTOM_ATTRIBUTE_TABLE_DESC + 4)
195 /* 0x0D FieldMarshal */
196 COL_HAS_FIELD_MARSHAL, /*Parent*/
197 COL_BLOB, /*NativeType*/
200 #define DECL_SECURITY_TABLE_DESC (FIELD_MARSHAL_TABLE_DESC + 3)
201 /* 0x0E DeclSecurity */
202 COL_UINT16, /*Action*/
203 COL_HAS_DECL_SECURITY, /*Parent*/
204 COL_BLOB, /*PermissionSet*/
207 #define CLASS_LAYOUT_TABLE_DESC (DECL_SECURITY_TABLE_DESC + 4)
208 /* 0x0F ClassLayout */
209 COL_UINT16, /*Packingsize*/
210 COL_UINT32, /*ClassSize*/
211 COL_TYPE_DEF, /*Parent*/
214 #define FIELD_LAYOUT_TABLE_DESC (CLASS_LAYOUT_TABLE_DESC + 4)
215 /* 0x10 FieldLayout */
216 COL_UINT32, /*Offset*/
220 #define STANDARD_ALONE_SIG_TABLE_DESC (FIELD_LAYOUT_TABLE_DESC + 3)
221 /* 0x11 StandAloneSig */
222 COL_BLOB, /*Signature*/
225 #define EVENT_MAP_TABLE_DESC (STANDARD_ALONE_SIG_TABLE_DESC + 2)
227 COL_TYPE_DEF, /*Parent*/
228 COL_EVENT, /*EventList*/
231 #define TABLE_13_DESC (EVENT_MAP_TABLE_DESC + 3)
232 /* 0x13 non documented extension */
235 #define EVENT_TABLE_DESC (TABLE_13_DESC + 1)
237 COL_UINT16, /*EventFlags*/
239 COL_TYPE_DEF_OR_REF, /*EventType*/
242 #define PROPERTY_MAP_TABLE_DESC (EVENT_TABLE_DESC + 4)
243 /* 0x15 PropertyMap */
244 COL_TYPE_DEF, /*Parent*/
245 COL_PROPERTY, /*PropertyList*/
248 #define TABLE_16_DESC (PROPERTY_MAP_TABLE_DESC + 3)
249 /* 0x16 non documented extension */
252 #define PROPERTY_TABLE_DESC (TABLE_16_DESC + 1)
254 COL_UINT16, /*Flags*/
256 COL_BLOB, /*Signature*/
259 #define METHOD_SEMANTICS_TABLE_DESC (PROPERTY_TABLE_DESC + 4)
260 /* 0x18 MethodSemantics */
261 COL_UINT16, /*Semantics*/
262 COL_METHOD_DEF, /*Method*/
263 COL_HAS_SEMANTICS, /*Association*/
266 #define METHOD_IMPL_TABLE_DESC (METHOD_SEMANTICS_TABLE_DESC + 4)
267 /* 0x19 MethodImpl */
268 COL_TYPE_DEF, /*Class*/
269 COL_METHOD_DEF_OR_REF, /*MethodBody*/
270 COL_METHOD_DEF_OR_REF, /*MethodDeclaration*/
273 #define MODULE_REF_TABLE_DESC (METHOD_IMPL_TABLE_DESC + 4)
278 #define TYPESPEC_TABLE_DESC (MODULE_REF_TABLE_DESC + 2)
280 COL_BLOB, /*Signature*/
283 #define IMPL_MAP_TABLE_DESC (TYPESPEC_TABLE_DESC + 2)
285 COL_UINT16, /*MappingFlags*/
286 COL_MEMBER_FORWARDED, /*MappingFlags*/
287 COL_STRING, /*ImportName*/
288 COL_MODULE_REF, /*ImportScope*/
291 #define FIELD_RVA_TABLE_DESC (IMPL_MAP_TABLE_DESC + 5)
297 #define TABLE_1E_DESC (IMPL_MAP_TABLE_DESC + 3)
301 #define TABLE_1F_DESC (TABLE_1E_DESC + 1)
305 #define ASSEMBLY_TABLE_DESC (TABLE_1F_DESC + 1)
307 COL_UINT32, /*HashAlgId*/
308 COL_UINT16, /*Major*/
309 COL_UINT16, /*Minor*/
310 COL_UINT16, /*Build*/
311 COL_UINT16, /*Revision*/
312 COL_UINT32, /*Flags*/
313 COL_BLOB, /*PublicKey*/
315 COL_STRING, /*Culture*/
318 #define ASSEMBLY_PROCESSOR_TABLE_DESC (ASSEMBLY_TABLE_DESC + 10)
319 /* 0x21 AssemblyProcessor */
320 COL_UINT32, /*Processor*/
323 #define ASSEMBLY_OS_TABLE_DESC (ASSEMBLY_PROCESSOR_TABLE_DESC + 2)
324 /* 0x22 AssemblyOS */
325 COL_UINT32, /*OSPlatformID*/
326 COL_UINT32, /*OSMajorVersion*/
327 COL_UINT32, /*OSMinorVersion*/
330 #define ASSEMBLY_REF_TABLE_DESC (ASSEMBLY_OS_TABLE_DESC + 4)
331 /* 0x23 AssemblyRef */
332 COL_UINT16, /*Major*/
333 COL_UINT16, /*Minor*/
334 COL_UINT16, /*Build*/
335 COL_UINT16, /*Revision*/
336 COL_UINT32, /*Flags*/
337 COL_BLOB, /*PublicKeyOrToken*/
339 COL_STRING, /*Culture*/
340 COL_BLOB, /*HashValue*/
343 #define ASSEMBLY_REF_PROCESSOR_TABLE_DESC (ASSEMBLY_REF_TABLE_DESC + 10)
344 /* 0x24 AssemblyRefProcessor */
345 COL_UINT32, /*Processor*/
346 COL_ASSEMBLY_REF, /*AssemblyRef*/
349 #define ASSEMBLY_REF_OS_TABLE_DESC (ASSEMBLY_REF_PROCESSOR_TABLE_DESC + 3)
350 /* 0x25 AssemblyRefOS */
351 COL_UINT32, /*OSPlatformID*/
352 COL_UINT32, /*OSMajorVersion*/
353 COL_UINT32, /*OSMinorVersion*/
354 COL_ASSEMBLY_REF, /*AssemblyRef*/
357 #define FILE_TABLE_DESC (ASSEMBLY_REF_OS_TABLE_DESC + 5)
359 COL_UINT32, /*Flags*/
361 COL_BLOB, /*HashValue*/
364 #define EXPORTED_TYPE_TABLE_DESC (FILE_TABLE_DESC + 4)
365 /* 0x27 ExportedType */
366 COL_UINT32, /*Flags*/
367 COL_UINT32, /*TypeDefId*/
368 COL_STRING, /*TypeName*/
369 COL_STRING, /*TypeNamespace*/
370 COL_IMPLEMENTATION, /*Implementation*/
373 #define MANIFEST_RESOURCE_TABLE_DESC (EXPORTED_TYPE_TABLE_DESC + 6)
374 /* 0x28 ManifestResource */
375 COL_UINT32, /*Offset*/
376 COL_UINT32, /*Flags*/
378 COL_IMPLEMENTATION, /*Implementation*/
381 #define NESTED_CLASS_TABLE_DESC (MANIFEST_RESOURCE_TABLE_DESC + 5)
382 /* 0x29 NestedClass */
383 COL_TYPE_DEF, /*NestedClass*/
384 COL_TYPE_DEF, /*EnclosingClass*/
387 #define GENERIC_PARAM_TABLE_DESC (NESTED_CLASS_TABLE_DESC + 3)
388 /* 0x2A GenericParam */
389 COL_UINT16, /*Number*/
390 COL_UINT16, /*Flags*/
391 COL_TYPE_OR_METHOD_DEF, /*Owner*/
395 #define METHOD_SPEC_TABLE_DESC (GENERIC_PARAM_TABLE_DESC + 5)
396 /* 0x2B MethodSpec */
397 COL_METHOD_DEF_OR_REF, /*Method*/
398 COL_BLOB, /*Instantiation*/
401 #define GENERIC_PARAM_CONSTRAINT_TABLE_DESC (METHOD_SPEC_TABLE_DESC + 3)
402 /* 0x2C GenericParamConstraint */
403 COL_GENERIC_PARAM, /*Owner*/
404 COL_TYPE_DEF_OR_REF, /*Constraint*/
408 const static unsigned char table_desc_start [] = {
415 METHODDEF_TABLE_DESC,
418 INTERFACE_IMPL_TABLE_DESC,
419 MEMBERREF_TABLE_DESC,
421 CUSTOM_ATTRIBUTE_TABLE_DESC,
422 FIELD_MARSHAL_TABLE_DESC,
423 DECL_SECURITY_TABLE_DESC,
424 CLASS_LAYOUT_TABLE_DESC,
425 FIELD_LAYOUT_TABLE_DESC,
426 STANDARD_ALONE_SIG_TABLE_DESC,
427 EVENT_MAP_TABLE_DESC,
430 PROPERTY_MAP_TABLE_DESC,
433 METHOD_SEMANTICS_TABLE_DESC,
434 METHOD_IMPL_TABLE_DESC,
435 MODULE_REF_TABLE_DESC,
438 FIELD_RVA_TABLE_DESC,
442 ASSEMBLY_PROCESSOR_TABLE_DESC,
443 ASSEMBLY_OS_TABLE_DESC,
444 ASSEMBLY_REF_TABLE_DESC,
445 ASSEMBLY_REF_PROCESSOR_TABLE_DESC,
446 ASSEMBLY_REF_OS_TABLE_DESC,
448 EXPORTED_TYPE_TABLE_DESC,
449 MANIFEST_RESOURCE_TABLE_DESC,
450 NESTED_CLASS_TABLE_DESC,
451 GENERIC_PARAM_TABLE_DESC,
452 METHOD_SPEC_TABLE_DESC,
453 GENERIC_PARAM_CONSTRAINT_TABLE_DESC
456 #define INVALID_TABLE (0xFF)
457 /*format: number of bits, number of tables, tables{n. tables} */
458 const static unsigned char coded_index_desc[] = {
459 #define TYPEDEF_OR_REF_DESC (0)
466 #define HAS_CONSTANT_DESC (TYPEDEF_OR_REF_DESC + 5)
473 #define HAS_CATTR_DESC (HAS_CONSTANT_DESC + 5)
481 MONO_TABLE_INTERFACEIMPL,
482 MONO_TABLE_MEMBERREF,
484 MONO_TABLE_DECLSECURITY,
487 MONO_TABLE_STANDALONESIG,
488 MONO_TABLE_MODULEREF,
491 MONO_TABLE_ASSEMBLYREF,
493 MONO_TABLE_EXPORTEDTYPE,
494 MONO_TABLE_MANIFESTRESOURCE,
496 #define HAS_FIELD_MARSHAL_DESC (HAS_CATTR_DESC + 21)
502 #define HAS_DECL_SECURITY_DESC (HAS_FIELD_MARSHAL_DESC + 4)
509 #define MEMBERREF_PARENT_DESC (HAS_DECL_SECURITY_DESC + 5)
518 #define HAS_SEMANTICS_DESC (MEMBERREF_PARENT_DESC + 7)
524 #define METHODDEF_OR_REF_DESC (HAS_SEMANTICS_DESC + 4)
528 MONO_TABLE_MEMBERREF,
530 #define MEMBER_FORWARDED_DESC (METHODDEF_OR_REF_DESC + 4)
536 #define IMPLEMENTATION_DESC (MEMBER_FORWARDED_DESC + 4)
540 MONO_TABLE_ASSEMBLYREF,
541 MONO_TABLE_EXPORTEDTYPE,
543 #define CATTR_TYPE_DESC (IMPLEMENTATION_DESC + 5)
549 MONO_TABLE_MEMBERREF,
552 #define RES_SCOPE_DESC (CATTR_TYPE_DESC + 7)
556 MONO_TABLE_MODULEREF,
557 MONO_TABLE_ASSEMBLYREF,
560 #define TYPE_OR_METHODDEF_DESC (RES_SCOPE_DESC + 6)
570 guint32 translated_offset;
582 guint32 rellocationsRVA;
583 guint16 numberOfRelocations;
597 guint32 section_count, tables_offset;
598 SectionHeader *sections;
599 gboolean wide_strings, wide_guid, wide_blob;
601 DataDirectory data_directories [16];
602 OffsetAndSize metadata_streams [5]; //offset from begin of the image
603 TableInfo tables [MONO_TABLE_NUM];
604 guint32 field_sizes [COL_LAST];
608 #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \
610 MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \
611 vinfo->info.status = __status; \
612 vinfo->info.message = ( __msg); \
613 vinfo->exception_type = (__exception); \
614 (__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo); \
618 #define ADD_ERROR(__ctx, __msg) \
620 ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
621 (__ctx)->valid = 0; \
625 #define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0)
627 #define CHECK_ERROR() do { if (!ctx->valid) return; } while (0)
630 pe_signature_offset (VerifyContext *ctx)
632 return read32 (ctx->data + 0x3c);
636 pe_header_offset (VerifyContext *ctx)
638 return read32 (ctx->data + 0x3c) + 4;
642 bounds_check_virtual_address (VerifyContext *ctx, guint32 rva, guint32 size)
649 for (i = 0; i < ctx->section_count; ++i) {
650 guint32 base = ctx->sections [i].baseRVA;
651 guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
652 if (rva >= base && rva + size <= end)
659 bounds_check_datadir (DataDirectory *dir, guint32 offset, guint32 size)
661 if (dir->translated_offset > offset)
663 if (dir->size < size)
665 return offset + size <= dir->translated_offset + dir->size;
669 bounds_check_offset (OffsetAndSize *off, guint32 offset, guint32 size)
671 if (off->offset > offset)
674 if (off->size < size)
677 return offset + size <= off->offset + off->size;
681 translate_rva (VerifyContext *ctx, guint32 rva)
688 for (i = 0; i < ctx->section_count; ++i) {
689 guint32 base = ctx->sections [i].baseRVA;
690 guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
691 if (rva >= base && rva <= end) {
692 guint32 res = (rva - base) + ctx->sections [i].baseOffset;
694 return res >= ctx->size ? INVALID_OFFSET : res;
698 return INVALID_OFFSET;
702 verify_msdos_header (VerifyContext *ctx)
706 ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header"));
707 if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a)
708 ADD_ERROR (ctx, g_strdup ("Invalid MS-DOS watermark"));
709 lfanew = pe_signature_offset (ctx);
710 if (lfanew > ctx->size - 4)
711 ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file"));
715 verify_pe_header (VerifyContext *ctx)
717 guint32 offset = pe_signature_offset (ctx);
718 const char *pe_header = ctx->data + offset;
719 if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0)
720 ADD_ERROR (ctx, g_strdup ("Invalid PE header watermark"));
724 if (offset > ctx->size - 20)
725 ADD_ERROR (ctx, g_strdup ("File with truncated pe header"));
726 if (read16 (pe_header) != 0x14c)
727 ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value"));
731 verify_pe_optional_header (VerifyContext *ctx)
733 guint32 offset = pe_header_offset (ctx);
734 guint32 header_size, file_alignment;
735 const char *pe_header = ctx->data + offset;
736 const char *pe_optional_header = pe_header + 20;
738 header_size = read16 (pe_header + 16);
741 if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/
742 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
744 if (offset > ctx->size - header_size || header_size > ctx->size)
745 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
747 if (read16 (pe_optional_header) == 0x10b) {
748 if (header_size != 224)
749 ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size));
751 /* LAMESPEC MS plays around this value and ignore it during validation
752 if (read32 (pe_optional_header + 28) != 0x400000)
753 ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));*/
754 if (read32 (pe_optional_header + 32) != 0x2000)
755 ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmnent %x", read32 (pe_optional_header + 32)));
756 file_alignment = read32 (pe_optional_header + 36);
757 if (file_alignment != 0x200 && file_alignment != 0x1000)
758 ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmnent %x", file_alignment));
759 /* All the junk in the middle is irrelevant, specially for mono. */
760 if (read32 (pe_optional_header + 92) > 0x10)
761 ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", read32 (pe_optional_header + 92)));
763 if (read16 (pe_optional_header) == 0x20B)
764 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+"));
766 ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header)));
771 load_section_table (VerifyContext *ctx)
774 SectionHeader *sections;
775 guint32 offset = pe_header_offset (ctx);
776 const char *ptr = ctx->data + offset;
777 guint16 num_sections = ctx->section_count = read16 (ptr + 2);
779 offset += 244;/*FIXME, this constant is different under PE32+*/
782 if (num_sections * 40 > ctx->size - offset)
783 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
785 sections = ctx->sections = g_new0 (SectionHeader, num_sections);
786 for (i = 0; i < num_sections; ++i) {
787 sections [i].size = read32 (ptr + 8);
788 sections [i].baseRVA = read32 (ptr + 12);
789 sections [i].baseOffset = read32 (ptr + 20);
790 sections [i].rellocationsRVA = read32 (ptr + 24);
791 sections [i].numberOfRelocations = read16 (ptr + 32);
795 ptr = ctx->data + offset; /*reset it to the beggining*/
796 for (i = 0; i < num_sections; ++i) {
797 guint32 raw_size, flags;
798 if (sections [i].baseOffset == 0)
799 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with intialized data only"));
800 if (sections [i].baseOffset >= ctx->size)
801 ADD_ERROR (ctx, g_strdup_printf ("Invalid PointerToRawData %x points beyond EOF", sections [i].baseOffset));
802 if (sections [i].size > ctx->size - sections [i].baseOffset)
803 ADD_ERROR (ctx, g_strdup ("Invalid VirtualSize points beyond EOF"));
805 raw_size = read32 (ptr + 16);
806 if (raw_size < sections [i].size)
807 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with SizeOfRawData < VirtualSize"));
809 if (raw_size > ctx->size - sections [i].baseOffset)
810 ADD_ERROR (ctx, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size));
812 if (sections [i].rellocationsRVA || sections [i].numberOfRelocations)
813 ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't handle section relocation"));
815 flags = read32 (ptr + 36);
816 /*TODO 0xFE0000E0 is all flags from cil-coff.h OR'd. Make it a less magical number*/
817 if (flags == 0 || (flags & ~0xFE0000E0) != 0)
818 ADD_ERROR (ctx, g_strdup_printf ("Invalid section flags %x", flags));
825 is_valid_data_directory (int i)
827 /*LAMESPEC 4 == certificate 6 == debug, MS uses both*/
828 return i == 1 || i == 2 || i == 5 || i == 12 || i == 14 || i == 4 || i == 6;
832 load_data_directories (VerifyContext *ctx)
834 guint32 offset = pe_header_offset (ctx) + 116; /*FIXME, this constant is different under PE32+*/
835 const char *ptr = ctx->data + offset;
838 for (i = 0; i < 16; ++i) {
839 guint32 rva = read32 (ptr);
840 guint32 size = read32 (ptr + 4);
842 if ((rva != 0 || size != 0) && !is_valid_data_directory (i))
843 ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d", i));
845 if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
846 ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d rva/size pair %x/%x", i, rva, size));
848 ctx->data_directories [i].rva = rva;
849 ctx->data_directories [i].size = size;
850 ctx->data_directories [i].translated_offset = translate_rva (ctx, rva);
856 #define SIZE_OF_MSCOREE (sizeof ("mscoree.dll"))
858 #define SIZE_OF_CORMAIN (sizeof ("_CorExeMain"))
861 verify_hint_name_table (VerifyContext *ctx, guint32 import_rva, const char *table_name)
864 guint32 hint_table_rva;
866 import_rva = translate_rva (ctx, import_rva);
867 g_assert (import_rva != INVALID_OFFSET);
869 hint_table_rva = read32 (ctx->data + import_rva);
870 if (!bounds_check_virtual_address (ctx, hint_table_rva, SIZE_OF_CORMAIN + 2))
871 ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint/Name rva %d for %s", hint_table_rva, table_name));
873 hint_table_rva = translate_rva (ctx, hint_table_rva);
874 g_assert (hint_table_rva != INVALID_OFFSET);
875 ptr = ctx->data + hint_table_rva + 2;
877 if (memcmp ("_CorExeMain", ptr, SIZE_OF_CORMAIN) && memcmp ("_CorDllMain", ptr, SIZE_OF_CORMAIN)) {
878 char name[SIZE_OF_CORMAIN];
879 memcpy (name, ptr, SIZE_OF_CORMAIN);
880 name [SIZE_OF_CORMAIN - 1] = 0;
881 ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint / Name: '%s'", name));
886 verify_import_table (VerifyContext *ctx)
888 DataDirectory it = ctx->data_directories [IMPORT_TABLE_IDX];
889 guint32 offset = it.translated_offset;
890 const char *ptr = ctx->data + offset;
891 guint32 name_rva, ilt_rva, iat_rva;
893 g_assert (offset != INVALID_OFFSET);
896 ADD_ERROR (ctx, g_strdup_printf ("Import table size %d is smaller than 40", it.size));
898 ilt_rva = read32 (ptr);
899 if (!bounds_check_virtual_address (ctx, ilt_rva, 8))
900 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Lookup Table rva %x", ilt_rva));
902 name_rva = read32 (ptr + 12);
903 if (!bounds_check_virtual_address (ctx, name_rva, SIZE_OF_MSCOREE))
904 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name rva %x", name_rva));
906 iat_rva = read32 (ptr + 16);
907 if (!bounds_check_virtual_address (ctx, iat_rva, 8))
908 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Address Table rva %x", iat_rva));
910 if (iat_rva != ctx->data_directories [IAT_IDX].rva)
911 ADD_ERROR (ctx, g_strdup_printf ("Import Address Table rva %x different from data directory entry %x", read32 (ptr + 16), ctx->data_directories [IAT_IDX].rva));
913 name_rva = translate_rva (ctx, name_rva);
914 g_assert (name_rva != INVALID_OFFSET);
915 ptr = ctx->data + name_rva;
917 if (memcmp ("mscoree.dll", ptr, SIZE_OF_MSCOREE)) {
918 char name[SIZE_OF_MSCOREE];
919 memcpy (name, ptr, SIZE_OF_MSCOREE);
920 name [SIZE_OF_MSCOREE - 1] = 0;
921 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name: '%s'", name));
924 verify_hint_name_table (ctx, ilt_rva, "Import Lookup Table");
926 verify_hint_name_table (ctx, iat_rva, "Import Address Table");
930 verify_resources_table (VerifyContext *ctx)
932 DataDirectory it = ctx->data_directories [RESOURCE_TABLE_IDX];
934 guint16 named_entries, id_entries;
935 const char *ptr, *root, *end;
941 ADD_ERROR (ctx, g_strdup_printf ("Resource section is too small, must be at least 16 bytes long but it's %d long", it.size));
943 offset = it.translated_offset;
944 root = ptr = ctx->data + offset;
945 end = root + it.size;
947 g_assert (offset != INVALID_OFFSET);
949 named_entries = read16 (ptr + 12);
950 id_entries = read16 (ptr + 14);
952 if ((named_entries + id_entries) * 8 + 16 > it.size)
953 ADD_ERROR (ctx, g_strdup_printf ("Resource section is too small, the number of entries (%d) doesn't fit on it's size %d", named_entries + id_entries, it.size));
955 /* XXX at least one unmanaged resource is added due to a call to AssemblyBuilder::DefineVersionInfoResource ()
956 if (named_entries || id_entries)
957 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support full verification of PECOFF resources"));
962 verify_cli_header (VerifyContext *ctx)
964 DataDirectory it = ctx->data_directories [CLI_HEADER_IDX];
970 ADD_ERROR (ctx, g_strdup_printf ("CLI header missing"));
973 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size in data directory %d must be 72", it.size));
975 offset = it.translated_offset;
976 ptr = ctx->data + offset;
978 g_assert (offset != INVALID_OFFSET);
980 if (read16 (ptr) != 72)
981 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size %d must be 72", read16 (ptr)));
983 if (!bounds_check_virtual_address (ctx, read32 (ptr + 8), read32 (ptr + 12)))
984 ADD_ERROR (ctx, g_strdup_printf ("Invalid medatata section rva/size pair %x/%x", read32 (ptr + 8), read32 (ptr + 12)));
986 if (!read32 (ptr + 8) || !read32 (ptr + 12))
987 ADD_ERROR (ctx, g_strdup_printf ("Missing medatata section in the CLI header"));
989 if ((read32 (ptr + 16) & ~0x0001000B) != 0)
990 ADD_ERROR (ctx, g_strdup_printf ("Invalid CLI header flags"));
993 for (i = 0; i < 6; ++i) {
994 guint32 rva = read32 (ptr);
995 guint32 size = read32 (ptr + 4);
997 if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
998 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli section %i rva/size pair %x/%x", i, rva, size));
1003 ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't support cli header section %d", i));
1008 pad4 (guint32 offset)
1010 if (offset & 0x3) //pad to the next 4 byte boundary
1011 offset = (offset & ~0x3) + 4;
1016 verify_metadata_header (VerifyContext *ctx)
1019 DataDirectory it = ctx->data_directories [CLI_HEADER_IDX];
1023 offset = it.translated_offset;
1024 ptr = ctx->data + offset;
1025 g_assert (offset != INVALID_OFFSET);
1027 //build a directory entry for the metadata root
1029 it.rva = read32 (ptr);
1031 it.size = read32 (ptr);
1032 it.translated_offset = offset = translate_rva (ctx, it.rva);
1034 ptr = ctx->data + offset;
1035 g_assert (offset != INVALID_OFFSET);
1038 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least 20 bytes required for initial decoding)", it.size));
1040 if (read32 (ptr) != 0x424A5342)
1041 ADD_ERROR (ctx, g_strdup_printf ("Invalid metadata signature, expected 0x424A5342 but got %08x", read32 (ptr)));
1043 offset = pad4 (offset + 16 + read32 (ptr + 12));
1045 if (!bounds_check_datadir (&it, offset, 4))
1046 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least %d bytes required for flags decoding)", it.size, offset + 4 - it.translated_offset));
1048 ptr = ctx->data + offset; //move to streams header
1050 if (read16 (ptr + 2) != 5)
1051 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section have %d streams (it must have exactly 5)", read16 (ptr + 2)));
1056 for (i = 0; i < 5; ++i) {
1057 guint32 stream_off, stream_size;
1058 int string_size, stream_idx;
1060 if (!bounds_check_datadir (&it, offset, 8))
1061 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small for initial decode of stream header %d, missing %d bytes", i, offset + 9 - it.translated_offset));
1063 stream_off = it.translated_offset + read32 (ptr);
1064 stream_size = read32 (ptr + 4);
1066 if (!bounds_check_datadir (&it, stream_off, stream_size))
1067 ADD_ERROR (ctx, g_strdup_printf ("Invalid stream header %d offset/size pair %x/%x", 0, stream_off, stream_size));
1072 for (string_size = 0; string_size < 32; ++string_size) {
1073 if (!bounds_check_datadir (&it, offset++, 1))
1074 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small to decode stream header %d name", i));
1075 if (!ptr [string_size])
1079 if (ptr [string_size])
1080 ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d name larger than 32 bytes", i));
1082 if (!strncmp ("#Strings", ptr, 9))
1083 stream_idx = STRINGS_STREAM;
1084 else if (!strncmp ("#US", ptr, 4))
1085 stream_idx = USER_STRINGS_STREAM;
1086 else if (!strncmp ("#Blob", ptr, 6))
1087 stream_idx = BLOB_STREAM;
1088 else if (!strncmp ("#GUID", ptr, 6))
1089 stream_idx = GUID_STREAM;
1090 else if (!strncmp ("#~", ptr, 3))
1091 stream_idx = TILDE_STREAM;
1093 ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr));
1095 if (ctx->metadata_streams [stream_idx].offset != 0)
1096 ADD_ERROR (ctx, g_strdup_printf ("Duplicated metadata stream header %s", ptr));
1098 ctx->metadata_streams [stream_idx].offset = stream_off;
1099 ctx->metadata_streams [stream_idx].size = stream_size;
1101 offset = pad4 (offset);
1102 ptr = ctx->data + offset;
1107 verify_tables_schema (VerifyContext *ctx)
1109 OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM];
1110 unsigned offset = tables_area.offset;
1111 const char *ptr = ctx->data + offset;
1112 guint64 valid_tables;
1116 if (tables_area.size < 24)
1117 ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for initial decoding (requires 24 bytes)", tables_area.size));
1120 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata major version %d, expected 2", ptr [4]));
1122 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata minor version %d, expected 0", ptr [5]));
1124 if ((ptr [6] & ~0x7) != 0)
1125 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata heap sizes 0x%02x, only bits 0, 1 and 2 can be set", ((unsigned char *) ptr) [6]));
1127 ctx->wide_strings = ptr [6] & 0x1;
1128 ctx->wide_guid = ptr [6] & 0x2;
1129 ctx->wide_blob = ptr [6] & 04;
1131 valid_tables = read64 (ptr + 8);
1133 for (i = 0; i < 64; ++i) {
1134 if (!(valid_tables & ((guint64)1 << i)))
1137 /*MS Extensions: 0x3 0x5 0x7 0x13 0x16
1138 Unused: 0x1E 0x1F 0x2D-0x3F
1139 We don't care about the MS extensions.*/
1140 if (i == 0x3 || i == 0x5 || i == 0x7 || i == 0x13 || i == 0x16)
1141 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifies doesn't support MS specific table %x", i));
1142 if (i == 0x1E || i == 0x1F || i >= 0x2D)
1143 ADD_ERROR (ctx, g_strdup_printf ("Invalid table %x", i));
1147 if (tables_area.size < 24 + count * 4)
1148 ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for decoding row counts (requires %d bytes)", tables_area.size, 24 + count * 4));
1152 for (i = 0; i < 64; ++i) {
1153 if (valid_tables & ((guint64)1 << i)) {
1154 ctx->tables [i].row_count = read32 (ptr);
1158 ctx->tables_offset = offset + 24 + count * 4;
1162 enc_index_size (guint32 bits, guint32 max)
1164 guint32 size = 1 << (16 - bits);
1165 return max >= size ? 4 : 2;
1169 calc_fields_size (VerifyContext *ctx)
1171 #define TS(T) (ctx->tables [T].row_count)
1172 #define MAX2(TA,TB) MAX (TS (TA), TS (TB))
1173 #define MAX3(TA,TB,TC) MAX (TS (TA), MAX (TS (TB), TS (TC)))
1174 #define TB_SIZE(T) (TS (T) >= (1 << 16) ? 4 : 2)
1177 memset (ctx->field_sizes, 0, sizeof (guint32) * COL_LAST);
1179 ctx->field_sizes [COL_UINT8] = 1;
1180 ctx->field_sizes [COL_UINT16] = 2;
1181 ctx->field_sizes [COL_UINT32] = 4;
1183 ctx->field_sizes [COL_STRING] = ctx->wide_strings ? 4 : 2;
1184 ctx->field_sizes [COL_GUID] = ctx->wide_guid ? 4 : 2;
1185 ctx->field_sizes [COL_BLOB] = ctx->wide_blob? 4 : 2;
1187 ctx->field_sizes [COL_TYPE_DEF_OR_REF] = enc_index_size (2, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_TYPEREF, MONO_TABLE_TYPESPEC));
1188 ctx->field_sizes [COL_HAS_CONSTANT] = enc_index_size (2, MAX3 (MONO_TABLE_FIELD, MONO_TABLE_PARAM, MONO_TABLE_PROPERTY));
1190 tmp = MAX3 (MONO_TABLE_METHOD, MONO_TABLE_FIELD, MONO_TABLE_TYPEREF);
1191 tmp = MAX (tmp, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_PARAM, MONO_TABLE_INTERFACEIMPL));
1192 tmp = MAX (tmp, MAX3 (MONO_TABLE_MEMBERREF, MONO_TABLE_MODULE, MONO_TABLE_DECLSECURITY));
1193 tmp = MAX (tmp, MAX3 (MONO_TABLE_PROPERTY, MONO_TABLE_EVENT, MONO_TABLE_STANDALONESIG));
1194 tmp = MAX (tmp, MAX3 (MONO_TABLE_MODULEREF, MONO_TABLE_TYPESPEC, MONO_TABLE_ASSEMBLY));
1195 tmp = MAX (tmp, MAX3 (MONO_TABLE_ASSEMBLYREF, MONO_TABLE_FILE, MONO_TABLE_EXPORTEDTYPE));
1196 tmp = MAX (tmp, MONO_TABLE_MANIFESTRESOURCE);
1197 ctx->field_sizes [COL_HAS_CATTR] = enc_index_size (5, tmp);
1199 ctx->field_sizes [COL_HAS_FIELD_MARSHAL] = enc_index_size (1, MAX2 (MONO_TABLE_FIELD, MONO_TABLE_PARAM));
1200 ctx->field_sizes [COL_HAS_DECL_SECURITY] = enc_index_size (2, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_METHOD, MONO_TABLE_ASSEMBLY));
1202 tmp = MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_TYPEREF, MONO_TABLE_MODULEREF);
1203 tmp = MAX (tmp, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_TYPESPEC));
1204 ctx->field_sizes [COL_MEMBER_REF_PARENT] = enc_index_size (3, tmp);
1206 ctx->field_sizes [COL_HAS_SEMANTICS] = enc_index_size (1, MAX2 (MONO_TABLE_EVENT, MONO_TABLE_PROPERTY));
1207 ctx->field_sizes [COL_METHOD_DEF_OR_REF] = enc_index_size (1, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_MEMBERREF));
1208 ctx->field_sizes [COL_MEMBER_FORWARDED] = enc_index_size (1, MAX2 (MONO_TABLE_FIELD, MONO_TABLE_METHOD));
1209 ctx->field_sizes [COL_IMPLEMENTATION] = enc_index_size (2, MAX3 (MONO_TABLE_FILE, MONO_TABLE_ASSEMBLYREF, MONO_TABLE_EXPORTEDTYPE));
1211 ctx->field_sizes [COL_CATTR_TYPE] = enc_index_size (3, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_MEMBERREF));
1212 ctx->field_sizes [COL_RES_SCOPE] = enc_index_size (2, MAX (MAX2 (MONO_TABLE_MODULE, MONO_TABLE_MODULEREF), MAX2 (MONO_TABLE_ASSEMBLYREF, MONO_TABLE_TYPEREF)));
1213 ctx->field_sizes [COL_TYPE_OR_METHOD_DEF] = enc_index_size (1, MAX2 (MONO_TABLE_TYPEDEF, MONO_TABLE_METHOD));
1215 ctx->field_sizes [COL_TYPE_DEF] = TB_SIZE (MONO_TABLE_TYPEDEF);
1216 ctx->field_sizes [COL_METHOD_DEF] = TB_SIZE (MONO_TABLE_METHOD);
1217 ctx->field_sizes [COL_FIELD] = TB_SIZE (MONO_TABLE_FIELD);
1218 ctx->field_sizes [COL_PARAM] = TB_SIZE (MONO_TABLE_PARAM);
1219 ctx->field_sizes [COL_PROPERTY] = TB_SIZE (MONO_TABLE_PROPERTY);
1220 ctx->field_sizes [COL_EVENT] = TB_SIZE (MONO_TABLE_EVENT);
1221 ctx->field_sizes [COL_GENERIC_PARAM] = TB_SIZE (MONO_TABLE_GENERICPARAM);
1222 ctx->field_sizes [COL_ASSEMBLY_REF] = TB_SIZE (MONO_TABLE_ASSEMBLYREF);
1223 ctx->field_sizes [COL_MODULE_REF] = TB_SIZE (MONO_TABLE_MODULEREF);
1228 calc_row_size (VerifyContext *ctx)
1231 guint64 total_size = 0;
1232 guint32 offset = ctx->tables_offset;
1234 for (idx = 0, i = 0; i < 0x2D; ++i) {
1237 while ((type = table_desc [idx++]) != COL_LAST)
1238 size += ctx->field_sizes [type];
1240 ctx->tables [i].row_size = size;
1241 ctx->tables [i].offset = offset;
1242 total_size += (guint64)size * ctx->tables [i].row_count;
1243 offset += size * ctx->tables [i].row_count;
1246 if (total_size > 0xFFFFFFFF)
1249 return (guint32)total_size;
1253 decode_row (VerifyContext *ctx, int desc_offset, TableInfo *table, int row, guint32 *res)
1255 const unsigned char *data = (unsigned char *)(ctx->data + table->offset);
1256 data += table->row_size * row;
1258 while (table_desc [desc_offset] != COL_LAST) {
1259 switch (ctx->field_sizes [table_desc [desc_offset++]]) {
1264 *res++ = read16 (data);
1268 *res++ = read32 (data);
1272 g_assert_not_reached ();
1278 get_col_offset (VerifyContext *ctx, int table, int column)
1280 guint32 desc_offset = table_desc_start [table];
1283 while (column-- > 0)
1284 offset += ctx->field_sizes [table_desc [desc_offset++]];
1290 get_col_size (VerifyContext *ctx, int table, int column)
1292 guint32 desc_offset = table_desc_start [table];
1293 guint32 type = table_desc [desc_offset + column];
1294 VERIFIER_DEBUG ( printf ("get_col_size table %d column %d type %d size %d\n", table, column, type, ctx->field_sizes [type]));
1297 return ctx->field_sizes [type];
1301 is_valid_non_empty_string (VerifyContext *ctx, guint32 offset)
1303 OffsetAndSize strings = ctx->metadata_streams [STRINGS_STREAM];
1305 const char *data = ctx->data + strings.offset;
1306 if (offset >= strings.size)
1308 if (data + offset < data) //FIXME, use a generalized and smart unsigned add with overflow check and fix the whole thing
1311 if (!mono_utf8_validate_and_len_with_bounds (data + offset, strings.size - offset, &length, NULL))
1317 is_valid_guid (VerifyContext *ctx, guint32 offset)
1319 OffsetAndSize guids = ctx->metadata_streams [GUID_STREAM];
1320 return guids.size >= 8 && guids.size - 8 >= offset;
1324 get_coded_index_token (VerifyContext *ctx, int token_kind, guint32 coded_token)
1326 guint32 bits = coded_index_desc [token_kind];
1327 return coded_token >> bits;
1331 make_coded_token (int kind, guint32 table, guint32 table_idx)
1333 guint32 bits = coded_index_desc [kind++];
1334 guint32 tables = coded_index_desc [kind++];
1336 for (i = 0; i < tables; ++i) {
1337 if (coded_index_desc [kind++] == table)
1338 return ((table_idx + 1) << bits) | i;
1340 g_assert_not_reached ();
1345 is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token)
1347 guint32 bits = coded_index_desc [token_kind++];
1348 guint32 table_count = coded_index_desc [token_kind++];
1349 guint32 table = coded_token & ((1 << bits) - 1);
1350 guint32 token = coded_token >> bits;
1352 if (table >= table_count)
1355 /*token_kind points to the first table idx*/
1356 table = coded_index_desc [token_kind + table];
1358 if (table == INVALID_TABLE)
1360 return token <= ctx->tables [table].row_count;
1370 token_locator (const void *a, const void *b)
1372 RowLocator *loc = (RowLocator *)a;
1373 unsigned const char *row = (unsigned const char *)b;
1374 guint32 token = loc->col_size == 2 ? read16 (row + loc->col_offset) : read32 (row + loc->col_offset);
1376 VERIFIER_DEBUG ( printf ("\tfound token %x\n", token) );
1377 return (int)loc->token - (int)token;
1381 search_sorted_table (VerifyContext *ctx, int table, int column, guint32 coded_token)
1383 TableInfo *tinfo = &ctx->tables [table];
1385 const char *res, *base;
1386 locator.token = coded_token;
1387 locator.col_offset = get_col_offset (ctx, table, column);
1388 locator.col_size = get_col_size (ctx, table, column);
1389 base = ctx->data + tinfo->offset;
1391 VERIFIER_DEBUG ( printf ("looking token %x table %d col %d rsize %d roff %d\n", coded_token, table, column, locator.col_size, locator.col_offset) );
1392 res = bsearch (&locator, base, tinfo->row_count, tinfo->row_size, token_locator);
1396 return (res - base) / tinfo->row_count;
1399 /*WARNING: This function doesn't verify if the strings @offset points to a valid string*/
1401 string_cmp (VerifyContext *ctx, const char *str, guint offset)
1404 return strcmp (str, "");
1406 return strcmp (str, ctx->data + ctx->metadata_streams [STRINGS_STREAM].offset + offset);
1410 typedef_is_system_object (VerifyContext *ctx, guint32 *data)
1412 return ctx->is_corlib && !string_cmp (ctx, "System", data [MONO_TYPEDEF_NAME]) && !string_cmp (ctx, "Object", data [MONO_TYPEDEF_NAMESPACE]);
1416 verify_module_table (VerifyContext *ctx)
1418 TableInfo *table = &ctx->tables [MONO_TABLE_MODULE];
1419 guint32 data [MONO_MODULE_SIZE];
1421 if (table->row_count != 1)
1422 ADD_ERROR (ctx, g_strdup_printf ("Module table must have exactly one row, but have %d", table->row_count));
1424 decode_row (ctx, MODULE_TABLE_DESC, table, 0, data);
1426 if (!is_valid_non_empty_string (ctx, data [MONO_MODULE_NAME]))
1427 ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid name, string index 0x%08x", data [MONO_MODULE_NAME]));
1429 if (!is_valid_guid (ctx, data [MONO_MODULE_MVID]))
1430 ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid Mvid, guid index %x", data [MONO_MODULE_MVID]));
1432 if (data [MONO_MODULE_ENC] != 0)
1433 ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero Enc field %x", data [MONO_MODULE_ENC]));
1435 if (data [MONO_MODULE_ENCBASE] != 0)
1436 ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero EncBase field %x", data [MONO_MODULE_ENCBASE]));
1440 verify_typeref_table (VerifyContext *ctx)
1442 TableInfo *table = &ctx->tables [MONO_TABLE_TYPEREF];
1443 guint32 data [MONO_TYPEREF_SIZE];
1446 for (i = 0; i < table->row_count; ++i) {
1447 decode_row (ctx, TYPEREF_TABLE_DESC, table, i, data);
1448 if (!is_valid_coded_index (ctx, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE]))
1449 ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d coded index 0x%08x", i, data [MONO_TYPEREF_SCOPE]));
1451 if (!get_coded_index_token (ctx, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE]))
1452 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", i));
1454 if (!data [MONO_TYPEREF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAME]))
1455 ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d name token 0x%08x", i, data [MONO_TYPEREF_NAME]));
1457 if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE]))
1458 ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d namespace token 0x%08x", i, data [MONO_TYPEREF_NAMESPACE]));
1462 /*bits 9,11,14,15,19,21,24-31 */
1463 #define INVALID_TYPEDEF_FLAG_BITS ((1 << 9) | (1 << 11) | (1 << 14) | (1 << 15) | (1 << 19) | (1 << 21) | 0xFF000000)
1465 verify_typedef_table (VerifyContext *ctx)
1467 TableInfo *table = &ctx->tables [MONO_TABLE_TYPEDEF];
1468 guint32 data [MONO_TYPEDEF_SIZE];
1469 guint32 fieldlist = 1, methodlist = 1;
1472 if (table->row_count == 0)
1473 ADD_ERROR (ctx, g_strdup_printf ("Typedef table must have exactly at least one row"));
1475 for (i = 0; i < table->row_count; ++i) {
1476 decode_row (ctx, TYPEDEF_TABLE_DESC, table, i, data);
1477 if (data [MONO_TYPEDEF_FLAGS] & INVALID_TYPEDEF_FLAG_BITS)
1478 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid flags field 0x%08x", i, data [MONO_TYPEDEF_FLAGS]));
1480 if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_LAYOUT_MASK) == 0x18)
1481 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid class layout 0x18", i));
1483 if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == 0x30000)
1484 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i));
1486 if ((data [MONO_TYPEDEF_FLAGS] & 0xC00000) != 0)
1487 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i));
1489 if (!data [MONO_TYPEDEF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEDEF_NAME]))
1490 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid name token %08x", i, data [MONO_TYPEDEF_NAME]));
1492 if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE]))
1493 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid namespace token %08x", i, data [MONO_TYPEREF_NAMESPACE]));
1496 if (data [MONO_TYPEDEF_EXTENDS] != 0)
1497 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row 0 for the special <module> type must have a null extend field"));
1499 if (typedef_is_system_object (ctx, data) && data [MONO_TYPEDEF_EXTENDS] != 0)
1500 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for System.Object must have a null extend field", i));
1502 if (data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_INTERFACE) {
1503 if (data [MONO_TYPEDEF_EXTENDS])
1504 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must have a null extend field", i));
1505 if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_ABSTRACT) == 0)
1506 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must be abstract", i));
1508 if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]))
1509 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d extend field coded index 0x%08x", i, data [MONO_TYPEDEF_EXTENDS]));
1511 if (!get_coded_index_token (ctx, TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]))
1512 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for non-interface type must have a non-null extend field", i));
1516 if (data [MONO_TYPEDEF_FIELD_LIST] == 0)
1517 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList be be >= 1", i));
1519 if (data [MONO_TYPEDEF_FIELD_LIST] < fieldlist)
1520 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_TYPEDEF_FIELD_LIST], fieldlist));
1522 if (data [MONO_TYPEDEF_METHOD_LIST] == 0)
1523 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList be be >= 1", i));
1525 if (data [MONO_TYPEDEF_METHOD_LIST] < methodlist)
1526 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_TYPEDEF_METHOD_LIST], methodlist));
1528 fieldlist = data [MONO_TYPEDEF_FIELD_LIST];
1529 methodlist = data [MONO_TYPEDEF_METHOD_LIST];
1534 #define INVALID_FIELD_FLAG_BITS ((1 << 3) | (1 << 11) | (1 << 14))
1536 verify_field_table (VerifyContext *ctx)
1538 TableInfo *table = &ctx->tables [MONO_TABLE_FIELD];
1539 guint32 data [MONO_TABLE_FIELD], flags;
1542 for (i = 0; i < table->row_count; ++i) {
1543 decode_row (ctx, FIELD_TABLE_DESC, table, i, data);
1544 flags = data [MONO_FIELD_FLAGS];
1546 if (flags & INVALID_FIELD_FLAG_BITS)
1547 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid flags field 0x%08x", i, flags));
1549 if ((flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == 0x7)
1550 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid field visibility 0x7", i));
1552 if ((flags & (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY)) == (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY))
1553 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d cannot be InitOnly and Literal at the same time", i));
1555 if ((flags & FIELD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & FIELD_ATTRIBUTE_SPECIAL_NAME))
1556 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is RTSpecialName but not SpecialName", i));
1558 if ((flags & FIELD_ATTRIBUTE_LITERAL) && !(flags & FIELD_ATTRIBUTE_STATIC))
1559 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but not Static", i));
1561 if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL) &&
1562 search_sorted_table (ctx, MONO_TABLE_FIELDMARSHAL, MONO_FIELD_MARSHAL_PARENT, make_coded_token (HAS_FIELD_MARSHAL_DESC, MONO_TABLE_FIELD, i)) == -1)
1563 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has FieldMarshal but there is no corresponding row in the FieldMarshal table", i));
1565 if ((flags & FIELD_ATTRIBUTE_HAS_DEFAULT) &&
1566 search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1)
1567 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i));
1569 if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_RVA) &&
1570 search_sorted_table (ctx, MONO_TABLE_FIELDRVA, MONO_FIELD_RVA_FIELD, i + 1) == -1)
1571 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i));
1573 if (!data [MONO_FIELD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_FIELD_NAME]))
1574 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid name token %08x", i, data [MONO_FIELD_NAME]));
1580 verify_tables_data (VerifyContext *ctx)
1582 OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM];
1583 guint table_area_size;
1584 calc_fields_size (ctx);
1585 table_area_size = calc_row_size (ctx);
1587 if (table_area_size == 0)
1588 ADD_ERROR (ctx, g_strdup_printf ("table space is either empty or overflowed"));
1590 if (!bounds_check_offset (&tables_area, ctx->tables_offset, table_area_size))
1591 ADD_ERROR (ctx, g_strdup_printf ("Tables data require %d bytes but the only %d are available in the #~ stream", table_area_size, tables_area.size - (ctx->tables_offset - tables_area.offset)));
1593 verify_module_table (ctx);
1595 verify_typeref_table (ctx);
1597 verify_typedef_table (ctx);
1599 verify_field_table (ctx);
1603 mono_image_verify (const char *data, guint32 size, gboolean is_corlib)
1606 memset (&ctx, 0, sizeof (VerifyContext));
1610 ctx.is_corlib = is_corlib;
1612 verify_msdos_header (&ctx);
1614 verify_pe_header (&ctx);
1616 verify_pe_optional_header (&ctx);
1618 load_section_table (&ctx);
1620 load_data_directories (&ctx);
1622 verify_import_table (&ctx);
1624 /*No need to check the IAT directory entry, it's content is indirectly verified by verify_import_table*/
1625 verify_resources_table (&ctx);
1627 verify_cli_header (&ctx);
1629 verify_metadata_header (&ctx);
1631 verify_tables_schema (&ctx);
1633 verify_tables_data (&ctx);
1636 g_free (ctx.sections);