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>
27 TODO add fail fast mode
28 TODO add PE32+ support
29 TODO verify the entry point RVA and content.
30 TODO load_section_table and load_data_directories must take PE32+ into account
31 TODO add section relocation support
32 TODO verify the relocation table, since we really don't use, no need so far.
33 TODO do full PECOFF resources verification
34 TODO verify in the CLI header entry point and resources
35 FIXME has_cattr coded index / 8 -> Permission table? -- it's decl security
36 FIXME use subtraction based bounds checking to avoid overflows
39 #define INVALID_OFFSET ((guint32)-1)
43 RESOURCE_TABLE_IDX = 2,
44 RELOCATION_TABLE_IDX = 5,
66 COL_TYPE_DEF_OR_REF, /*includes typespec*/
69 COL_HAS_FIELD_MARSHAL,
70 COL_HAS_DECL_SECURITY,
71 COL_MEMBER_REF_PARENT,
73 COL_METHOD_DEF_OR_REF,
78 COL_TYPE_OR_METHOD_DEF,
93 const static unsigned char table_desc [] = {
95 COL_UINT16, /*Generation*/
99 COL_GUID, /*EncBaseId*/
103 COL_RES_SCOPE, /*ResolutionScope*/
104 COL_STRING, /*TypeName*/
105 COL_STRING, /*TypeNameSpace*/
109 COL_UINT32, /*Flags*/
110 COL_STRING, /*TypeName*/
111 COL_STRING, /*TypeNameSpace*/
112 COL_TYPE_DEF_OR_REF, /*Extends*/
113 COL_FIELD, /*FieldList*/
114 COL_METHOD_DEF, /*FieldList*/
117 /* 0x03 non documented extension */
121 COL_UINT16, /*FieldAttributes*/
123 COL_BLOB, /*Signature*/
126 /* 0x05 non documented extension */
131 COL_UINT16, /*ImplFlags*/
132 COL_UINT16, /*Flags*/
134 COL_BLOB, /*Signature*/
135 COL_PARAM, /*ParamList*/
138 /* 0x07 non documented extension */
142 COL_UINT16, /*Flags*/
143 COL_UINT16, /*Sequence*/
147 /* 0x09 InterfaceImpl */
148 COL_TYPE_DEF, /*Class*/
149 COL_TYPE_DEF_OR_REF, /*Interface*/
153 COL_MEMBER_REF_PARENT, /*Class*/
155 COL_BLOB, /*Signature*/
159 COL_HAS_CONSTANT, /*Parent*/
163 /* 0x0C CustomAttribute */
164 COL_HAS_CATTR, /*Parent*/
165 COL_CATTR_TYPE, /*Type*/
169 /* 0x0D FieldMarshal */
170 COL_HAS_FIELD_MARSHAL, /*Parent*/
171 COL_BLOB, /*NativeType*/
174 /* 0x0E DeclSecurity */
175 COL_UINT16, /*Action*/
176 COL_HAS_DECL_SECURITY, /*Parent*/
177 COL_BLOB, /*PermissionSet*/
180 /* 0x0F ClassLayout */
181 COL_UINT16, /*Packingsize*/
182 COL_UINT32, /*ClassSize*/
183 COL_TYPE_DEF, /*Parent*/
186 /* 0x10 FieldLayout */
187 COL_UINT32, /*Offset*/
191 /* 0x11 StandAloneSig */
192 COL_BLOB, /*Signature*/
196 COL_TYPE_DEF, /*Parent*/
197 COL_EVENT, /*EventList*/
200 /* 0x13 non documented extension */
204 COL_UINT16, /*EventFlags*/
206 COL_TYPE_DEF_OR_REF, /*EventType*/
209 /* 0x15 PropertyMap */
210 COL_TYPE_DEF, /*Parent*/
211 COL_PROPERTY, /*PropertyList*/
214 /* 0x16 non documented extension */
218 COL_UINT16, /*Flags*/
220 COL_BLOB, /*Signature*/
223 /* 0x18 MethodSemantics */
224 COL_UINT16, /*Semantics*/
225 COL_METHOD_DEF, /*Method*/
226 COL_HAS_SEMANTICS, /*Association*/
229 /* 0x19 MethodImpl */
230 COL_TYPE_DEF, /*Class*/
231 COL_METHOD_DEF_OR_REF, /*MethodBody*/
232 COL_METHOD_DEF_OR_REF, /*MethodDeclaration*/
240 COL_BLOB, /*Signature*/
244 COL_UINT16, /*MappingFlags*/
245 COL_MEMBER_FORWARDED, /*MappingFlags*/
246 COL_STRING, /*ImportName*/
247 COL_MODULE_REF, /*ImportScope*/
262 COL_UINT32, /*HashAlgId*/
263 COL_UINT16, /*Major*/
264 COL_UINT16, /*Minor*/
265 COL_UINT16, /*Build*/
266 COL_UINT16, /*Revision*/
267 COL_UINT32, /*Flags*/
268 COL_BLOB, /*PublicKey*/
270 COL_STRING, /*Culture*/
273 /* 0x21 AssemblyProcessor */
274 COL_UINT32, /*Processor*/
277 /* 0x22 AssemblyOS */
278 COL_UINT32, /*OSPlatformID*/
279 COL_UINT32, /*OSMajorVersion*/
280 COL_UINT32, /*OSMinorVersion*/
283 /* 0x23 AssemblyRef */
284 COL_UINT16, /*Major*/
285 COL_UINT16, /*Minor*/
286 COL_UINT16, /*Build*/
287 COL_UINT16, /*Revision*/
288 COL_UINT32, /*Flags*/
289 COL_BLOB, /*PublicKeyOrToken*/
291 COL_STRING, /*Culture*/
292 COL_BLOB, /*HashValue*/
295 /* 0x24 AssemblyRefProcessor */
296 COL_UINT32, /*Processor*/
297 COL_ASSEMBLY_REF, /*AssemblyRef*/
300 /* 0x25 AssemblyRefOS */
301 COL_UINT32, /*OSPlatformID*/
302 COL_UINT32, /*OSMajorVersion*/
303 COL_UINT32, /*OSMinorVersion*/
304 COL_ASSEMBLY_REF, /*AssemblyRef*/
308 COL_UINT32, /*Flags*/
310 COL_BLOB, /*HashValue*/
313 /* 0x27 ExportedType */
314 COL_UINT32, /*Flags*/
315 COL_UINT32, /*TypeDefId*/
316 COL_STRING, /*TypeName*/
317 COL_STRING, /*TypeNamespace*/
318 COL_IMPLEMENTATION, /*Implementation*/
321 /* 0x28 ManifestResource */
322 COL_UINT32, /*Offset*/
323 COL_UINT32, /*Flags*/
325 COL_IMPLEMENTATION, /*Implementation*/
328 /* 0x29 NestedClass */
329 COL_TYPE_DEF, /*NestedClass*/
330 COL_TYPE_DEF, /*EnclosingClass*/
333 /* 0x2A GenericParam */
334 COL_UINT16, /*Number*/
335 COL_UINT16, /*Flags*/
336 COL_TYPE_OR_METHOD_DEF, /*Owner*/
340 /* 0x2B MethodSpec */
341 COL_METHOD_DEF_OR_REF, /*Method*/
342 COL_BLOB, /*Instantiation*/
345 /* 0x2C GenericParamConstraint */
346 COL_GENERIC_PARAM, /*Owner*/
347 COL_TYPE_DEF_OR_REF, /*Constraint*/
355 guint32 translated_offset;
367 guint32 rellocationsRVA;
368 guint16 numberOfRelocations;
381 guint32 section_count, tables_offset;
382 SectionHeader *sections;
383 gboolean wide_strings, wide_guid, wide_blob;
385 DataDirectory data_directories [16];
386 OffsetAndSize metadata_streams [5]; //offset from begin of the image
387 TableInfo tables [MONO_TABLE_NUM];
388 guint32 field_sizes [COL_LAST];
391 #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \
393 MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \
394 vinfo->info.status = __status; \
395 vinfo->info.message = ( __msg); \
396 vinfo->exception_type = (__exception); \
397 (__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo); \
401 #define ADD_ERROR(__ctx, __msg) \
403 ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
404 (__ctx)->valid = 0; \
408 #define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0)
410 #define CHECK_ERROR() do { if (!ctx->valid) return; } while (0)
413 pe_signature_offset (VerifyContext *ctx)
415 return read32 (ctx->data + 0x3c);
419 pe_header_offset (VerifyContext *ctx)
421 return read32 (ctx->data + 0x3c) + 4;
425 bounds_check_virtual_address (VerifyContext *ctx, guint32 rva, guint32 size)
432 for (i = 0; i < ctx->section_count; ++i) {
433 guint32 base = ctx->sections [i].baseRVA;
434 guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
435 if (rva >= base && rva + size <= end)
442 bounds_check_datadir (DataDirectory *dir, guint32 offset, guint32 size)
444 if (dir->translated_offset > offset)
446 if (dir->size < size)
448 return offset + size <= dir->translated_offset + dir->size;
452 bounds_check_offset (OffsetAndSize *off, guint32 offset, guint32 size)
454 if (off->offset > offset)
457 if (off->size < size)
460 return offset + size <= off->offset + off->size;
464 translate_rva (VerifyContext *ctx, guint32 rva)
471 for (i = 0; i < ctx->section_count; ++i) {
472 guint32 base = ctx->sections [i].baseRVA;
473 guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
474 if (rva >= base && rva <= end) {
475 guint32 res = (rva - base) + ctx->sections [i].baseOffset;
477 return res >= ctx->size ? INVALID_OFFSET : res;
481 return INVALID_OFFSET;
485 verify_msdos_header (VerifyContext *ctx)
489 ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header"));
490 if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a)
491 ADD_ERROR (ctx, g_strdup ("Invalid MS-DOS watermark"));
492 lfanew = pe_signature_offset (ctx);
493 if (lfanew > ctx->size - 4)
494 ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file"));
498 verify_pe_header (VerifyContext *ctx)
500 guint32 offset = pe_signature_offset (ctx);
501 const char *pe_header = ctx->data + offset;
502 if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0)
503 ADD_ERROR (ctx, g_strdup ("Invalid PE header watermark"));
507 if (offset > ctx->size - 20)
508 ADD_ERROR (ctx, g_strdup ("File with truncated pe header"));
509 if (read16 (pe_header) != 0x14c)
510 ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value"));
514 verify_pe_optional_header (VerifyContext *ctx)
516 guint32 offset = pe_header_offset (ctx);
517 guint32 header_size, file_alignment;
518 const char *pe_header = ctx->data + offset;
519 const char *pe_optional_header = pe_header + 20;
521 header_size = read16 (pe_header + 16);
524 if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/
525 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
527 if (offset > ctx->size - header_size || header_size > ctx->size)
528 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
530 if (read16 (pe_optional_header) == 0x10b) {
531 if (header_size != 224)
532 ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size));
534 /*if (read32 (pe_optional_header + 28) != 0x400000)
535 ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));*/
536 if (read32 (pe_optional_header + 32) != 0x2000)
537 ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmnent %x", read32 (pe_optional_header + 32)));
538 file_alignment = read32 (pe_optional_header + 36);
539 if (file_alignment != 0x200 && file_alignment != 0x1000)
540 ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmnent %x", file_alignment));
541 /* All the junk in the middle is irrelevant, specially for mono. */
542 if (read32 (pe_optional_header + 92) > 0x10)
543 ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", read32 (pe_optional_header + 92)));
545 if (read16 (pe_optional_header) == 0x20B)
546 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+"));
548 ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header)));
553 load_section_table (VerifyContext *ctx)
556 SectionHeader *sections;
557 guint32 offset = pe_header_offset (ctx);
558 const char *ptr = ctx->data + offset;
559 guint16 num_sections = ctx->section_count = read16 (ptr + 2);
561 offset += 244;/*FIXME, this constant is different under PE32+*/
564 if (num_sections * 40 > ctx->size - offset)
565 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
567 sections = ctx->sections = g_new0 (SectionHeader, num_sections);
568 for (i = 0; i < num_sections; ++i) {
569 sections [i].size = read32 (ptr + 8);
570 sections [i].baseRVA = read32 (ptr + 12);
571 sections [i].baseOffset = read32 (ptr + 20);
572 sections [i].rellocationsRVA = read32 (ptr + 24);
573 sections [i].numberOfRelocations = read16 (ptr + 32);
577 ptr = ctx->data + offset; /*reset it to the beggining*/
578 for (i = 0; i < num_sections; ++i) {
579 guint32 raw_size, flags;
580 if (sections [i].baseOffset == 0)
581 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with intialized data only"));
582 if (sections [i].baseOffset >= ctx->size)
583 ADD_ERROR (ctx, g_strdup_printf ("Invalid PointerToRawData %x points beyond EOF", sections [i].baseOffset));
584 if (sections [i].size > ctx->size - sections [i].baseOffset)
585 ADD_ERROR (ctx, g_strdup ("Invalid VirtualSize points beyond EOF"));
587 raw_size = read32 (ptr + 16);
588 if (raw_size < sections [i].size)
589 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with SizeOfRawData < VirtualSize"));
591 if (raw_size > ctx->size - sections [i].baseOffset)
592 ADD_ERROR (ctx, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size));
594 if (sections [i].rellocationsRVA || sections [i].numberOfRelocations)
595 ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't handle section relocation"));
597 flags = read32 (ptr + 36);
598 /*TODO 0xFE0000E0 is all flags from cil-coff.h OR'd. Make it a less magical number*/
599 if (flags == 0 || (flags & ~0xFE0000E0) != 0)
600 ADD_ERROR (ctx, g_strdup_printf ("Invalid section flags %x", flags));
607 is_valid_data_directory (int i)
609 /*LAMESPEC 4 == certificate 6 == debug, MS uses both*/
610 return i == 1 || i == 2 || i == 5 || i == 12 || i == 14 || i == 4 || i == 6;
614 load_data_directories (VerifyContext *ctx)
616 guint32 offset = pe_header_offset (ctx) + 116; /*FIXME, this constant is different under PE32+*/
617 const char *ptr = ctx->data + offset;
620 for (i = 0; i < 16; ++i) {
621 guint32 rva = read32 (ptr);
622 guint32 size = read32 (ptr + 4);
624 if ((rva != 0 || size != 0) && !is_valid_data_directory (i))
625 ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d", i));
627 if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
628 ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d rva/size pair %x/%x", i, rva, size));
630 ctx->data_directories [i].rva = rva;
631 ctx->data_directories [i].size = size;
632 ctx->data_directories [i].translated_offset = translate_rva (ctx, rva);
638 #define SIZE_OF_MSCOREE (sizeof ("mscoree.dll"))
640 #define SIZE_OF_CORMAIN (sizeof ("_CorExeMain"))
643 verify_hint_name_table (VerifyContext *ctx, guint32 import_rva, const char *table_name)
646 guint32 hint_table_rva;
648 import_rva = translate_rva (ctx, import_rva);
649 g_assert (import_rva != INVALID_OFFSET);
651 hint_table_rva = read32 (ctx->data + import_rva);
652 if (!bounds_check_virtual_address (ctx, hint_table_rva, SIZE_OF_CORMAIN + 2))
653 ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint/Name rva %d for %s", hint_table_rva, table_name));
655 hint_table_rva = translate_rva (ctx, hint_table_rva);
656 g_assert (hint_table_rva != INVALID_OFFSET);
657 ptr = ctx->data + hint_table_rva + 2;
659 if (memcmp ("_CorExeMain", ptr, SIZE_OF_CORMAIN) && memcmp ("_CorDllMain", ptr, SIZE_OF_CORMAIN)) {
660 char name[SIZE_OF_CORMAIN];
661 memcpy (name, ptr, SIZE_OF_CORMAIN);
662 name [SIZE_OF_CORMAIN - 1] = 0;
663 ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint / Name: '%s'", name));
668 verify_import_table (VerifyContext *ctx)
670 DataDirectory it = ctx->data_directories [IMPORT_TABLE_IDX];
671 guint32 offset = it.translated_offset;
672 const char *ptr = ctx->data + offset;
673 guint32 name_rva, ilt_rva, iat_rva;
675 g_assert (offset != INVALID_OFFSET);
678 ADD_ERROR (ctx, g_strdup_printf ("Import table size %d is smaller than 40", it.size));
680 ilt_rva = read32 (ptr);
681 if (!bounds_check_virtual_address (ctx, ilt_rva, 8))
682 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Lookup Table rva %x", ilt_rva));
684 name_rva = read32 (ptr + 12);
685 if (!bounds_check_virtual_address (ctx, name_rva, SIZE_OF_MSCOREE))
686 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name rva %x", name_rva));
688 iat_rva = read32 (ptr + 16);
689 if (!bounds_check_virtual_address (ctx, iat_rva, 8))
690 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Address Table rva %x", iat_rva));
692 if (iat_rva != ctx->data_directories [IAT_IDX].rva)
693 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));
695 name_rva = translate_rva (ctx, name_rva);
696 g_assert (name_rva != INVALID_OFFSET);
697 ptr = ctx->data + name_rva;
699 if (memcmp ("mscoree.dll", ptr, SIZE_OF_MSCOREE)) {
700 char name[SIZE_OF_MSCOREE];
701 memcpy (name, ptr, SIZE_OF_MSCOREE);
702 name [SIZE_OF_MSCOREE - 1] = 0;
703 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name: '%s'", name));
706 verify_hint_name_table (ctx, ilt_rva, "Import Lookup Table");
708 verify_hint_name_table (ctx, iat_rva, "Import Address Table");
712 verify_resources_table (VerifyContext *ctx)
714 DataDirectory it = ctx->data_directories [RESOURCE_TABLE_IDX];
716 guint16 named_entries, id_entries;
717 const char *ptr, *root, *end;
723 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));
725 offset = it.translated_offset;
726 root = ptr = ctx->data + offset;
727 end = root + it.size;
729 g_assert (offset != INVALID_OFFSET);
731 named_entries = read16 (ptr + 12);
732 id_entries = read16 (ptr + 14);
734 printf ("named %d id_entries %d\n", named_entries, id_entries);
735 if ((named_entries + id_entries) * 8 + 16 > it.size)
736 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));
738 if (named_entries || id_entries)
739 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support full verification of PECOFF resources"));
743 verify_cli_header (VerifyContext *ctx)
745 DataDirectory it = ctx->data_directories [CLI_HEADER_IDX];
751 ADD_ERROR (ctx, g_strdup_printf ("CLI header missing"));
754 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size in data directory %d must be 72", it.size));
756 offset = it.translated_offset;
757 ptr = ctx->data + offset;
759 g_assert (offset != INVALID_OFFSET);
761 if (read16 (ptr) != 72)
762 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size %d must be 72", read16 (ptr)));
764 if (!bounds_check_virtual_address (ctx, read32 (ptr + 8), read32 (ptr + 12)))
765 ADD_ERROR (ctx, g_strdup_printf ("Invalid medatata section rva/size pair %x/%x", read32 (ptr + 8), read32 (ptr + 12)));
767 if (!read32 (ptr + 8) || !read32 (ptr + 12))
768 ADD_ERROR (ctx, g_strdup_printf ("Missing medatata section in the CLI header"));
770 if ((read32 (ptr + 16) & ~0x0001000B) != 0)
771 ADD_ERROR (ctx, g_strdup_printf ("Invalid CLI header flags"));
774 for (i = 0; i < 6; ++i) {
775 guint32 rva = read32 (ptr);
776 guint32 size = read32 (ptr + 4);
778 if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
779 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli section %i rva/size pair %x/%x", i, rva, size));
784 ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't support cli header section %d", i));
789 pad4 (guint32 offset)
791 if (offset & 0x3) //pad to the next 4 byte boundary
792 offset = (offset & ~0x3) + 4;
797 verify_metadata_header (VerifyContext *ctx)
800 DataDirectory it = ctx->data_directories [CLI_HEADER_IDX];
804 offset = it.translated_offset;
805 ptr = ctx->data + offset;
806 g_assert (offset != INVALID_OFFSET);
808 //build a directory entry for the metadata root
810 it.rva = read32 (ptr);
812 it.size = read32 (ptr);
813 it.translated_offset = offset = translate_rva (ctx, it.rva);
815 ptr = ctx->data + offset;
816 g_assert (offset != INVALID_OFFSET);
819 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least 20 bytes required for initial decoding)", it.size));
821 if (read32 (ptr) != 0x424A5342)
822 ADD_ERROR (ctx, g_strdup_printf ("Invalid metadata signature, expected 0x424A5342 but got %08x", read32 (ptr)));
824 offset = pad4 (offset + 16 + read32 (ptr + 12));
826 if (!bounds_check_datadir (&it, offset, 4))
827 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));
829 ptr = ctx->data + offset; //move to streams header
831 if (read16 (ptr + 2) != 5)
832 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section have %d streams (it must have exactly 5)", read16 (ptr + 2)));
837 for (i = 0; i < 5; ++i) {
838 guint32 stream_off, stream_size;
839 int string_size, stream_idx;
841 if (!bounds_check_datadir (&it, offset, 8))
842 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));
844 stream_off = it.translated_offset + read32 (ptr);
845 stream_size = read32 (ptr + 4);
847 if (!bounds_check_datadir (&it, stream_off, stream_size))
848 ADD_ERROR (ctx, g_strdup_printf ("Invalid stream header %d offset/size pair %x/%x", 0, stream_off, stream_size));
853 for (string_size = 0; string_size < 32; ++string_size) {
854 if (!bounds_check_datadir (&it, offset++, 1))
855 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small to decode stream header %d name", i));
856 if (!ptr [string_size])
860 if (ptr [string_size])
861 ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d name larger than 32 bytes", i));
863 if (!strncmp ("#Strings", ptr, 9))
864 stream_idx = STRINGS_STREAM;
865 else if (!strncmp ("#US", ptr, 4))
866 stream_idx = USER_STRINGS_STREAM;
867 else if (!strncmp ("#Blob", ptr, 6))
868 stream_idx = BLOB_STREAM;
869 else if (!strncmp ("#GUID", ptr, 6))
870 stream_idx = GUID_STREAM;
871 else if (!strncmp ("#~", ptr, 3))
872 stream_idx = TILDE_STREAM;
874 ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr));
876 if (ctx->metadata_streams [stream_idx].offset != 0)
877 ADD_ERROR (ctx, g_strdup_printf ("Duplicated metadata stream header %s", ptr));
879 ctx->metadata_streams [stream_idx].offset = stream_off;
880 ctx->metadata_streams [stream_idx].size = stream_size;
882 offset = pad4 (offset);
883 ptr = ctx->data + offset;
888 verify_tables_schema (VerifyContext *ctx)
890 OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM];
891 unsigned offset = tables_area.offset;
892 const char *ptr = ctx->data + offset;
893 guint64 valid_tables;
897 if (tables_area.size < 24)
898 ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for initial decoding (requires 24 bytes)", tables_area.size));
901 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata major version %d, expected 2", ptr [4]));
903 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata minor version %d, expected 0", ptr [5]));
905 if ((ptr [6] & ~0x7) != 0)
906 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]));
908 ctx->wide_strings = ptr [6] & 0x1;
909 ctx->wide_guid = ptr [6] & 0x2;
910 ctx->wide_blob = ptr [6] & 04;
912 valid_tables = read64 (ptr + 8);
914 for (i = 0; i < 64; ++i) {
915 if (!(valid_tables & ((guint64)1 << i)))
918 /*MS Extensions: 0x3 0x5 0x7 0x13 0x16
919 Unused: 0x1E 0x1F 0x2D-0x3F
920 We don't care about the MS extensions.*/
921 if (i == 0x3 || i == 0x5 || i == 0x7 || i == 0x13 || i == 0x16)
922 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifies doesn't support MS specific table %x", i));
923 if (i == 0x1E || i == 0x1F || i >= 0x2D)
924 ADD_ERROR (ctx, g_strdup_printf ("Invalid table %x", i));
928 if (tables_area.size < 24 + count * 4)
929 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));
933 for (i = 0; i < 64; ++i) {
934 if (valid_tables & ((guint64)1 << i)) {
935 ctx->tables [i].row_count = read32 (ptr);
939 ctx->tables_offset = offset + 24 + count * 4;
943 enc_index_size (guint32 bits, guint32 max)
945 guint32 size = 1 << (16 - bits);
946 return max >= size ? 4 : 2;
950 calc_fields_size (VerifyContext *ctx)
952 #define TS(T) (ctx->tables [T].row_count)
953 #define MAX2(TA,TB) MAX (TS (TA), TS (TB))
954 #define MAX3(TA,TB,TC) MAX (TS (TA), MAX (TS (TB), TS (TC)))
955 #define TB_SIZE(T) (TS (T) >= (1 << 16) ? 4 : 2)
958 memset (ctx->field_sizes, 0, sizeof (guint32) * COL_LAST);
960 ctx->field_sizes [COL_UINT8] = 1;
961 ctx->field_sizes [COL_UINT16] = 2;
962 ctx->field_sizes [COL_UINT32] = 4;
964 ctx->field_sizes [COL_STRING] = ctx->wide_strings ? 4 : 2;
965 ctx->field_sizes [COL_GUID] = ctx->wide_guid ? 4 : 2;
966 ctx->field_sizes [COL_BLOB] = ctx->wide_blob? 4 : 2;
968 ctx->field_sizes [COL_TYPE_DEF_OR_REF] = enc_index_size (2, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_TYPEREF, MONO_TABLE_TYPESPEC));
969 ctx->field_sizes [COL_HAS_CONSTANT] = enc_index_size (2, MAX3 (MONO_TABLE_FIELD, MONO_TABLE_PARAM, MONO_TABLE_PROPERTY));
971 tmp = MAX3 (MONO_TABLE_METHOD, MONO_TABLE_FIELD, MONO_TABLE_TYPEREF);
972 tmp = MAX (tmp, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_PARAM, MONO_TABLE_INTERFACEIMPL));
973 tmp = MAX (tmp, MAX3 (MONO_TABLE_MEMBERREF, MONO_TABLE_MODULE, COL_HAS_DECL_SECURITY));
974 tmp = MAX (tmp, MAX3 (MONO_TABLE_PROPERTY, MONO_TABLE_EVENT, MONO_TABLE_STANDALONESIG));
975 tmp = MAX (tmp, MAX3 (MONO_TABLE_MODULEREF, MONO_TABLE_TYPESPEC, MONO_TABLE_ASSEMBLY));
976 tmp = MAX (tmp, MAX3 (MONO_TABLE_ASSEMBLYREF, MONO_TABLE_FILE, MONO_TABLE_EXPORTEDTYPE));
977 tmp = MAX (tmp, MONO_TABLE_MANIFESTRESOURCE);
978 ctx->field_sizes [COL_HAS_CATTR] = enc_index_size (5, tmp);
980 ctx->field_sizes [COL_HAS_FIELD_MARSHAL] = enc_index_size (1, MAX2 (MONO_TABLE_FIELD, MONO_TABLE_PARAM));
981 ctx->field_sizes [COL_HAS_DECL_SECURITY] = enc_index_size (2, MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_METHOD, MONO_TABLE_ASSEMBLY));
983 tmp = MAX3 (MONO_TABLE_TYPEDEF, MONO_TABLE_TYPEREF, MONO_TABLE_MODULEREF);
984 tmp = MAX (tmp, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_TYPESPEC));
985 ctx->field_sizes [COL_MEMBER_REF_PARENT] = enc_index_size (3, tmp);
987 ctx->field_sizes [COL_HAS_SEMANTICS] = enc_index_size (1, MAX2 (MONO_TABLE_EVENT, MONO_TABLE_PROPERTY));
988 ctx->field_sizes [COL_METHOD_DEF_OR_REF] = enc_index_size (1, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_MEMBERREF));
989 ctx->field_sizes [COL_MEMBER_FORWARDED] = enc_index_size (1, MAX2 (MONO_TABLE_FIELD, MONO_TABLE_METHOD));
990 ctx->field_sizes [COL_IMPLEMENTATION] = enc_index_size (2, MAX3 (MONO_TABLE_FILE, MONO_TABLE_ASSEMBLYREF, MONO_TABLE_EXPORTEDTYPE));
992 ctx->field_sizes [COL_CATTR_TYPE] = enc_index_size (3, MAX2 (MONO_TABLE_METHOD, MONO_TABLE_MEMBERREF));
993 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)));
994 ctx->field_sizes [COL_TYPE_OR_METHOD_DEF] = enc_index_size (1, MAX2 (MONO_TABLE_TYPEDEF, MONO_TABLE_METHOD));
996 ctx->field_sizes [COL_TYPE_DEF] = TB_SIZE (MONO_TABLE_TYPEDEF);
997 ctx->field_sizes [COL_METHOD_DEF] = TB_SIZE (MONO_TABLE_TYPEDEF);
998 ctx->field_sizes [COL_FIELD] = TB_SIZE (MONO_TABLE_TYPEDEF);
999 ctx->field_sizes [COL_PARAM] = TB_SIZE (MONO_TABLE_TYPEDEF);
1000 ctx->field_sizes [COL_PROPERTY] = TB_SIZE (MONO_TABLE_TYPEDEF);
1001 ctx->field_sizes [COL_EVENT] = TB_SIZE (MONO_TABLE_TYPEDEF);
1002 ctx->field_sizes [COL_GENERIC_PARAM] = TB_SIZE (MONO_TABLE_TYPEDEF);
1003 ctx->field_sizes [COL_ASSEMBLY_REF] = TB_SIZE (MONO_TABLE_TYPEDEF);
1004 ctx->field_sizes [COL_MODULE_REF] = TB_SIZE (MONO_TABLE_TYPEDEF);
1008 calc_row_size (VerifyContext *ctx)
1011 guint64 total_size = 0;
1013 for (idx = 0, i = 0; i < 0x2D; ++i) {
1016 while ((type = table_desc [idx++]) != COL_LAST)
1017 size += ctx->field_sizes [type];
1019 ctx->tables [i].row_size = size;
1020 total_size += (guint64)size * ctx->tables [i].row_count;
1023 if (total_size > G_MAXUINT32)
1026 return (guint32)total_size;
1031 verify_tables_data (VerifyContext *ctx)
1033 OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM];
1034 guint table_area_size;
1035 calc_fields_size (ctx);
1036 table_area_size = calc_row_size (ctx);
1038 if (table_area_size == 0)
1039 ADD_ERROR (ctx, g_strdup_printf ("table space is either empty or overflowed"));
1041 if (!bounds_check_offset (&tables_area, ctx->tables_offset, table_area_size))
1042 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)));
1046 mono_image_verify (const char *data, guint32 size)
1049 memset (&ctx, 0, sizeof (VerifyContext));
1054 verify_msdos_header (&ctx);
1056 verify_pe_header (&ctx);
1058 verify_pe_optional_header (&ctx);
1060 load_section_table (&ctx);
1062 load_data_directories (&ctx);
1064 verify_import_table (&ctx);
1066 /*No need to check the IAT directory entry, it's content is indirectly verified by verify_import_table*/
1067 verify_resources_table (&ctx);
1069 verify_cli_header (&ctx);
1071 verify_metadata_header (&ctx);
1073 verify_tables_schema (&ctx);
1075 verify_tables_data (&ctx);
1078 g_free (ctx.sections);