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/metadata/security-manager.h>
23 #include <mono/metadata/security-core-clr.h>
24 #include <mono/metadata/cil-coff.h>
25 #include <mono/utils/strenc.h>
31 TODO add fail fast mode
32 TODO add PE32+ support
33 TODO verify the entry point RVA and content.
34 TODO load_section_table and load_data_directories must take PE32+ into account
35 TODO add section relocation support
36 TODO verify the relocation table, since we really don't use, no need so far.
37 TODO do full PECOFF resources verification
38 TODO verify in the CLI header entry point and resources
39 TODO implement null token typeref validation
40 TODO verify table wide invariants for typedef (sorting and uniqueness)
41 TODO implement proper authenticode data directory validation
42 TODO verify properties that require multiple tables to be valid
43 FIXME use subtraction based bounds checking to avoid overflows
44 FIXME get rid of metadata_streams and other fields from VerifyContext
47 #ifdef MONO_VERIFIER_DEBUG
48 #define VERIFIER_DEBUG(code) do { code; } while (0)
50 #define VERIFIER_DEBUG(code)
53 #define INVALID_OFFSET ((guint32)-1)
54 #define INVALID_ADDRESS 0xffffffff
64 RESOURCE_TABLE_IDX = 2,
65 CERTIFICATE_TABLE_IDX = 4,
66 RELOCATION_TABLE_IDX = 5,
80 #define INVALID_TABLE (0xFF)
81 /*format: number of bits, number of tables, tables{n. tables} */
82 const static unsigned char coded_index_desc[] = {
83 #define TYPEDEF_OR_REF_DESC (0)
90 #define HAS_CONSTANT_DESC (TYPEDEF_OR_REF_DESC + 5)
97 #define HAS_CATTR_DESC (HAS_CONSTANT_DESC + 5)
105 MONO_TABLE_INTERFACEIMPL,
106 MONO_TABLE_MEMBERREF,
108 MONO_TABLE_DECLSECURITY,
111 MONO_TABLE_STANDALONESIG,
112 MONO_TABLE_MODULEREF,
115 MONO_TABLE_ASSEMBLYREF,
117 MONO_TABLE_EXPORTEDTYPE,
118 MONO_TABLE_MANIFESTRESOURCE,
120 #define HAS_FIELD_MARSHAL_DESC (HAS_CATTR_DESC + 21)
126 #define HAS_DECL_SECURITY_DESC (HAS_FIELD_MARSHAL_DESC + 4)
133 #define MEMBERREF_PARENT_DESC (HAS_DECL_SECURITY_DESC + 5)
142 #define HAS_SEMANTICS_DESC (MEMBERREF_PARENT_DESC + 7)
148 #define METHODDEF_OR_REF_DESC (HAS_SEMANTICS_DESC + 4)
152 MONO_TABLE_MEMBERREF,
154 #define MEMBER_FORWARDED_DESC (METHODDEF_OR_REF_DESC + 4)
160 #define IMPLEMENTATION_DESC (MEMBER_FORWARDED_DESC + 4)
164 MONO_TABLE_ASSEMBLYREF,
165 MONO_TABLE_EXPORTEDTYPE,
167 #define CATTR_TYPE_DESC (IMPLEMENTATION_DESC + 5)
173 MONO_TABLE_MEMBERREF,
176 #define RES_SCOPE_DESC (CATTR_TYPE_DESC + 7)
180 MONO_TABLE_MODULEREF,
181 MONO_TABLE_ASSEMBLYREF,
184 #define TYPE_OR_METHODDEF_DESC (RES_SCOPE_DESC + 6)
194 guint32 translated_offset;
206 guint32 rellocationsRVA;
207 guint16 numberOfRelocations;
223 gboolean report_error;
226 DataDirectory data_directories [16];
227 guint32 section_count;
228 SectionHeader *sections;
230 OffsetAndSize metadata_streams [5]; //offset from begin of the image
233 #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \
235 MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \
236 vinfo->info.status = __status; \
237 vinfo->info.message = ( __msg); \
238 vinfo->exception_type = (__exception); \
239 (__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo); \
243 #define ADD_ERROR(__ctx, __msg) \
245 if ((__ctx)->report_error) \
246 ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
247 (__ctx)->valid = 0; \
251 #define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0)
253 #define CHECK_ERROR() do { if (!ctx->valid) return; } while (0)
256 pe_signature_offset (VerifyContext *ctx)
258 return read32 (ctx->data + 0x3c);
262 pe_header_offset (VerifyContext *ctx)
264 return read32 (ctx->data + 0x3c) + 4;
268 bounds_check_virtual_address (VerifyContext *ctx, guint32 rva, guint32 size)
272 if (rva + size < rva) //overflow
275 if (ctx->stage > STAGE_PE) {
276 MonoCLIImageInfo *iinfo = ctx->image->image_info;
277 const int top = iinfo->cli_section_count;
278 MonoSectionTable *tables = iinfo->cli_section_tables;
281 for (i = 0; i < top; i++) {
282 guint32 base = tables->st_virtual_address;
283 guint32 end = base + tables->st_raw_data_size;
285 if (rva >= base && rva + size <= end)
288 /*if ((addr >= tables->st_virtual_address) &&
289 (addr < tables->st_virtual_address + tables->st_raw_data_size)){
291 return addr - tables->st_virtual_address + tables->st_raw_data_ptr;
301 for (i = 0; i < ctx->section_count; ++i) {
302 guint32 base = ctx->sections [i].baseRVA;
303 guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
304 if (rva >= base && rva + size <= end)
311 bounds_check_datadir (DataDirectory *dir, guint32 offset, guint32 size)
313 if (dir->translated_offset > offset)
315 if (dir->size < size)
317 return offset + size <= dir->translated_offset + dir->size;
321 bounds_check_offset (OffsetAndSize *off, guint32 offset, guint32 size)
323 if (off->offset > offset)
326 if (off->size < size)
329 return offset + size <= off->offset + off->size;
333 translate_rva (VerifyContext *ctx, guint32 rva)
337 if (ctx->stage > STAGE_PE)
338 return mono_cli_rva_image_map (ctx->image, rva);
343 for (i = 0; i < ctx->section_count; ++i) {
344 guint32 base = ctx->sections [i].baseRVA;
345 guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size;
346 if (rva >= base && rva <= end) {
347 guint32 res = (rva - base) + ctx->sections [i].baseOffset;
349 return res >= ctx->size ? INVALID_OFFSET : res;
353 return INVALID_OFFSET;
357 verify_msdos_header (VerifyContext *ctx)
361 ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header"));
362 if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a)
363 ADD_ERROR (ctx, g_strdup ("Invalid MS-DOS watermark"));
364 lfanew = pe_signature_offset (ctx);
365 if (lfanew > ctx->size - 4)
366 ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file"));
370 verify_pe_header (VerifyContext *ctx)
372 guint32 offset = pe_signature_offset (ctx);
373 const char *pe_header = ctx->data + offset;
374 if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0)
375 ADD_ERROR (ctx, g_strdup ("Invalid PE header watermark"));
379 if (offset > ctx->size - 20)
380 ADD_ERROR (ctx, g_strdup ("File with truncated pe header"));
381 if (read16 (pe_header) != 0x14c)
382 ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value"));
386 verify_pe_optional_header (VerifyContext *ctx)
388 guint32 offset = pe_header_offset (ctx);
389 guint32 header_size, file_alignment;
390 const char *pe_header = ctx->data + offset;
391 const char *pe_optional_header = pe_header + 20;
393 header_size = read16 (pe_header + 16);
396 if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/
397 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
399 if (offset > ctx->size - header_size || header_size > ctx->size)
400 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
402 if (read16 (pe_optional_header) == 0x10b) {
403 if (header_size != 224)
404 ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size));
406 /* LAMESPEC MS plays around this value and ignore it during validation
407 if (read32 (pe_optional_header + 28) != 0x400000)
408 ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));*/
409 if (read32 (pe_optional_header + 32) != 0x2000)
410 ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmnent %x", read32 (pe_optional_header + 32)));
411 file_alignment = read32 (pe_optional_header + 36);
412 if (file_alignment != 0x200 && file_alignment != 0x1000)
413 ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmnent %x", file_alignment));
414 /* All the junk in the middle is irrelevant, specially for mono. */
415 if (read32 (pe_optional_header + 92) > 0x10)
416 ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", read32 (pe_optional_header + 92)));
418 if (read16 (pe_optional_header) == 0x20B)
419 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+"));
421 ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header)));
426 load_section_table (VerifyContext *ctx)
429 SectionHeader *sections;
430 guint32 offset = pe_header_offset (ctx);
431 const char *ptr = ctx->data + offset;
432 guint16 num_sections = ctx->section_count = read16 (ptr + 2);
434 offset += 244;/*FIXME, this constant is different under PE32+*/
437 if (num_sections * 40 > ctx->size - offset)
438 ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size"));
440 sections = ctx->sections = g_new0 (SectionHeader, num_sections);
441 for (i = 0; i < num_sections; ++i) {
442 sections [i].size = read32 (ptr + 8);
443 sections [i].baseRVA = read32 (ptr + 12);
444 sections [i].baseOffset = read32 (ptr + 20);
445 sections [i].rellocationsRVA = read32 (ptr + 24);
446 sections [i].numberOfRelocations = read16 (ptr + 32);
450 ptr = ctx->data + offset; /*reset it to the beggining*/
451 for (i = 0; i < num_sections; ++i) {
452 guint32 raw_size, flags;
453 if (sections [i].baseOffset == 0)
454 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with intialized data only"));
455 if (sections [i].baseOffset >= ctx->size)
456 ADD_ERROR (ctx, g_strdup_printf ("Invalid PointerToRawData %x points beyond EOF", sections [i].baseOffset));
457 if (sections [i].size > ctx->size - sections [i].baseOffset)
458 ADD_ERROR (ctx, g_strdup ("Invalid VirtualSize points beyond EOF"));
460 raw_size = read32 (ptr + 16);
461 if (raw_size < sections [i].size)
462 ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with SizeOfRawData < VirtualSize"));
464 if (raw_size > ctx->size - sections [i].baseOffset)
465 ADD_ERROR (ctx, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size));
467 if (sections [i].rellocationsRVA || sections [i].numberOfRelocations)
468 ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't handle section relocation"));
470 flags = read32 (ptr + 36);
471 /*TODO 0xFE0000E0 is all flags from cil-coff.h OR'd. Make it a less magical number*/
472 if (flags == 0 || (flags & ~0xFE0000E0) != 0)
473 ADD_ERROR (ctx, g_strdup_printf ("Invalid section flags %x", flags));
480 is_valid_data_directory (int i)
482 /*LAMESPEC 4 == certificate 6 == debug, MS uses both*/
483 return i == 1 || i == 2 || i == 5 || i == 12 || i == 14 || i == 4 || i == 6;
487 load_data_directories (VerifyContext *ctx)
489 guint32 offset = pe_header_offset (ctx) + 116; /*FIXME, this constant is different under PE32+*/
490 const char *ptr = ctx->data + offset;
493 for (i = 0; i < 16; ++i) {
494 guint32 rva = read32 (ptr);
495 guint32 size = read32 (ptr + 4);
497 /*LAMESPEC the authenticode data directory format is different. We don't support CAS, so lets ignore for now.*/
498 if (i == CERTIFICATE_TABLE_IDX) {
502 if ((rva != 0 || size != 0) && !is_valid_data_directory (i))
503 ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d", i));
505 if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
506 ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d rva/size pair %x/%x", i, rva, size));
508 ctx->data_directories [i].rva = rva;
509 ctx->data_directories [i].size = size;
510 ctx->data_directories [i].translated_offset = translate_rva (ctx, rva);
516 #define SIZE_OF_MSCOREE (sizeof ("mscoree.dll"))
518 #define SIZE_OF_CORMAIN (sizeof ("_CorExeMain"))
521 verify_hint_name_table (VerifyContext *ctx, guint32 import_rva, const char *table_name)
524 guint32 hint_table_rva;
526 import_rva = translate_rva (ctx, import_rva);
527 g_assert (import_rva != INVALID_OFFSET);
529 hint_table_rva = read32 (ctx->data + import_rva);
530 if (!bounds_check_virtual_address (ctx, hint_table_rva, SIZE_OF_CORMAIN + 2))
531 ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint/Name rva %d for %s", hint_table_rva, table_name));
533 hint_table_rva = translate_rva (ctx, hint_table_rva);
534 g_assert (hint_table_rva != INVALID_OFFSET);
535 ptr = ctx->data + hint_table_rva + 2;
537 if (memcmp ("_CorExeMain", ptr, SIZE_OF_CORMAIN) && memcmp ("_CorDllMain", ptr, SIZE_OF_CORMAIN)) {
538 char name[SIZE_OF_CORMAIN];
539 memcpy (name, ptr, SIZE_OF_CORMAIN);
540 name [SIZE_OF_CORMAIN - 1] = 0;
541 ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint / Name: '%s'", name));
546 verify_import_table (VerifyContext *ctx)
548 DataDirectory it = ctx->data_directories [IMPORT_TABLE_IDX];
549 guint32 offset = it.translated_offset;
550 const char *ptr = ctx->data + offset;
551 guint32 name_rva, ilt_rva, iat_rva;
553 g_assert (offset != INVALID_OFFSET);
556 ADD_ERROR (ctx, g_strdup_printf ("Import table size %d is smaller than 40", it.size));
558 ilt_rva = read32 (ptr);
559 if (!bounds_check_virtual_address (ctx, ilt_rva, 8))
560 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Lookup Table rva %x", ilt_rva));
562 name_rva = read32 (ptr + 12);
563 if (!bounds_check_virtual_address (ctx, name_rva, SIZE_OF_MSCOREE))
564 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name rva %x", name_rva));
566 iat_rva = read32 (ptr + 16);
567 if (!bounds_check_virtual_address (ctx, iat_rva, 8))
568 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Address Table rva %x", iat_rva));
570 if (iat_rva != ctx->data_directories [IAT_IDX].rva)
571 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));
573 name_rva = translate_rva (ctx, name_rva);
574 g_assert (name_rva != INVALID_OFFSET);
575 ptr = ctx->data + name_rva;
577 if (memcmp ("mscoree.dll", ptr, SIZE_OF_MSCOREE)) {
578 char name[SIZE_OF_MSCOREE];
579 memcpy (name, ptr, SIZE_OF_MSCOREE);
580 name [SIZE_OF_MSCOREE - 1] = 0;
581 ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name: '%s'", name));
584 verify_hint_name_table (ctx, ilt_rva, "Import Lookup Table");
586 verify_hint_name_table (ctx, iat_rva, "Import Address Table");
590 verify_resources_table (VerifyContext *ctx)
592 DataDirectory it = ctx->data_directories [RESOURCE_TABLE_IDX];
594 guint16 named_entries, id_entries;
595 const char *ptr, *root, *end;
601 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));
603 offset = it.translated_offset;
604 root = ptr = ctx->data + offset;
605 end = root + it.size;
607 g_assert (offset != INVALID_OFFSET);
609 named_entries = read16 (ptr + 12);
610 id_entries = read16 (ptr + 14);
612 if ((named_entries + id_entries) * 8 + 16 > it.size)
613 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));
615 /* XXX at least one unmanaged resource is added due to a call to AssemblyBuilder::DefineVersionInfoResource ()
616 if (named_entries || id_entries)
617 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support full verification of PECOFF resources"));
621 /*----------nothing from here on can use data_directory---*/
624 get_data_dir (VerifyContext *ctx, int idx)
626 MonoCLIImageInfo *iinfo = ctx->image->image_info;
627 MonoPEDirEntry *entry= &iinfo->cli_header.datadir.pe_export_table;
631 res.rva = entry->rva;
632 res.size = entry->size;
633 res.translated_offset = translate_rva (ctx, res.rva);
638 verify_cli_header (VerifyContext *ctx)
640 DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX);
646 ADD_ERROR (ctx, g_strdup_printf ("CLI header missing"));
649 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size in data directory %d must be 72", it.size));
651 offset = it.translated_offset;
652 ptr = ctx->data + offset;
654 g_assert (offset != INVALID_OFFSET);
656 if (read16 (ptr) != 72)
657 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size %d must be 72", read16 (ptr)));
659 if (!bounds_check_virtual_address (ctx, read32 (ptr + 8), read32 (ptr + 12)))
660 ADD_ERROR (ctx, g_strdup_printf ("Invalid medatata section rva/size pair %x/%x", read32 (ptr + 8), read32 (ptr + 12)));
663 if (!read32 (ptr + 8) || !read32 (ptr + 12))
664 ADD_ERROR (ctx, g_strdup_printf ("Missing medatata section in the CLI header"));
666 if ((read32 (ptr + 16) & ~0x0001000B) != 0)
667 ADD_ERROR (ctx, g_strdup_printf ("Invalid CLI header flags"));
670 for (i = 0; i < 6; ++i) {
671 guint32 rva = read32 (ptr);
672 guint32 size = read32 (ptr + 4);
674 if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size))
675 ADD_ERROR (ctx, g_strdup_printf ("Invalid cli section %i rva/size pair %x/%x", i, rva, size));
680 ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't support cli header section %d", i));
685 pad4 (guint32 offset)
687 if (offset & 0x3) //pad to the next 4 byte boundary
688 offset = (offset & ~0x3) + 4;
693 verify_metadata_header (VerifyContext *ctx)
696 DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX);
700 offset = it.translated_offset;
701 ptr = ctx->data + offset;
702 g_assert (offset != INVALID_OFFSET);
704 //build a directory entry for the metadata root
706 it.rva = read32 (ptr);
708 it.size = read32 (ptr);
709 it.translated_offset = offset = translate_rva (ctx, it.rva);
711 ptr = ctx->data + offset;
712 g_assert (offset != INVALID_OFFSET);
715 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least 20 bytes required for initial decoding)", it.size));
717 if (read32 (ptr) != 0x424A5342)
718 ADD_ERROR (ctx, g_strdup_printf ("Invalid metadata signature, expected 0x424A5342 but got %08x", read32 (ptr)));
720 offset = pad4 (offset + 16 + read32 (ptr + 12));
722 if (!bounds_check_datadir (&it, offset, 4))
723 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));
725 ptr = ctx->data + offset; //move to streams header
727 if (read16 (ptr + 2) != 5)
728 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section have %d streams (it must have exactly 5)", read16 (ptr + 2)));
733 for (i = 0; i < 5; ++i) {
734 guint32 stream_off, stream_size;
735 int string_size, stream_idx;
737 if (!bounds_check_datadir (&it, offset, 8))
738 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));
740 stream_off = it.translated_offset + read32 (ptr);
741 stream_size = read32 (ptr + 4);
743 if (!bounds_check_datadir (&it, stream_off, stream_size))
744 ADD_ERROR (ctx, g_strdup_printf ("Invalid stream header %d offset/size pair %x/%x", 0, stream_off, stream_size));
749 for (string_size = 0; string_size < 32; ++string_size) {
750 if (!bounds_check_datadir (&it, offset++, 1))
751 ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small to decode stream header %d name", i));
752 if (!ptr [string_size])
756 if (ptr [string_size])
757 ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d name larger than 32 bytes", i));
759 if (!strncmp ("#Strings", ptr, 9))
760 stream_idx = STRINGS_STREAM;
761 else if (!strncmp ("#US", ptr, 4))
762 stream_idx = USER_STRINGS_STREAM;
763 else if (!strncmp ("#Blob", ptr, 6))
764 stream_idx = BLOB_STREAM;
765 else if (!strncmp ("#GUID", ptr, 6))
766 stream_idx = GUID_STREAM;
767 else if (!strncmp ("#~", ptr, 3))
768 stream_idx = TILDE_STREAM;
770 ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr));
772 if (ctx->metadata_streams [stream_idx].offset != 0)
773 ADD_ERROR (ctx, g_strdup_printf ("Duplicated metadata stream header %s", ptr));
775 ctx->metadata_streams [stream_idx].offset = stream_off;
776 ctx->metadata_streams [stream_idx].size = stream_size;
778 offset = pad4 (offset);
779 ptr = ctx->data + offset;
784 verify_tables_schema (VerifyContext *ctx)
786 OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM];
787 unsigned offset = tables_area.offset;
788 const char *ptr = ctx->data + offset;
789 guint64 valid_tables;
793 //printf ("tables_area size %d offset %x %s\n", tables_area.size, tables_area.offset, ctx->image->name);
794 if (tables_area.size < 24)
795 ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for initial decoding (requires 24 bytes)", tables_area.size));
797 //printf ("ptr %x %x\n", ptr[4], ptr[5]);
798 if (ptr [4] != 2 && ptr [4] != 1)
799 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata major version %d, expected 2", ptr [4]));
801 ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata minor version %d, expected 0", ptr [5]));
803 if ((ptr [6] & ~0x7) != 0)
804 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]));
806 valid_tables = read64 (ptr + 8);
808 for (i = 0; i < 64; ++i) {
809 if (!(valid_tables & ((guint64)1 << i)))
812 /*MS Extensions: 0x3 0x5 0x7 0x13 0x16
813 Unused: 0x1E 0x1F 0x2D-0x3F
814 We don't care about the MS extensions.*/
815 if (i == 0x3 || i == 0x5 || i == 0x7 || i == 0x13 || i == 0x16)
816 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifies doesn't support MS specific table %x", i));
817 if (i == 0x1E || i == 0x1F || i >= 0x2D)
818 ADD_ERROR (ctx, g_strdup_printf ("Invalid table %x", i));
822 if (tables_area.size < 24 + count * 4)
823 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));
826 for (i = 0; i < 64; ++i) {
827 if (valid_tables & ((guint64)1 << i)) {
828 guint32 row_count = read32 (ptr);
829 if (row_count > (1 << 25) - 1)
830 ADD_ERROR (ctx, g_strdup_printf ("Invalid Table %d row count, mono only supports ", i));
836 /*----------nothing from here on can use data_directory or metadata_streams ---*/
839 get_col_offset (VerifyContext *ctx, int table, int column)
841 guint32 bitfield = ctx->image->tables [table].size_bitfield;
845 offset += mono_metadata_table_size (bitfield, column);
851 get_col_size (VerifyContext *ctx, int table, int column)
853 return mono_metadata_table_size (ctx->image->tables [table].size_bitfield, column);
857 get_metadata_stream (VerifyContext *ctx, MonoStreamHeader *header)
860 res.offset = header->data - ctx->data;
861 res.size = header->size;
867 is_valid_string_full (VerifyContext *ctx, guint32 offset, gboolean allow_empty)
869 OffsetAndSize strings = get_metadata_stream (ctx, &ctx->image->heap_strings);
871 const char *data = ctx->data + strings.offset;
873 if (offset >= strings.size)
875 if (data + offset < data) //FIXME, use a generalized and smart unsigned add with overflow check and fix the whole thing
878 if (!mono_utf8_validate_and_len_with_bounds (data + offset, strings.size - offset, &length, NULL))
880 return allow_empty || length > 0;
884 is_valid_string (VerifyContext *ctx, guint32 offset)
886 return is_valid_string_full (ctx, offset, TRUE);
890 is_valid_non_empty_string (VerifyContext *ctx, guint32 offset)
892 return is_valid_string_full (ctx, offset, FALSE);
896 is_valid_guid (VerifyContext *ctx, guint32 offset)
898 OffsetAndSize guids = get_metadata_stream (ctx, &ctx->image->heap_guid);
899 return guids.size >= 8 && guids.size - 8 >= offset;
903 get_coded_index_token (int token_kind, guint32 coded_token)
905 guint32 bits = coded_index_desc [token_kind];
906 return coded_token >> bits;
910 make_coded_token (int kind, guint32 table, guint32 table_idx)
912 guint32 bits = coded_index_desc [kind++];
913 guint32 tables = coded_index_desc [kind++];
915 for (i = 0; i < tables; ++i) {
916 if (coded_index_desc [kind++] == table)
917 return ((table_idx + 1) << bits) | i;
919 g_assert_not_reached ();
924 is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token)
926 guint32 bits = coded_index_desc [token_kind++];
927 guint32 table_count = coded_index_desc [token_kind++];
928 guint32 table = coded_token & ((1 << bits) - 1);
929 guint32 token = coded_token >> bits;
931 if (table >= table_count)
934 /*token_kind points to the first table idx*/
935 table = coded_index_desc [token_kind + table];
937 if (table == INVALID_TABLE)
939 return token <= ctx->image->tables [table].rows;
949 token_locator (const void *a, const void *b)
951 RowLocator *loc = (RowLocator *)a;
952 unsigned const char *row = (unsigned const char *)b;
953 guint32 token = loc->col_size == 2 ? read16 (row + loc->col_offset) : read32 (row + loc->col_offset);
955 VERIFIER_DEBUG ( printf ("\tfound token %x\n", token) );
956 return (int)loc->token - (int)token;
960 search_sorted_table (VerifyContext *ctx, int table, int column, guint32 coded_token)
962 MonoTableInfo *tinfo = &ctx->image->tables [table];
964 const char *res, *base;
965 locator.token = coded_token;
966 locator.col_offset = get_col_offset (ctx, table, column);
967 locator.col_size = get_col_size (ctx, table, column);
970 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) );
971 res = bsearch (&locator, base, tinfo->rows, tinfo->row_size, token_locator);
975 return (res - base) / tinfo->rows;
978 /*WARNING: This function doesn't verify if the strings @offset points to a valid string*/
980 get_string_ptr (VerifyContext *ctx, guint offset)
982 return ctx->image->heap_strings.data + offset;
985 /*WARNING: This function doesn't verify if the strings @offset points to a valid string*/
987 string_cmp (VerifyContext *ctx, const char *str, guint offset)
990 return strcmp (str, "");
992 return strcmp (str, get_string_ptr (ctx, offset));
996 typedef_is_system_object (VerifyContext *ctx, guint32 *data)
998 return ctx->is_corlib && !string_cmp (ctx, "System", data [MONO_TYPEDEF_NAME]) && !string_cmp (ctx, "Object", data [MONO_TYPEDEF_NAMESPACE]);
1002 is_valid_field_signature (VerifyContext *ctx, guint32 offset)
1004 OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob);
1005 //TODO do proper verification
1006 return blob.size >= 2 && blob.size - 2 >= offset;
1010 is_valid_method_signature (VerifyContext *ctx, guint32 offset)
1012 OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob);
1013 //TODO do proper verification
1014 return blob.size >= 2 && blob.size - 2 >= offset;
1018 is_valid_method_header (VerifyContext *ctx, guint32 rva)
1020 //TODO do proper method header validation
1021 return mono_cli_rva_image_map (ctx->image, rva) != INVALID_ADDRESS;
1025 verify_module_table (VerifyContext *ctx)
1027 MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MODULE];
1028 guint32 data [MONO_MODULE_SIZE];
1030 if (table->rows != 1)
1031 ADD_ERROR (ctx, g_strdup_printf ("Module table must have exactly one row, but have %d", table->rows));
1033 mono_metadata_decode_row (table, 0, data, MONO_MODULE_SIZE);
1035 if (!is_valid_non_empty_string (ctx, data [MONO_MODULE_NAME]))
1036 ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid name, string index 0x%08x", data [MONO_MODULE_NAME]));
1038 if (!is_valid_guid (ctx, data [MONO_MODULE_MVID]))
1039 ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid Mvid, guid index %x", data [MONO_MODULE_MVID]));
1041 if (data [MONO_MODULE_ENC] != 0)
1042 ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero Enc field %x", data [MONO_MODULE_ENC]));
1044 if (data [MONO_MODULE_ENCBASE] != 0)
1045 ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero EncBase field %x", data [MONO_MODULE_ENCBASE]));
1049 verify_typeref_table (VerifyContext *ctx)
1051 MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF];
1052 guint32 data [MONO_TYPEREF_SIZE];
1055 for (i = 0; i < table->rows; ++i) {
1056 mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE);
1057 if (!is_valid_coded_index (ctx, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE]))
1058 ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d coded index 0x%08x", i, data [MONO_TYPEREF_SCOPE]));
1060 if (!get_coded_index_token (RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE]))
1061 ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", i));
1063 if (!data [MONO_TYPEREF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAME]))
1064 ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d name token 0x%08x", i, data [MONO_TYPEREF_NAME]));
1066 if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE]))
1067 ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d namespace token 0x%08x", i, data [MONO_TYPEREF_NAMESPACE]));
1071 /*bits 9,11,14,15,19,21,24-31 */
1072 #define INVALID_TYPEDEF_FLAG_BITS ((1 << 9) | (1 << 11) | (1 << 14) | (1 << 15) | (1 << 19) | (1 << 21) | 0xFF000000)
1074 verify_typedef_table (VerifyContext *ctx)
1076 MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF];
1077 guint32 data [MONO_TYPEDEF_SIZE];
1078 guint32 fieldlist = 1, methodlist = 1;
1081 if (table->rows == 0)
1082 ADD_ERROR (ctx, g_strdup_printf ("Typedef table must have exactly at least one row"));
1084 for (i = 0; i < table->rows; ++i) {
1085 mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE);
1086 if (data [MONO_TYPEDEF_FLAGS] & INVALID_TYPEDEF_FLAG_BITS)
1087 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid flags field 0x%08x", i, data [MONO_TYPEDEF_FLAGS]));
1089 if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_LAYOUT_MASK) == 0x18)
1090 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid class layout 0x18", i));
1092 if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == 0x30000)
1093 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i));
1095 if ((data [MONO_TYPEDEF_FLAGS] & 0xC00000) != 0)
1096 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i));
1098 if (!data [MONO_TYPEDEF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEDEF_NAME]))
1099 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid name token %08x", i, data [MONO_TYPEDEF_NAME]));
1101 if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE]))
1102 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid namespace token %08x", i, data [MONO_TYPEREF_NAMESPACE]));
1105 if (data [MONO_TYPEDEF_EXTENDS] != 0)
1106 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row 0 for the special <module> type must have a null extend field"));
1108 if (typedef_is_system_object (ctx, data) && data [MONO_TYPEDEF_EXTENDS] != 0)
1109 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for System.Object must have a null extend field", i));
1111 if (data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_INTERFACE) {
1112 if (data [MONO_TYPEDEF_EXTENDS])
1113 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must have a null extend field", i));
1114 if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_ABSTRACT) == 0)
1115 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must be abstract", i));
1117 if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]))
1118 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d extend field coded index 0x%08x", i, data [MONO_TYPEDEF_EXTENDS]));
1120 if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]))
1121 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for non-interface type must have a non-null extend field", i));
1125 if (data [MONO_TYPEDEF_FIELD_LIST] == 0)
1126 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList be be >= 1", i));
1128 if (data [MONO_TYPEDEF_FIELD_LIST] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1)
1129 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList rowid 0x%08x is out of range", i, data [MONO_TYPEDEF_FIELD_LIST]));
1131 if (data [MONO_TYPEDEF_FIELD_LIST] < fieldlist)
1132 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));
1134 if (data [MONO_TYPEDEF_METHOD_LIST] == 0)
1135 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList be be >= 1", i));
1137 if (data [MONO_TYPEDEF_METHOD_LIST] > ctx->image->tables [MONO_TABLE_METHOD].rows + 1)
1138 ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList rowid 0x%08x is out of range", i, data [MONO_TYPEDEF_METHOD_LIST]));
1140 if (data [MONO_TYPEDEF_METHOD_LIST] < methodlist)
1141 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));
1144 fieldlist = data [MONO_TYPEDEF_FIELD_LIST];
1145 methodlist = data [MONO_TYPEDEF_METHOD_LIST];
1150 #define INVALID_FIELD_FLAG_BITS ((1 << 3) | (1 << 11) | (1 << 14))
1152 verify_field_table (VerifyContext *ctx)
1154 MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELD];
1155 guint32 data [MONO_FIELD_SIZE], flags, module_field_list;
1158 module_field_list = (guint32)-1;
1159 if (ctx->image->tables [MONO_TABLE_TYPEDEF].rows > 1) {
1160 MonoTableInfo *type = &ctx->image->tables [MONO_TABLE_TYPEDEF];
1161 module_field_list = mono_metadata_decode_row_col (type, 1, MONO_TYPEDEF_FIELD_LIST);
1164 for (i = 0; i < table->rows; ++i) {
1165 mono_metadata_decode_row (table, i, data, MONO_FIELD_SIZE);
1166 flags = data [MONO_FIELD_FLAGS];
1168 if (flags & INVALID_FIELD_FLAG_BITS)
1169 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid flags field 0x%08x", i, flags));
1171 if ((flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == 0x7)
1172 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid field visibility 0x7", i));
1174 if ((flags & (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY)) == (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY))
1175 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d cannot be InitOnly and Literal at the same time", i));
1177 if ((flags & FIELD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & FIELD_ATTRIBUTE_SPECIAL_NAME))
1178 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is RTSpecialName but not SpecialName", i));
1180 if ((flags & FIELD_ATTRIBUTE_LITERAL) && !(flags & FIELD_ATTRIBUTE_STATIC))
1181 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but not Static", i));
1183 if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL) &&
1184 search_sorted_table (ctx, MONO_TABLE_FIELDMARSHAL, MONO_FIELD_MARSHAL_PARENT, make_coded_token (HAS_FIELD_MARSHAL_DESC, MONO_TABLE_FIELD, i)) == -1)
1185 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has FieldMarshal but there is no corresponding row in the FieldMarshal table", i));
1187 if ((flags & FIELD_ATTRIBUTE_HAS_DEFAULT) &&
1188 search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1)
1189 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i));
1191 if ((flags & FIELD_ATTRIBUTE_LITERAL) &&
1192 search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1)
1193 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but there is no corresponding row in the Constant table", i));
1195 if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_RVA) &&
1196 search_sorted_table (ctx, MONO_TABLE_FIELDRVA, MONO_FIELD_RVA_FIELD, i + 1) == -1)
1197 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i));
1199 if (!data [MONO_FIELD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_FIELD_NAME]))
1200 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid name token %08x", i, data [MONO_FIELD_NAME]));
1202 //TODO verify contant flag
1203 if (!data [MONO_FIELD_SIGNATURE] || !is_valid_field_signature (ctx, data [MONO_FIELD_SIGNATURE]))
1204 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid signature token %08x", i, data [MONO_FIELD_SIGNATURE]));
1206 if (i + 1 < module_field_list) {
1207 guint32 access = flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK;
1208 if (!(flags & FIELD_ATTRIBUTE_STATIC))
1209 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is a global variable but is not static", i));
1210 if (access != FIELD_ATTRIBUTE_COMPILER_CONTROLLED && access != FIELD_ATTRIBUTE_PRIVATE && access != FIELD_ATTRIBUTE_PUBLIC)
1211 ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is a global variable but have wrong visibility %x", i, access));
1216 /*bits 6,8,9,10,11,13,14,15*/
1217 #define INVALID_METHOD_IMPLFLAG_BITS ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15))
1219 verify_method_table (VerifyContext *ctx)
1221 MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD];
1222 guint32 data [MONO_METHOD_SIZE], flags, implflags, rva, module_method_list, access, code_type;
1223 guint32 paramlist = 1;
1224 gboolean is_ctor, is_cctor;
1228 module_method_list = (guint32)-1;
1229 if (ctx->image->tables [MONO_TABLE_TYPEDEF].rows > 1) {
1230 MonoTableInfo *type = &ctx->image->tables [MONO_TABLE_TYPEDEF];
1231 module_method_list = mono_metadata_decode_row_col (type, 1, MONO_TYPEDEF_METHOD_LIST);
1234 for (i = 0; i < table->rows; ++i) {
1235 mono_metadata_decode_row (table, i, data, MONO_METHOD_SIZE);
1236 rva = data [MONO_METHOD_RVA];
1237 implflags = data [MONO_METHOD_IMPLFLAGS];
1238 flags = data [MONO_METHOD_FLAGS];
1239 access = flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK;
1240 code_type = implflags & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK;
1243 if (implflags & INVALID_METHOD_IMPLFLAG_BITS)
1244 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid implflags field 0x%08x", i, implflags));
1247 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid MemberAccessMask 0x7", i));
1249 if (!data [MONO_METHOD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_METHOD_NAME]))
1250 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid name field 0x%08x", i, data [MONO_METHOD_NAME]));
1252 name = get_string_ptr (ctx, data [MONO_METHOD_NAME]);
1253 is_ctor = !strcmp (".ctor", name);
1254 is_cctor = !strcmp (".cctor", name);
1256 if ((is_ctor || is_cctor) &&
1257 search_sorted_table (ctx, MONO_TABLE_GENERICPARAM, MONO_GENERICPARAM_OWNER, make_coded_token (TYPE_OR_METHODDEF_DESC, MONO_TABLE_METHOD, i)) != -1)
1258 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d .ctor or .cctor has generic param", i));
1260 if ((flags & METHOD_ATTRIBUTE_STATIC) && (flags & (METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_NEW_SLOT)))
1261 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is static and (final, virtual or new slot)", i));
1263 if (flags & METHOD_ATTRIBUTE_ABSTRACT) {
1264 if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
1265 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and PinvokeImpl", i));
1266 if (!(flags & METHOD_ATTRIBUTE_VIRTUAL))
1267 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract but not Virtual", i));
1270 if (access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED && (flags & (METHOD_ATTRIBUTE_RT_SPECIAL_NAME | METHOD_ATTRIBUTE_SPECIAL_NAME)))
1271 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is CompileControlled and SpecialName or RtSpecialName", i));
1273 if ((flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & METHOD_ATTRIBUTE_SPECIAL_NAME))
1274 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is RTSpecialName but not SpecialName", i));
1276 //XXX no checks against cas stuff 10,11,12,13)
1278 //TODO check iface with .ctor (15,16)
1280 if (!data [MONO_METHOD_SIGNATURE] || !is_valid_method_signature (ctx, data [MONO_METHOD_SIGNATURE]))
1281 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid signature token %08x", i, data [MONO_METHOD_SIGNATURE]));
1283 if (i + 1 < module_method_list) {
1284 if (!(flags & METHOD_ATTRIBUTE_STATIC))
1285 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not Static", i));
1286 if (flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_VIRTUAL))
1287 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but is Abstract or Virtual", i));
1288 if (!(access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED || access == METHOD_ATTRIBUTE_PUBLIC || access == METHOD_ATTRIBUTE_PRIVATE))
1289 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not CompilerControled, Public or Private", i));
1292 //TODO check valuetype for synchronized
1294 if ((flags & (METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_NEW_SLOT | METHOD_ATTRIBUTE_STRICT)) && !(flags & METHOD_ATTRIBUTE_VIRTUAL))
1295 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is (Final, NewSlot or Strict) but not Virtual", i));
1297 if ((flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) && (flags & METHOD_ATTRIBUTE_VIRTUAL))
1298 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl and Virtual", i));
1300 if (!(flags & METHOD_ATTRIBUTE_ABSTRACT) && !rva && !(flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) &&
1301 !(implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && code_type != METHOD_IMPL_ATTRIBUTE_RUNTIME)
1302 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is not Abstract and neither PinvokeImpl, Runtime, InternalCall or with RVA != 0", i));
1304 if (access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED && !(rva || (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)))
1305 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is CompilerControlled but neither RVA != 0 or PinvokeImpl", i));
1307 //TODO check signature contents
1310 if (flags & METHOD_ATTRIBUTE_ABSTRACT)
1311 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is Abstract", i));
1312 if (code_type == METHOD_IMPL_ATTRIBUTE_OPTIL)
1313 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is CodeTypeMask is neither Native, CIL or Runtime", i));
1314 if (!is_valid_method_header (ctx, rva))
1315 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d RVA points to an invalid method header", i));
1317 if (!(flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_PINVOKE_IMPL)) && !(implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && code_type != METHOD_IMPL_ATTRIBUTE_RUNTIME)
1318 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA = 0 but neither Abstract, InternalCall, Runtime or PinvokeImpl", i));
1321 if ((flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
1323 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but has RVA != 0", i));
1324 if (search_sorted_table (ctx, MONO_TABLE_IMPLMAP, MONO_IMPLMAP_MEMBER, make_coded_token (MEMBER_FORWARDED_DESC, MONO_TABLE_METHOD, i)) == -1)
1325 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but has no row in the ImplMap table", i));
1327 if (flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME && !is_ctor && !is_cctor)
1328 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is RtSpecialName but not named .ctor or .cctor", i));
1330 if ((is_ctor || is_cctor) && !(flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME))
1331 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is named .ctor or .cctor but is not RtSpecialName", i));
1333 if (data [MONO_METHOD_PARAMLIST] == 0)
1334 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList be be >= 1", i));
1336 if (data [MONO_METHOD_PARAMLIST] < paramlist)
1337 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList rowid 0x%08x can't be smaller than of previous row 0x%08x", i, data [MONO_METHOD_PARAMLIST], paramlist));
1339 if (data [MONO_METHOD_PARAMLIST] > ctx->image->tables [MONO_TABLE_PARAM].rows + 1)
1340 ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList rowid 0x%08x is out of range", i, data [MONO_METHOD_PARAMLIST]));
1342 paramlist = data [MONO_METHOD_PARAMLIST];
1348 get_next_param_count (VerifyContext *ctx, guint32 *current_method)
1350 MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD];
1351 guint32 row = *current_method;
1352 guint32 paramlist, tmp;
1355 paramlist = mono_metadata_decode_row_col (table, row++, MONO_METHOD_PARAMLIST);
1356 while (row < table->rows) {
1357 tmp = mono_metadata_decode_row_col (table, row, MONO_METHOD_PARAMLIST);
1358 if (tmp > paramlist) {
1359 *current_method = row;
1360 return tmp - paramlist;
1365 /*no more methods, all params apply to the last one*/
1366 *current_method = table->rows;
1371 #define INVALID_PARAM_FLAGS_BITS ((1 << 2) | (1 << 3) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 14) | (1 << 15))
1373 verify_param_table (VerifyContext *ctx)
1375 MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PARAM];
1376 guint32 data [MONO_PARAM_SIZE], flags, sequence = 0, remaining_params, current_method = 0;
1377 gboolean first_param = TRUE;
1380 remaining_params = get_next_param_count (ctx, ¤t_method);
1382 for (i = 0; i < table->rows; ++i) {
1383 mono_metadata_decode_row (table, i, data, MONO_PARAM_SIZE);
1384 flags = data [MONO_PARAM_FLAGS];
1386 if (flags & INVALID_PARAM_FLAGS_BITS)
1387 ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d bad Flags value 0x%08x", i, flags));
1389 if (search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_PARAM, i)) == -1) {
1390 if (flags & PARAM_ATTRIBUTE_HAS_DEFAULT)
1391 ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasDefault = 1 but no owned row in Contant table", i));
1393 if (!(flags & PARAM_ATTRIBUTE_HAS_DEFAULT))
1394 ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasDefault = 0 but has owned row in Contant table", i));
1397 if ((flags & PARAM_ATTRIBUTE_HAS_FIELD_MARSHAL) && search_sorted_table (ctx, MONO_TABLE_FIELDMARSHAL, MONO_FIELD_MARSHAL_PARENT, make_coded_token (HAS_FIELD_MARSHAL_DESC, MONO_TABLE_PARAM, i)) == -1)
1398 ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasFieldMarshal = 1 but no owned row in FieldMarshal table", i));
1400 if (!is_valid_string (ctx, data [MONO_PARAM_NAME]))
1401 ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d Name = 1 bad token 0x%08x", i, data [MONO_PARAM_NAME]));
1403 if (!first_param && data [MONO_PARAM_SEQUENCE] <= sequence)
1404 ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d sequece = %d previus param has %d", i, data [MONO_PARAM_SEQUENCE], sequence));
1406 first_param = FALSE;
1407 sequence = data [MONO_PARAM_SEQUENCE];
1408 if (--remaining_params == 0) {
1409 remaining_params = get_next_param_count (ctx, ¤t_method);
1416 verify_interfaceimpl_table (VerifyContext *ctx)
1418 MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_INTERFACEIMPL];
1419 guint32 data [MONO_INTERFACEIMPL_SIZE];
1422 for (i = 0; i < table->rows; ++i) {
1423 mono_metadata_decode_row (table, i, data, MONO_INTERFACEIMPL_SIZE);
1424 if (data [MONO_INTERFACEIMPL_CLASS] && data [MONO_INTERFACEIMPL_CLASS] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows)
1425 ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Class field 0x%08x", i, data [MONO_TABLE_TYPEDEF]));
1427 if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_INTERFACEIMPL_INTERFACE]))
1428 ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Inteface field coded index 0x%08x", i, data [MONO_INTERFACEIMPL_INTERFACE]));
1430 if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_INTERFACEIMPL_INTERFACE]))
1431 ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Inteface field is null", i));
1436 verify_tables_data (VerifyContext *ctx)
1438 OffsetAndSize tables_area = get_metadata_stream (ctx, &ctx->image->heap_tables);
1439 guint32 size = 0, tables_offset;
1442 for (i = 0; i < 0x2D; ++i) {
1443 MonoTableInfo *table = &ctx->image->tables [i];
1445 tmp_size = size + (guint32)table->row_size * (guint32)table->rows;
1446 if (tmp_size < size) {
1454 ADD_ERROR (ctx, g_strdup_printf ("table space is either empty or overflowed"));
1456 tables_offset = ctx->image->tables_base - ctx->data;
1457 if (!bounds_check_offset (&tables_area, tables_offset, size))
1458 ADD_ERROR (ctx, g_strdup_printf ("Tables data require %d bytes but the only %d are available in the #~ stream", size, tables_area.size - (tables_offset - tables_area.offset)));
1460 verify_module_table (ctx);
1462 verify_typeref_table (ctx);
1464 verify_typedef_table (ctx);
1466 verify_field_table (ctx);
1468 verify_method_table (ctx);
1470 verify_param_table (ctx);
1472 verify_interfaceimpl_table (ctx);
1476 mono_verifier_is_corlib (MonoImage *image)
1478 gboolean trusted_location = (mono_security_get_mode () != MONO_SECURITY_MODE_CORE_CLR) ?
1479 TRUE : mono_security_core_clr_is_platform_image (image);
1481 return trusted_location && !strcmp ("mscorlib.dll", image->name);
1485 init_verify_context (VerifyContext *ctx, MonoImage *image, GSList **error_list)
1487 memset (ctx, 0, sizeof (VerifyContext));
1489 ctx->report_error = error_list != NULL;
1491 ctx->size = image->raw_data_len;
1492 ctx->data = image->raw_data;
1493 ctx->is_corlib = mono_verifier_is_corlib (image);
1497 cleanup_context (VerifyContext *ctx, GSList **error_list)
1499 g_free (ctx->sections);
1501 *error_list = ctx->errors;
1503 mono_free_verify_list (ctx->errors);
1508 mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list)
1512 if (!mono_verifier_is_enabled_for_image (image))
1515 init_verify_context (&ctx, image, error_list);
1516 ctx.stage = STAGE_PE;
1518 verify_msdos_header (&ctx);
1520 verify_pe_header (&ctx);
1522 verify_pe_optional_header (&ctx);
1524 load_section_table (&ctx);
1526 load_data_directories (&ctx);
1528 verify_import_table (&ctx);
1530 /*No need to check the IAT directory entry, it's content is indirectly verified by verify_import_table*/
1531 verify_resources_table (&ctx);
1534 return cleanup_context (&ctx, error_list);
1538 mono_verifier_verify_cli_data (MonoImage *image, GSList **error_list)
1542 if (!mono_verifier_is_enabled_for_image (image))
1545 init_verify_context (&ctx, image, error_list);
1546 ctx.stage = STAGE_CLI;
1548 verify_cli_header (&ctx);
1550 verify_metadata_header (&ctx);
1552 verify_tables_schema (&ctx);
1555 return cleanup_context (&ctx, error_list);
1559 mono_verifier_verify_table_data (MonoImage *image, GSList **error_list)
1563 if (!mono_verifier_is_enabled_for_image (image))
1566 init_verify_context (&ctx, image, error_list);
1567 ctx.stage = STAGE_TABLES;
1569 verify_tables_data (&ctx);
1571 return cleanup_context (&ctx, error_list);