/** * \file * Metadata verfication support * * Author: * Mono Project (http://www.mono-project.com) * * Copyright (C) 2005-2008 Novell, Inc. (http://www.novell.com) * Licensed under the MIT license. See LICENSE file in the project root for full license information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #ifndef DISABLE_VERIFIER /* TODO add fail fast mode TODO add PE32+ support TODO verify the entry point RVA and content. TODO load_section_table and load_data_directories must take PE32+ into account TODO add section relocation support TODO verify the relocation table, since we really don't use, no need so far. TODO do full PECOFF resources verification TODO verify in the CLI header entry point and resources TODO implement null token typeref validation TODO verify table wide invariants for typedef (sorting and uniqueness) TODO implement proper authenticode data directory validation TODO verify properties that require multiple tables to be valid FIXME use subtraction based bounds checking to avoid overflows FIXME get rid of metadata_streams and other fields from VerifyContext */ #ifdef MONO_VERIFIER_DEBUG #define VERIFIER_DEBUG(code) do { code; } while (0) #else #define VERIFIER_DEBUG(code) #endif #define INVALID_OFFSET ((guint32)-1) #define INVALID_ADDRESS 0xffffffff enum { STAGE_PE, STAGE_CLI, STAGE_TABLES }; enum { IMPORT_TABLE_IDX = 1, RESOURCE_TABLE_IDX = 2, CERTIFICATE_TABLE_IDX = 4, RELOCATION_TABLE_IDX = 5, IAT_IDX = 12, CLI_HEADER_IDX = 14, }; enum { STRINGS_STREAM, USER_STRINGS_STREAM, BLOB_STREAM, GUID_STREAM, TILDE_STREAM }; #define INVALID_TABLE (0xFF) /*format: number of bits, number of tables, tables{n. tables} */ const static unsigned char coded_index_desc[] = { #define TYPEDEF_OR_REF_DESC (0) 2, /*bits*/ 3, /*tables*/ MONO_TABLE_TYPEDEF, MONO_TABLE_TYPEREF, MONO_TABLE_TYPESPEC, #define HAS_CONSTANT_DESC (TYPEDEF_OR_REF_DESC + 5) 2, /*bits*/ 3, /*tables*/ MONO_TABLE_FIELD, MONO_TABLE_PARAM, MONO_TABLE_PROPERTY, #define HAS_CATTR_DESC (HAS_CONSTANT_DESC + 5) 5, /*bits*/ 20, /*tables*/ MONO_TABLE_METHOD, MONO_TABLE_FIELD, MONO_TABLE_TYPEREF, MONO_TABLE_TYPEDEF, MONO_TABLE_PARAM, MONO_TABLE_INTERFACEIMPL, MONO_TABLE_MEMBERREF, MONO_TABLE_MODULE, MONO_TABLE_DECLSECURITY, MONO_TABLE_PROPERTY, MONO_TABLE_EVENT, MONO_TABLE_STANDALONESIG, MONO_TABLE_MODULEREF, MONO_TABLE_TYPESPEC, MONO_TABLE_ASSEMBLY, MONO_TABLE_ASSEMBLYREF, MONO_TABLE_FILE, MONO_TABLE_EXPORTEDTYPE, MONO_TABLE_MANIFESTRESOURCE, MONO_TABLE_GENERICPARAM, #define HAS_FIELD_MARSHAL_DESC (HAS_CATTR_DESC + 22) 1, /*bits*/ 2, /*tables*/ MONO_TABLE_FIELD, MONO_TABLE_PARAM, #define HAS_DECL_SECURITY_DESC (HAS_FIELD_MARSHAL_DESC + 4) 2, /*bits*/ 3, /*tables*/ MONO_TABLE_TYPEDEF, MONO_TABLE_METHOD, MONO_TABLE_ASSEMBLY, #define MEMBERREF_PARENT_DESC (HAS_DECL_SECURITY_DESC + 5) 3, /*bits*/ 5, /*tables*/ MONO_TABLE_TYPEDEF, MONO_TABLE_TYPEREF, MONO_TABLE_MODULEREF, MONO_TABLE_METHOD, MONO_TABLE_TYPESPEC, #define HAS_SEMANTICS_DESC (MEMBERREF_PARENT_DESC + 7) 1, /*bits*/ 2, /*tables*/ MONO_TABLE_EVENT, MONO_TABLE_PROPERTY, #define METHODDEF_OR_REF_DESC (HAS_SEMANTICS_DESC + 4) 1, /*bits*/ 2, /*tables*/ MONO_TABLE_METHOD, MONO_TABLE_MEMBERREF, #define MEMBER_FORWARDED_DESC (METHODDEF_OR_REF_DESC + 4) 1, /*bits*/ 2, /*tables*/ MONO_TABLE_FIELD, MONO_TABLE_METHOD, #define IMPLEMENTATION_DESC (MEMBER_FORWARDED_DESC + 4) 2, /*bits*/ 3, /*tables*/ MONO_TABLE_FILE, MONO_TABLE_ASSEMBLYREF, MONO_TABLE_EXPORTEDTYPE, #define CATTR_TYPE_DESC (IMPLEMENTATION_DESC + 5) 3, /*bits*/ 5, /*tables*/ INVALID_TABLE, INVALID_TABLE, MONO_TABLE_METHOD, MONO_TABLE_MEMBERREF, INVALID_TABLE, #define RES_SCOPE_DESC (CATTR_TYPE_DESC + 7) 2, /*bits*/ 4, /*tables*/ MONO_TABLE_MODULE, MONO_TABLE_MODULEREF, MONO_TABLE_ASSEMBLYREF, MONO_TABLE_TYPEREF, #define TYPE_OR_METHODDEF_DESC (RES_SCOPE_DESC + 6) 1, /*bits*/ 2, /*tables*/ MONO_TABLE_TYPEDEF, MONO_TABLE_METHOD }; typedef struct { guint32 rva; guint32 size; guint32 translated_offset; } DataDirectory; typedef struct { guint32 offset; guint32 size; } OffsetAndSize; typedef struct { guint32 baseRVA; guint32 baseOffset; guint32 size; guint32 rellocationsRVA; guint16 numberOfRelocations; } SectionHeader; typedef struct { guint32 row_count; guint32 row_size; guint32 offset; } TableInfo; typedef struct { const char *data; guint32 size, token; GSList *errors; int valid; MonoImage *image; gboolean report_error; gboolean report_warning; int stage; DataDirectory data_directories [16]; guint32 section_count; SectionHeader *sections; OffsetAndSize metadata_streams [5]; //offset from begin of the image } VerifyContext; #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \ do { \ MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \ vinfo->info.status = __status; \ vinfo->info.message = ( __msg); \ vinfo->exception_type = (__exception); \ (__ctx)->errors = g_slist_prepend ((__ctx)->errors, vinfo); \ } while (0) #define ADD_WARNING(__ctx, __msg) \ do { \ if ((__ctx)->report_warning) { \ ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_WARNING, MONO_EXCEPTION_INVALID_PROGRAM); \ (__ctx)->valid = 0; \ return; \ } \ } while (0) #define ADD_ERROR_NO_RETURN(__ctx, __msg) \ do { \ if ((__ctx)->report_error) \ ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \ (__ctx)->valid = 0; \ } while (0) #define ADD_ERROR(__ctx, __msg) \ do { \ if ((__ctx)->report_error) \ ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \ (__ctx)->valid = 0; \ return; \ } while (0) #define FAIL(__ctx, __msg) \ do { \ if ((__ctx)->report_error) \ ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \ (__ctx)->valid = 0; \ return FALSE; \ } while (0) #define CHECK_STATE() do { if (!ctx.valid) goto cleanup; } while (0) #define CHECK_ERROR() do { if (!ctx->valid) return; } while (0) #define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a)) #define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a)) #if SIZEOF_VOID_P == 4 #define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b) #else #define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b) #endif #define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b)) #define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b)) static const char * dword_align (const char *ptr) { #if SIZEOF_VOID_P == 8 return (const char *) (((guint64) (ptr + 3)) & ~3); #else return (const char *) (((guint32) (ptr + 3)) & ~3); #endif } static void add_from_mono_error (VerifyContext *ctx, MonoError *error) { if (mono_error_ok (error)) return; ADD_ERROR (ctx, g_strdup (mono_error_get_message (error))); mono_error_cleanup (error); } static guint32 pe_signature_offset (VerifyContext *ctx) { return read32 (ctx->data + 0x3c); } static guint32 pe_header_offset (VerifyContext *ctx) { return read32 (ctx->data + 0x3c) + 4; } static gboolean bounds_check_virtual_address (VerifyContext *ctx, guint32 rva, guint32 size) { int i; if (rva + size < rva) //overflow return FALSE; if (ctx->stage > STAGE_PE) { MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)ctx->image->image_info; const int top = iinfo->cli_section_count; MonoSectionTable *tables = iinfo->cli_section_tables; int i; for (i = 0; i < top; i++) { guint32 base = tables->st_virtual_address; guint32 end = base + tables->st_raw_data_size; if (rva >= base && rva + size <= end) return TRUE; /*if ((addr >= tables->st_virtual_address) && (addr < tables->st_virtual_address + tables->st_raw_data_size)){ return addr - tables->st_virtual_address + tables->st_raw_data_ptr; }*/ tables++; } return FALSE; } if (!ctx->sections) return FALSE; for (i = 0; i < ctx->section_count; ++i) { guint32 base = ctx->sections [i].baseRVA; guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size; if (rva >= base && rva + size <= end) return TRUE; } return FALSE; } static gboolean bounds_check_datadir (DataDirectory *dir, guint32 offset, guint32 size) { if (dir->translated_offset > offset) return FALSE; if (dir->size < size) return FALSE; return offset + size <= dir->translated_offset + dir->size; } static gboolean bounds_check_offset (OffsetAndSize *off, guint32 offset, guint32 size) { if (off->offset > offset) return FALSE; if (off->size < size) return FALSE; return offset + size <= off->offset + off->size; } static guint32 translate_rva (VerifyContext *ctx, guint32 rva) { int i; if (ctx->stage > STAGE_PE) return mono_cli_rva_image_map (ctx->image, rva); if (!ctx->sections) return FALSE; for (i = 0; i < ctx->section_count; ++i) { guint32 base = ctx->sections [i].baseRVA; guint32 end = ctx->sections [i].baseRVA + ctx->sections [i].size; if (rva >= base && rva <= end) { guint32 res = (rva - base) + ctx->sections [i].baseOffset; /* double check */ return res >= ctx->size ? INVALID_OFFSET : res; } } return INVALID_OFFSET; } static void verify_msdos_header (VerifyContext *ctx) { guint32 lfanew; if (ctx->size < 128) ADD_ERROR (ctx, g_strdup ("Not enough space for the MS-DOS header")); if (ctx->data [0] != 0x4d || ctx->data [1] != 0x5a) ADD_ERROR (ctx, g_strdup ("Invalid MS-DOS watermark")); lfanew = pe_signature_offset (ctx); if (lfanew > ctx->size - 4) ADD_ERROR (ctx, g_strdup ("MS-DOS lfanew offset points to outside of the file")); } static void verify_pe_header (VerifyContext *ctx) { guint32 offset = pe_signature_offset (ctx); const char *pe_header = ctx->data + offset; if (pe_header [0] != 'P' || pe_header [1] != 'E' ||pe_header [2] != 0 ||pe_header [3] != 0) ADD_ERROR (ctx, g_strdup ("Invalid PE header watermark")); pe_header += 4; offset += 4; if (offset > ctx->size - 20) ADD_ERROR (ctx, g_strdup ("File with truncated pe header")); if (read16 (pe_header) != 0x14c) ADD_ERROR (ctx, g_strdup ("Invalid PE header Machine value")); } static void verify_pe_optional_header (VerifyContext *ctx) { guint32 offset = pe_header_offset (ctx); guint32 header_size, file_alignment; const char *pe_header = ctx->data + offset; const char *pe_optional_header = pe_header + 20; header_size = read16 (pe_header + 16); offset += 20; if (header_size < 2) /*must be at least 2 or we won't be able to read magic*/ ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size")); if (offset > ctx->size - header_size || header_size > ctx->size) ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size")); if (read16 (pe_optional_header) == 0x10b) { if (header_size != 224) ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header size %d", header_size)); /* LAMESPEC MS plays around this value and ignore it during validation if (read32 (pe_optional_header + 28) != 0x400000) ADD_ERROR (ctx, g_strdup_printf ("Invalid Image base %x", read32 (pe_optional_header + 28)));*/ if (read32 (pe_optional_header + 32) != 0x2000) ADD_ERROR (ctx, g_strdup_printf ("Invalid Section Aligmnent %x", read32 (pe_optional_header + 32))); file_alignment = read32 (pe_optional_header + 36); if (file_alignment != 0x200 && file_alignment != 0x1000) ADD_ERROR (ctx, g_strdup_printf ("Invalid file Aligmnent %x", file_alignment)); /* All the junk in the middle is irrelevant, specially for mono. */ if (read32 (pe_optional_header + 92) > 0x10) ADD_ERROR (ctx, g_strdup_printf ("Too many data directories %x", read32 (pe_optional_header + 92))); } else { if (read16 (pe_optional_header) == 0x20B) ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle PE32+")); else ADD_ERROR (ctx, g_strdup_printf ("Invalid optional header magic %d", read16 (pe_optional_header))); } } static void load_section_table (VerifyContext *ctx) { int i; SectionHeader *sections; guint32 offset = pe_header_offset (ctx); const char *ptr = ctx->data + offset; guint16 num_sections = ctx->section_count = read16 (ptr + 2); offset += 244;/*FIXME, this constant is different under PE32+*/ ptr += 244; if (num_sections * 40 > ctx->size - offset) ADD_ERROR (ctx, g_strdup ("Invalid PE optional header size")); sections = ctx->sections = g_new0 (SectionHeader, num_sections); for (i = 0; i < num_sections; ++i) { sections [i].size = read32 (ptr + 8); sections [i].baseRVA = read32 (ptr + 12); sections [i].baseOffset = read32 (ptr + 20); sections [i].rellocationsRVA = read32 (ptr + 24); sections [i].numberOfRelocations = read16 (ptr + 32); ptr += 40; } ptr = ctx->data + offset; /*reset it to the beggining*/ for (i = 0; i < num_sections; ++i) { guint32 raw_size, flags; if (sections [i].baseOffset == 0) ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with intialized data only")); if (sections [i].baseOffset >= ctx->size) ADD_ERROR (ctx, g_strdup_printf ("Invalid PointerToRawData %x points beyond EOF", sections [i].baseOffset)); if (sections [i].size > ctx->size - sections [i].baseOffset) ADD_ERROR (ctx, g_strdup ("Invalid VirtualSize points beyond EOF")); raw_size = read32 (ptr + 16); if (raw_size < sections [i].size) ADD_ERROR (ctx, g_strdup ("Metadata verifier doesn't handle sections with SizeOfRawData < VirtualSize")); if (raw_size > ctx->size - sections [i].baseOffset) ADD_ERROR (ctx, g_strdup_printf ("Invalid SizeOfRawData %x points beyond EOF", raw_size)); if (sections [i].rellocationsRVA || sections [i].numberOfRelocations) ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't handle section relocation")); flags = read32 (ptr + 36); /*TODO 0xFE0000E0 is all flags from cil-coff.h OR'd. Make it a less magical number*/ if (flags == 0 || (flags & ~0xFE0000E0) != 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid section flags %x", flags)); ptr += 40; } } static gboolean is_valid_data_directory (int i) { /*LAMESPEC 4 == certificate 6 == debug, MS uses both*/ return i == 1 || i == 2 || i == 5 || i == 12 || i == 14 || i == 4 || i == 6; } static void load_data_directories (VerifyContext *ctx) { guint32 offset = pe_header_offset (ctx) + 116; /*FIXME, this constant is different under PE32+*/ const char *ptr = ctx->data + offset; int i; for (i = 0; i < 16; ++i) { guint32 rva = read32 (ptr); guint32 size = read32 (ptr + 4); /*LAMESPEC the authenticode data directory format is different. We don't support CAS, so lets ignore for now.*/ if (i == CERTIFICATE_TABLE_IDX) { ptr += 8; continue; } if ((rva != 0 || size != 0) && !is_valid_data_directory (i)) ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d", i)); if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size)) ADD_ERROR (ctx, g_strdup_printf ("Invalid data directory %d rva/size pair %x/%x", i, rva, size)); ctx->data_directories [i].rva = rva; ctx->data_directories [i].size = size; ctx->data_directories [i].translated_offset = translate_rva (ctx, rva); ptr += 8; } } #define SIZE_OF_MSCOREE (sizeof ("mscoree.dll")) #define SIZE_OF_CORMAIN (sizeof ("_CorExeMain")) static void verify_hint_name_table (VerifyContext *ctx, guint32 import_rva, const char *table_name) { const char *ptr; guint32 hint_table_rva; import_rva = translate_rva (ctx, import_rva); g_assert (import_rva != INVALID_OFFSET); hint_table_rva = read32 (ctx->data + import_rva); if (!bounds_check_virtual_address (ctx, hint_table_rva, SIZE_OF_CORMAIN + 2)) ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint/Name rva %d for %s", hint_table_rva, table_name)); hint_table_rva = translate_rva (ctx, hint_table_rva); g_assert (hint_table_rva != INVALID_OFFSET); ptr = ctx->data + hint_table_rva + 2; if (memcmp ("_CorExeMain", ptr, SIZE_OF_CORMAIN) && memcmp ("_CorDllMain", ptr, SIZE_OF_CORMAIN)) { char name[SIZE_OF_CORMAIN]; memcpy (name, ptr, SIZE_OF_CORMAIN); name [SIZE_OF_CORMAIN - 1] = 0; ADD_ERROR (ctx, g_strdup_printf ("Invalid Hint / Name: '%s'", name)); } } static void verify_import_table (VerifyContext *ctx) { DataDirectory it = ctx->data_directories [IMPORT_TABLE_IDX]; guint32 offset = it.translated_offset; const char *ptr = ctx->data + offset; guint32 name_rva, ilt_rva, iat_rva; g_assert (offset != INVALID_OFFSET); if (it.size < 40) ADD_ERROR (ctx, g_strdup_printf ("Import table size %d is smaller than 40", it.size)); ilt_rva = read32 (ptr); if (ilt_rva && !bounds_check_virtual_address (ctx, ilt_rva, 8)) ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Lookup Table rva %x", ilt_rva)); name_rva = read32 (ptr + 12); if (name_rva && !bounds_check_virtual_address (ctx, name_rva, SIZE_OF_MSCOREE)) ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name rva %x", name_rva)); iat_rva = read32 (ptr + 16); if (iat_rva) { if (!bounds_check_virtual_address (ctx, iat_rva, 8)) ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Address Table rva %x", iat_rva)); if (iat_rva != ctx->data_directories [IAT_IDX].rva) 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)); } if (name_rva) { name_rva = translate_rva (ctx, name_rva); g_assert (name_rva != INVALID_OFFSET); ptr = ctx->data + name_rva; if (memcmp ("mscoree.dll", ptr, SIZE_OF_MSCOREE)) { char name[SIZE_OF_MSCOREE]; memcpy (name, ptr, SIZE_OF_MSCOREE); name [SIZE_OF_MSCOREE - 1] = 0; ADD_ERROR (ctx, g_strdup_printf ("Invalid Import Table Name: '%s'", name)); } } if (ilt_rva) { verify_hint_name_table (ctx, ilt_rva, "Import Lookup Table"); CHECK_ERROR (); } if (iat_rva) verify_hint_name_table (ctx, iat_rva, "Import Address Table"); } static void verify_resources_table (VerifyContext *ctx) { DataDirectory it = ctx->data_directories [RESOURCE_TABLE_IDX]; guint32 offset; guint16 named_entries, id_entries; const char *ptr; if (it.rva == 0) return; if (it.size < 16) 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)); offset = it.translated_offset; ptr = ctx->data + offset; g_assert (offset != INVALID_OFFSET); named_entries = read16 (ptr + 12); id_entries = read16 (ptr + 14); if ((named_entries + id_entries) * 8 + 16 > it.size) 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)); /* XXX at least one unmanaged resource is added due to a call to AssemblyBuilder::DefineVersionInfoResource () if (named_entries || id_entries) ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support full verification of PECOFF resources")); */ } /*----------nothing from here on can use data_directory---*/ static DataDirectory get_data_dir (VerifyContext *ctx, int idx) { MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)ctx->image->image_info; MonoPEDirEntry *entry= &iinfo->cli_header.datadir.pe_export_table; DataDirectory res; entry += idx; res.rva = entry->rva; res.size = entry->size; res.translated_offset = translate_rva (ctx, res.rva); return res; } static void verify_cli_header (VerifyContext *ctx) { DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX); guint32 offset; const char *ptr; int i; if (it.rva == 0) ADD_ERROR (ctx, g_strdup_printf ("CLI header missing")); if (it.size != 72) ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size in data directory %d must be 72", it.size)); offset = it.translated_offset; ptr = ctx->data + offset; g_assert (offset != INVALID_OFFSET); if (read16 (ptr) != 72) ADD_ERROR (ctx, g_strdup_printf ("Invalid cli header size %d must be 72", read16 (ptr))); if (!bounds_check_virtual_address (ctx, read32 (ptr + 8), read32 (ptr + 12))) ADD_ERROR (ctx, g_strdup_printf ("Invalid medatata section rva/size pair %x/%x", read32 (ptr + 8), read32 (ptr + 12))); if (!read32 (ptr + 8) || !read32 (ptr + 12)) ADD_ERROR (ctx, g_strdup_printf ("Missing medatata section in the CLI header")); if ((read32 (ptr + 16) & ~0x0003000B) != 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid CLI header flags")); ptr += 24; for (i = 0; i < 6; ++i) { guint32 rva = read32 (ptr); guint32 size = read32 (ptr + 4); if (rva != 0 && !bounds_check_virtual_address (ctx, rva, size)) ADD_ERROR (ctx, g_strdup_printf ("Invalid cli section %i rva/size pair %x/%x", i, rva, size)); ptr += 8; if (rva && i > 1) ADD_ERROR (ctx, g_strdup_printf ("Metadata verifier doesn't support cli header section %d", i)); } } static guint32 pad4 (guint32 offset) { if (offset & 0x3) //pad to the next 4 byte boundary offset = (offset & ~0x3) + 4; return offset; } static void verify_metadata_header (VerifyContext *ctx) { int i; DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX); guint32 offset, section_count; const char *ptr; offset = it.translated_offset; ptr = ctx->data + offset; g_assert (offset != INVALID_OFFSET); //build a directory entry for the metadata root ptr += 8; it.rva = read32 (ptr); ptr += 4; it.size = read32 (ptr); it.translated_offset = offset = translate_rva (ctx, it.rva); ptr = ctx->data + offset; g_assert (offset != INVALID_OFFSET); if (it.size < 20) ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small %d (at least 20 bytes required for initial decoding)", it.size)); if (read32 (ptr) != 0x424A5342) ADD_ERROR (ctx, g_strdup_printf ("Invalid metadata signature, expected 0x424A5342 but got %08x", read32 (ptr))); offset = pad4 (offset + 16 + read32 (ptr + 12)); if (!bounds_check_datadir (&it, offset, 4)) 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)); ptr = ctx->data + offset; //move to streams header section_count = read16 (ptr + 2); if (section_count < 2) ADD_ERROR (ctx, g_strdup_printf ("Metadata root section must have at least 2 streams (#~ and #GUID)")); ptr += 4; offset += 4; for (i = 0; i < section_count; ++i) { guint32 stream_off, stream_size; int string_size, stream_idx; if (!bounds_check_datadir (&it, offset, 8)) 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)); stream_off = it.translated_offset + read32 (ptr); stream_size = read32 (ptr + 4); if (!bounds_check_datadir (&it, stream_off, stream_size)) ADD_ERROR (ctx, g_strdup_printf ("Invalid stream header %d offset/size pair %x/%x", 0, stream_off, stream_size)); ptr += 8; offset += 8; for (string_size = 0; string_size < 32; ++string_size) { if (!bounds_check_datadir (&it, offset++, 1)) ADD_ERROR (ctx, g_strdup_printf ("Metadata root section is too small to decode stream header %d name", i)); if (!ptr [string_size]) break; } if (ptr [string_size]) ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d name larger than 32 bytes", i)); if (!strncmp ("#Strings", ptr, 9)) stream_idx = STRINGS_STREAM; else if (!strncmp ("#US", ptr, 4)) stream_idx = USER_STRINGS_STREAM; else if (!strncmp ("#Blob", ptr, 6)) stream_idx = BLOB_STREAM; else if (!strncmp ("#GUID", ptr, 6)) stream_idx = GUID_STREAM; else if (!strncmp ("#~", ptr, 3)) stream_idx = TILDE_STREAM; else { ADD_WARNING (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr)); offset = pad4 (offset); ptr = ctx->data + offset; continue; } if (ctx->metadata_streams [stream_idx].offset != 0) ADD_ERROR (ctx, g_strdup_printf ("Duplicated metadata stream header %s", ptr)); ctx->metadata_streams [stream_idx].offset = stream_off; ctx->metadata_streams [stream_idx].size = stream_size; offset = pad4 (offset); ptr = ctx->data + offset; } if (!ctx->metadata_streams [TILDE_STREAM].size) ADD_ERROR (ctx, g_strdup_printf ("Metadata #~ stream missing")); if (!ctx->metadata_streams [GUID_STREAM].size) ADD_ERROR (ctx, g_strdup_printf ("Metadata guid stream missing")); } static void verify_tables_schema (VerifyContext *ctx) { OffsetAndSize tables_area = ctx->metadata_streams [TILDE_STREAM]; unsigned offset = tables_area.offset; const char *ptr = ctx->data + offset; guint64 valid_tables; guint32 count; int i; if (tables_area.size < 24) ADD_ERROR (ctx, g_strdup_printf ("Table schemata size (%d) too small to for initial decoding (requires 24 bytes)", tables_area.size)); if (ptr [4] != 2 && ptr [4] != 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata major version %d, expected 2", ptr [4])); if (ptr [5] != 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid table schemata minor version %d, expected 0", ptr [5])); if ((ptr [6] & ~0x7) != 0) 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])); valid_tables = read64 (ptr + 8); count = 0; for (i = 0; i < 64; ++i) { if (!(valid_tables & ((guint64)1 << i))) continue; /*MS Extensions: 0x3 0x5 0x7 0x13 0x16 Unused: 0x1E 0x1F 0x2D-0x3F We don't care about the MS extensions.*/ if (i == 0x3 || i == 0x5 || i == 0x7 || i == 0x13 || i == 0x16) ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support MS specific table %x", i)); if (i == 0x1E || i == 0x1F || i >= 0x2D) ADD_ERROR (ctx, g_strdup_printf ("Invalid table %x", i)); ++count; } if (tables_area.size < 24 + count * 4) 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)); ptr += 24; for (i = 0; i < 64; ++i) { if (valid_tables & ((guint64)1 << i)) { guint32 row_count = read32 (ptr); if (row_count > (1 << 24) - 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid Table %d row count: %d. Mono only supports 16777215 rows", i, row_count)); ptr += 4; } } } /*----------nothing from here on can use data_directory or metadata_streams ---*/ static guint32 get_col_offset (VerifyContext *ctx, int table, int column) { guint32 bitfield = ctx->image->tables [table].size_bitfield; guint32 offset = 0; while (column-- > 0) offset += mono_metadata_table_size (bitfield, column); return offset; } static guint32 get_col_size (VerifyContext *ctx, int table, int column) { return mono_metadata_table_size (ctx->image->tables [table].size_bitfield, column); } static OffsetAndSize get_metadata_stream (VerifyContext *ctx, MonoStreamHeader *header) { OffsetAndSize res; res.offset = header->data - ctx->data; res.size = header->size; return res; } static gboolean is_valid_string_full_with_image (MonoImage *image, guint32 offset, gboolean allow_empty) { guint32 heap_offset = (char*)image->heap_strings.data - image->raw_data; guint32 heap_size = image->heap_strings.size; glong length; const char *data = image->raw_data + heap_offset; if (offset >= heap_size) return FALSE; if (CHECK_ADDP_OVERFLOW_UN (data, offset)) return FALSE; if (!mono_utf8_validate_and_len_with_bounds (data + offset, heap_size - offset, &length, NULL)) return FALSE; return allow_empty || length > 0; } static gboolean is_valid_string_full (VerifyContext *ctx, guint32 offset, gboolean allow_empty) { return is_valid_string_full_with_image (ctx->image, offset, allow_empty); } static gboolean is_valid_string (VerifyContext *ctx, guint32 offset) { return is_valid_string_full (ctx, offset, TRUE); } static gboolean is_valid_non_empty_string (VerifyContext *ctx, guint32 offset) { return is_valid_string_full (ctx, offset, FALSE); } static gboolean is_valid_guid (VerifyContext *ctx, guint32 offset) { OffsetAndSize guids = get_metadata_stream (ctx, &ctx->image->heap_guid); return guids.size >= 8 && guids.size - 8 >= offset; } static guint32 get_coded_index_token (int token_kind, guint32 coded_token) { guint32 bits = coded_index_desc [token_kind]; return coded_token >> bits; } static guint32 get_coded_index_table (int kind, guint32 coded_token) { guint32 idx, bits = coded_index_desc [kind]; kind += 2; idx = coded_token & ((1 << bits) - 1); return coded_index_desc [kind + idx]; } static guint32 make_coded_token (int kind, guint32 table, guint32 table_idx) { guint32 bits = coded_index_desc [kind++]; guint32 tables = coded_index_desc [kind++]; guint32 i; for (i = 0; i < tables; ++i) { if (coded_index_desc [kind++] == table) return ((table_idx + 1) << bits) | i; } g_assert_not_reached (); return -1; } static gboolean is_valid_coded_index_with_image (MonoImage *image, int token_kind, guint32 coded_token) { guint32 bits = coded_index_desc [token_kind++]; guint32 table_count = coded_index_desc [token_kind++]; guint32 table = coded_token & ((1 << bits) - 1); guint32 token = coded_token >> bits; if (table >= table_count) return FALSE; /*token_kind points to the first table idx*/ table = coded_index_desc [token_kind + table]; if (table == INVALID_TABLE) return FALSE; return token <= image->tables [table].rows; } static gboolean is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token) { return is_valid_coded_index_with_image (ctx->image, token_kind, coded_token); } typedef struct { guint32 token; guint32 col_size; guint32 col_offset; MonoTableInfo *table; } RowLocator; static int token_locator (const void *a, const void *b) { RowLocator *loc = (RowLocator *)a; unsigned const char *row = (unsigned const char *)b; guint32 token = loc->col_size == 2 ? read16 (row + loc->col_offset) : read32 (row + loc->col_offset); VERIFIER_DEBUG ( printf ("\tfound token %x at idx %d\n", token, ((const char*)row - loc->table->base) / loc->table->row_size) ); return (int)loc->token - (int)token; } static int search_sorted_table (VerifyContext *ctx, int table, int column, guint32 coded_token) { MonoTableInfo *tinfo = &ctx->image->tables [table]; RowLocator locator; const char *res, *base; locator.token = coded_token; locator.col_offset = get_col_offset (ctx, table, column); locator.col_size = get_col_size (ctx, table, column); locator.table = tinfo; base = tinfo->base; 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) ); res = (const char *)mono_binary_search (&locator, base, tinfo->rows, tinfo->row_size, token_locator); if (!res) return -1; return (res - base) / tinfo->row_size; } /*WARNING: This function doesn't verify if the strings @offset points to a valid string*/ static const char* get_string_ptr (VerifyContext *ctx, guint offset) { return ctx->image->heap_strings.data + offset; } /*WARNING: This function doesn't verify if the strings @offset points to a valid string*/ static int string_cmp (VerifyContext *ctx, const char *str, guint offset) { if (offset == 0) return strcmp (str, ""); return strcmp (str, get_string_ptr (ctx, offset)); } static gboolean mono_verifier_is_corlib (MonoImage *image) { gboolean trusted_location = !mono_security_core_clr_enabled () ? TRUE : mono_security_core_clr_is_platform_image (image); return trusted_location && image->module_name && !strcmp ("mscorlib.dll", image->module_name); } static gboolean typedef_is_system_object (VerifyContext *ctx, guint32 *data) { return mono_verifier_is_corlib (ctx->image) && !string_cmp (ctx, "System", data [MONO_TYPEDEF_NAMESPACE]) && !string_cmp (ctx, "Object", data [MONO_TYPEDEF_NAME]); } static gboolean decode_value (const char *_ptr, unsigned available, unsigned *value, unsigned *size) { unsigned char b; const unsigned char *ptr = (const unsigned char *)_ptr; if (!available) return FALSE; b = *ptr; *value = *size = 0; if ((b & 0x80) == 0) { *size = 1; *value = b; } else if ((b & 0x40) == 0) { if (available < 2) return FALSE; *size = 2; *value = ((b & 0x3f) << 8 | ptr [1]); } else { if (available < 4) return FALSE; *size = 4; *value = ((b & 0x1f) << 24) | (ptr [1] << 16) | (ptr [2] << 8) | ptr [3]; } return TRUE; } static gboolean decode_signature_header (VerifyContext *ctx, guint32 offset, guint32 *size, const char **first_byte) { MonoStreamHeader blob = ctx->image->heap_blob; guint32 value, enc_size; if (offset >= blob.size) return FALSE; if (!decode_value (blob.data + offset, blob.size - offset, &value, &enc_size)) return FALSE; if (CHECK_ADD4_OVERFLOW_UN (offset, enc_size)) return FALSE; offset += enc_size; if (ADD_IS_GREATER_OR_OVF (offset, value, blob.size)) return FALSE; *size = value; *first_byte = blob.data + offset; return TRUE; } static gboolean safe_read (const char **_ptr, const char *limit, unsigned *dest, int size) { const char *ptr = *_ptr; if (ptr + size > limit) return FALSE; switch (size) { case 1: *dest = *((guint8*)ptr); ++ptr; break; case 2: *dest = read16 (ptr); ptr += 2; break; case 4: *dest = read32 (ptr); ptr += 4; break; } *_ptr = ptr; return TRUE; } static gboolean safe_read_compressed_int (const char **_ptr, const char *limit, unsigned *dest) { unsigned size = 0; const char *ptr = *_ptr; gboolean res = decode_value (ptr, limit - ptr, dest, &size); *_ptr = ptr + size; return res; } #define safe_read8(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 1) #define safe_read_cint(VAR, PTR, LIMIT) safe_read_compressed_int (&PTR, LIMIT, &VAR) #define safe_read16(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 2) #define safe_read32(VAR, PTR, LIMIT) safe_read (&PTR, LIMIT, &VAR, 4) static gboolean parse_type (VerifyContext *ctx, const char **_ptr, const char *end); static gboolean parse_method_signature (VerifyContext *ctx, const char **_ptr, const char *end, gboolean allow_sentinel, gboolean allow_unmanaged); static gboolean parse_custom_mods (VerifyContext *ctx, const char **_ptr, const char *end) { const char *ptr = *_ptr; unsigned type = 0; unsigned token = 0; while (TRUE) { if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("CustomMod: Not enough room for the type")); if (type != MONO_TYPE_CMOD_REQD && type != MONO_TYPE_CMOD_OPT) { --ptr; break; } if (!safe_read_cint (token, ptr, end)) FAIL (ctx, g_strdup ("CustomMod: Not enough room for the token")); if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token)) FAIL (ctx, g_strdup_printf ("CustomMod: invalid TypeDefOrRef token %x", token)); } *_ptr = ptr; return TRUE; } static gboolean parse_array_shape (VerifyContext *ctx, const char **_ptr, const char *end) { const char *ptr = *_ptr; unsigned val = 0; unsigned size, num, i; if (!safe_read8 (val, ptr, end)) FAIL (ctx, g_strdup ("ArrayShape: Not enough room for Rank")); if (val == 0) FAIL (ctx, g_strdup ("ArrayShape: Invalid shape with zero Rank")); if (!safe_read_cint (size, ptr, end)) FAIL (ctx, g_strdup ("ArrayShape: Not enough room for NumSizes")); for (i = 0; i < size; ++i) { if (!safe_read_cint (num, ptr, end)) FAIL (ctx, g_strdup_printf ("ArrayShape: Not enough room for Size of rank %d", i + 1)); } if (!safe_read_cint (size, ptr, end)) FAIL (ctx, g_strdup ("ArrayShape: Not enough room for NumLoBounds")); for (i = 0; i < size; ++i) { if (!safe_read_cint (num, ptr, end)) FAIL (ctx, g_strdup_printf ("ArrayShape: Not enough room for LoBound of rank %d", i + 1)); } *_ptr = ptr; return TRUE; } static gboolean parse_generic_inst (VerifyContext *ctx, const char **_ptr, const char *end) { const char *ptr = *_ptr; unsigned type; unsigned count, token, i; if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("GenericInst: Not enough room for kind")); if (type != MONO_TYPE_CLASS && type != MONO_TYPE_VALUETYPE) FAIL (ctx, g_strdup_printf ("GenericInst: Invalid GenericInst kind %x\n", type)); if (!safe_read_cint (token, ptr, end)) FAIL (ctx, g_strdup ("GenericInst: Not enough room for type token")); if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token)) FAIL (ctx, g_strdup_printf ("GenericInst: invalid TypeDefOrRef token %x", token)); if (ctx->token) { if (mono_metadata_token_index (ctx->token) == get_coded_index_token (TYPEDEF_OR_REF_DESC, token) && mono_metadata_token_table (ctx->token) == get_coded_index_table (TYPEDEF_OR_REF_DESC, token)) FAIL (ctx, g_strdup_printf ("Type: Recurside generic instance specification (%x). A type signature can't reference itself", ctx->token)); } if (!safe_read_cint (count, ptr, end)) FAIL (ctx, g_strdup ("GenericInst: Not enough room for argument count")); if (count == 0) FAIL (ctx, g_strdup ("GenericInst: Zero arguments generic instance")); for (i = 0; i < count; ++i) { if (!parse_custom_mods (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr")); if (!parse_type (ctx, &ptr, end)) FAIL (ctx, g_strdup_printf ("GenericInst: invalid generic argument %d", i + 1)); } *_ptr = ptr; return TRUE; } static gboolean parse_type (VerifyContext *ctx, const char **_ptr, const char *end) { const char *ptr = *_ptr; unsigned type; unsigned token = 0; if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("Type: Not enough room for the type")); if (!((type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_PTR) || (type >= MONO_TYPE_VALUETYPE && type <= MONO_TYPE_GENERICINST) || (type >= MONO_TYPE_I && type <= MONO_TYPE_U) || (type >= MONO_TYPE_FNPTR && type <= MONO_TYPE_MVAR))) FAIL (ctx, g_strdup_printf ("Type: Invalid type kind %x\n", type)); switch (type) { case MONO_TYPE_PTR: if (!parse_custom_mods (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr")); if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("Type: Not enough room to parse the pointer type")); if (type != MONO_TYPE_VOID) { --ptr; if (!parse_type (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Could not parse pointer type")); } break; case MONO_TYPE_VALUETYPE: case MONO_TYPE_CLASS: if (!safe_read_cint (token, ptr, end)) FAIL (ctx, g_strdup ("Type: Not enough room for the type token")); if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token) || !get_coded_index_token (TYPEDEF_OR_REF_DESC, token)) FAIL (ctx, g_strdup_printf ("Type: invalid TypeDefOrRef token %x", token)); if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, token)) FAIL (ctx, g_strdup_printf ("Type: zero TypeDefOrRef token %x", token)); if (ctx->token) { if (mono_metadata_token_index (ctx->token) == get_coded_index_token (TYPEDEF_OR_REF_DESC, token) && mono_metadata_token_table (ctx->token) == get_coded_index_table (TYPEDEF_OR_REF_DESC, token)) FAIL (ctx, g_strdup_printf ("Type: Recursive type specification (%x). A type signature can't reference itself", ctx->token)); } break; case MONO_TYPE_VAR: case MONO_TYPE_MVAR: if (!safe_read_cint (token, ptr, end)) FAIL (ctx, g_strdup ("Type: Not enough room for to decode generic argument number")); break; case MONO_TYPE_ARRAY: if (!parse_type (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Could not parse array type")); if (!parse_array_shape (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Could not parse array shape")); break; case MONO_TYPE_GENERICINST: if (!parse_generic_inst (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Could not parse generic inst")); break; case MONO_TYPE_FNPTR: if (!parse_method_signature (ctx, &ptr, end, TRUE, TRUE)) FAIL (ctx, g_strdup ("Type: Could not parse method pointer signature")); break; case MONO_TYPE_SZARRAY: if (!parse_custom_mods (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Failed to parse array element custom attr")); if (!parse_type (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Could not parse array type")); break; } *_ptr = ptr; return TRUE; } static gboolean parse_return_type (VerifyContext *ctx, const char **_ptr, const char *end) { const char *ptr; unsigned type = 0; if (!parse_custom_mods (ctx, _ptr, end)) return FALSE; ptr = *_ptr; if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("ReturnType: Not enough room for the type")); if (type == MONO_TYPE_VOID || type == MONO_TYPE_TYPEDBYREF) { *_ptr = ptr; return TRUE; } //it's a byref, update the cursor ptr if (type == MONO_TYPE_BYREF) *_ptr = ptr; return parse_type (ctx, _ptr, end); } static gboolean parse_param (VerifyContext *ctx, const char **_ptr, const char *end) { const char *ptr; unsigned type = 0; if (!parse_custom_mods (ctx, _ptr, end)) return FALSE; ptr = *_ptr; if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("Param: Not enough room for the type")); if (type == MONO_TYPE_TYPEDBYREF) { *_ptr = ptr; return TRUE; } //it's a byref, update the cursor ptr if (type == MONO_TYPE_BYREF) { *_ptr = ptr; if (!parse_custom_mods (ctx, _ptr, end)) return FALSE; } return parse_type (ctx, _ptr, end); } static gboolean parse_method_signature (VerifyContext *ctx, const char **_ptr, const char *end, gboolean allow_sentinel, gboolean allow_unmanaged) { unsigned cconv = 0; unsigned param_count = 0, gparam_count = 0, type = 0, i; const char *ptr = *_ptr; gboolean saw_sentinel = FALSE; if (!safe_read8 (cconv, ptr, end)) FAIL (ctx, g_strdup ("MethodSig: Not enough room for the call conv")); if (cconv & 0x80) FAIL (ctx, g_strdup ("MethodSig: CallConv has 0x80 set")); if (allow_unmanaged) { if ((cconv & 0x0F) > MONO_CALL_VARARG) FAIL (ctx, g_strdup_printf ("MethodSig: CallConv is not valid, it's %x", cconv & 0x0F)); } else if ((cconv & 0x0F) != MONO_CALL_DEFAULT && (cconv & 0x0F) != MONO_CALL_VARARG) FAIL (ctx, g_strdup_printf ("MethodSig: CallConv is not Default or Vararg, it's %x", cconv & 0x0F)); if ((cconv & 0x10) && !safe_read_cint (gparam_count, ptr, end)) FAIL (ctx, g_strdup ("MethodSig: Not enough room for the generic param count")); if ((cconv & 0x10) && gparam_count == 0) FAIL (ctx, g_strdup ("MethodSig: Signature with generics but zero arity")); if (allow_unmanaged && (cconv & 0x10)) FAIL (ctx, g_strdup ("MethodSig: Standalone signature with generic params")); if (!safe_read_cint (param_count, ptr, end)) FAIL (ctx, g_strdup ("MethodSig: Not enough room for the param count")); if (!parse_return_type (ctx, &ptr, end)) FAIL (ctx, g_strdup ("MethodSig: Error parsing return type")); for (i = 0; i < param_count; ++i) { if (allow_sentinel) { if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup_printf ("MethodSig: Not enough room for param %d type", i)); if (type == MONO_TYPE_SENTINEL) { if ((cconv & 0x0F) != MONO_CALL_VARARG) FAIL (ctx, g_strdup ("MethodSig: Found sentinel but signature is not vararg")); if (saw_sentinel) FAIL (ctx, g_strdup ("MethodSig: More than one sentinel type")); saw_sentinel = TRUE; } else { --ptr; } } if (!parse_param (ctx, &ptr, end)) FAIL (ctx, g_strdup_printf ("MethodSig: Error parsing arg %d", i)); } *_ptr = ptr; return TRUE; } static gboolean parse_property_signature (VerifyContext *ctx, const char **_ptr, const char *end) { unsigned type = 0; unsigned sig = 0; unsigned param_count = 0, i; const char *ptr = *_ptr; if (!safe_read8 (sig, ptr, end)) FAIL (ctx, g_strdup ("PropertySig: Not enough room for signature")); if (sig != 0x08 && sig != 0x28) FAIL (ctx, g_strdup_printf ("PropertySig: Signature is not 0x28 or 0x08: %x", sig)); if (!safe_read_cint (param_count, ptr, end)) FAIL (ctx, g_strdup ("PropertySig: Not enough room for the param count")); if (!parse_custom_mods (ctx, &ptr, end)) return FALSE; if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("PropertySig: Not enough room for the type")); //check if it's a byref. safe_read8 did update ptr, so we rollback if it's not a byref if (type != MONO_TYPE_BYREF) --ptr; if (!parse_type (ctx, &ptr, end)) FAIL (ctx, g_strdup ("PropertySig: Could not parse property type")); for (i = 0; i < param_count; ++i) { if (!parse_custom_mods (ctx, &ptr, end)) FAIL (ctx, g_strdup ("Type: Failed to parse pointer custom attr")); if (!parse_type (ctx, &ptr, end)) FAIL (ctx, g_strdup_printf ("PropertySig: Error parsing arg %d", i)); } *_ptr = ptr; return TRUE; } static gboolean parse_field (VerifyContext *ctx, const char **_ptr, const char *end) { const char *ptr = *_ptr; unsigned signature = 0; if (!safe_read8 (signature, ptr, end)) FAIL (ctx, g_strdup ("Field: Not enough room for field signature")); if (signature != 0x06) FAIL (ctx, g_strdup_printf ("Field: Invalid signature 0x%x, must be 6", signature)); if (!parse_custom_mods (ctx, &ptr, end)) return FALSE; if (safe_read8 (signature, ptr, end)) { if (signature != MONO_TYPE_BYREF) --ptr; } *_ptr = ptr; return parse_type (ctx, _ptr, end); } static gboolean parse_locals_signature (VerifyContext *ctx, const char **_ptr, const char *end) { unsigned sig = 0; unsigned locals_count = 0, i; const char *ptr = *_ptr; if (!safe_read8 (sig, ptr, end)) FAIL (ctx, g_strdup ("LocalsSig: Not enough room for signature")); if (sig != 0x07) FAIL (ctx, g_strdup_printf ("LocalsSig: Signature is not 0x28 or 0x08: %x", sig)); if (!safe_read_cint (locals_count, ptr, end)) FAIL (ctx, g_strdup ("LocalsSig: Not enough room for the param count")); /* LAMEIMPL: MS sometimes generates empty local signatures and its verifier is ok with. if (locals_count == 0) FAIL (ctx, g_strdup ("LocalsSig: Signature with zero locals")); */ for (i = 0; i < locals_count; ++i) { if (!safe_read8 (sig, ptr, end)) FAIL (ctx, g_strdup ("LocalsSig: Not enough room for type")); while (sig == MONO_TYPE_CMOD_REQD || sig == MONO_TYPE_CMOD_OPT || sig == MONO_TYPE_PINNED) { if (sig != MONO_TYPE_PINNED && !parse_custom_mods (ctx, &ptr, end)) FAIL (ctx, g_strdup_printf ("LocalsSig: Error parsing local %d", i)); if (!safe_read8 (sig, ptr, end)) FAIL (ctx, g_strdup ("LocalsSig: Not enough room for type")); } if (sig == MONO_TYPE_BYREF) { if (!safe_read8 (sig, ptr, end)) FAIL (ctx, g_strdup_printf ("Type: Not enough room for byref type for local %d", i)); if (sig == MONO_TYPE_TYPEDBYREF) FAIL (ctx, g_strdup_printf ("Type: Invalid type typedref& for local %d", i)); } if (sig == MONO_TYPE_TYPEDBYREF) continue; --ptr; if (!parse_type (ctx, &ptr, end)) FAIL (ctx, g_strdup_printf ("LocalsSig: Error parsing local %d", i)); } *_ptr = ptr; return TRUE; } static gboolean is_valid_field_signature (VerifyContext *ctx, guint32 offset) { guint32 size = 0; unsigned signature = 0; const char *ptr = NULL, *end; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("FieldSig: Could not decode signature header")); end = ptr + size; if (!safe_read8 (signature, ptr, end)) FAIL (ctx, g_strdup ("FieldSig: Not enough room for the signature")); if (signature != 6) FAIL (ctx, g_strdup_printf ("FieldSig: Invalid signature %x", signature)); --ptr; return parse_field (ctx, &ptr, end); } static gboolean is_valid_method_signature (VerifyContext *ctx, guint32 offset) { guint32 size = 0; const char *ptr = NULL, *end; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("MethodSig: Could not decode signature header")); end = ptr + size; return parse_method_signature (ctx, &ptr, end, FALSE, FALSE); } static gboolean is_valid_memberref_method_signature (VerifyContext *ctx, guint32 offset) { guint32 size = 0; const char *ptr = NULL, *end; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("MemberRefSig: Could not decode signature header")); end = ptr + size; return parse_method_signature (ctx, &ptr, end, TRUE, FALSE); } static gboolean is_valid_method_or_field_signature (VerifyContext *ctx, guint32 offset) { guint32 size = 0; unsigned signature = 0; const char *ptr = NULL, *end; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("MemberRefSig: Could not decode signature header")); end = ptr + size; if (!safe_read8 (signature, ptr, end)) FAIL (ctx, g_strdup ("MemberRefSig: Not enough room for the call conv")); --ptr; if (signature == 0x06) return parse_field (ctx, &ptr, end); return parse_method_signature (ctx, &ptr, end, TRUE, FALSE); } static gboolean is_valid_cattr_blob (VerifyContext *ctx, guint32 offset) { guint32 size = 0; unsigned prolog = 0; const char *ptr = NULL, *end; if (!offset) return TRUE; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("CustomAttribute: Could not decode signature header")); end = ptr + size; if (!safe_read16 (prolog, ptr, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for prolog")); if (prolog != 1) FAIL (ctx, g_strdup_printf ("CustomAttribute: Prolog is 0x%x, expected 0x1", prolog)); return TRUE; } static gboolean is_valid_cattr_type (MonoType *type) { MonoClass *klass; if (type->type == MONO_TYPE_OBJECT || (type->type >= MONO_TYPE_BOOLEAN && type->type <= MONO_TYPE_STRING)) return TRUE; if (type->type == MONO_TYPE_VALUETYPE) { klass = mono_class_from_mono_type (type); return klass && klass->enumtype; } if (type->type == MONO_TYPE_CLASS) return mono_class_from_mono_type (type) == mono_defaults.systemtype_class; return FALSE; } static gboolean is_valid_ser_string_full (VerifyContext *ctx, const char **str_start, guint32 *str_len, const char **_ptr, const char *end) { guint32 size = 0; const char *ptr = *_ptr; *str_start = NULL; *str_len = 0; if (ptr >= end) FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string size")); /*NULL string*/ if (*ptr == (char)0xFF) { *_ptr = ptr + 1; return TRUE; } if (!safe_read_cint (size, ptr, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string size")); if (ADDP_IS_GREATER_OR_OVF (ptr, size, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for string")); *str_start = ptr; *str_len = size; *_ptr = ptr + size; return TRUE; } static gboolean is_valid_ser_string (VerifyContext *ctx, const char **_ptr, const char *end) { const char *dummy_str; guint32 dummy_int; return is_valid_ser_string_full (ctx, &dummy_str, &dummy_int, _ptr, end); } static MonoClass* get_enum_by_encoded_name (VerifyContext *ctx, const char **_ptr, const char *end) { MonoType *type; MonoClass *klass; const char *str_start = NULL; const char *ptr = *_ptr; char *enum_name; guint32 str_len = 0; if (!is_valid_ser_string_full (ctx, &str_start, &str_len, &ptr, end)) return NULL; /*NULL or empty string*/ if (str_start == NULL || str_len == 0) { ADD_ERROR_NO_RETURN (ctx, g_strdup ("CustomAttribute: Null or empty enum name")); return NULL; } enum_name = (char *)g_memdup (str_start, str_len + 1); enum_name [str_len] = 0; type = mono_reflection_type_from_name (enum_name, ctx->image); if (!type) { ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute: Invalid enum class %s", enum_name)); g_free (enum_name); return NULL; } g_free (enum_name); klass = mono_class_from_mono_type (type); if (!klass || !klass->enumtype) { ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute:Class %s::%s is not an enum", klass->name_space, klass->name)); return NULL; } *_ptr = ptr; return klass; } static gboolean is_valid_fixed_param (VerifyContext *ctx, MonoType *mono_type, const char **_ptr, const char *end) { MonoClass *klass; const char *ptr = *_ptr; int elem_size = 0; guint32 element_count, i; int type; klass = mono_type->data.klass; type = mono_type->type; handle_enum: switch (type) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_I1: case MONO_TYPE_U1: elem_size = 1; break; case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: elem_size = 2; break; case MONO_TYPE_I4: case MONO_TYPE_U4: case MONO_TYPE_R4: elem_size = 4; break; case MONO_TYPE_I8: case MONO_TYPE_U8: case MONO_TYPE_R8: elem_size = 8; break; case MONO_TYPE_STRING: *_ptr = ptr; return is_valid_ser_string (ctx, _ptr, end); case MONO_TYPE_OBJECT: { unsigned sub_type = 0; if (!safe_read8 (sub_type, ptr, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array type")); if (sub_type >= MONO_TYPE_BOOLEAN && sub_type <= MONO_TYPE_STRING) { type = sub_type; goto handle_enum; } if (sub_type == MONO_TYPE_ENUM) { klass = get_enum_by_encoded_name (ctx, &ptr, end); if (!klass) return FALSE; klass = klass->element_class; type = klass->byval_arg.type; goto handle_enum; } if (sub_type == 0x50) { /*Type*/ *_ptr = ptr; return is_valid_ser_string (ctx, _ptr, end); } if (sub_type == MONO_TYPE_SZARRAY) { MonoType simple_type = {{0}}; unsigned etype = 0; if (!safe_read8 (etype, ptr, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array element type")); if (etype == MONO_TYPE_ENUM) { klass = get_enum_by_encoded_name (ctx, &ptr, end); if (!klass) return FALSE; } else if (etype == 0x50 || etype == MONO_TYPE_CLASS) { klass = mono_defaults.systemtype_class; } else if ((etype >= MONO_TYPE_BOOLEAN && etype <= MONO_TYPE_STRING) || etype == 0x51) { simple_type.type = etype == 0x51 ? MONO_TYPE_OBJECT : (MonoTypeEnum)etype; klass = mono_class_from_mono_type (&simple_type); } else FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %x", etype)); type = MONO_TYPE_SZARRAY; goto handle_enum; } FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid boxed object type %x", sub_type)); } case MONO_TYPE_CLASS: if (klass && klass->enumtype) { klass = klass->element_class; type = klass->byval_arg.type; goto handle_enum; } if (klass != mono_defaults.systemtype_class) FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid class parameter type %s:%s ",klass->name_space, klass->name)); *_ptr = ptr; return is_valid_ser_string (ctx, _ptr, end); case MONO_TYPE_VALUETYPE: if (!klass || !klass->enumtype) FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid valuetype parameter expected enum %s:%s ",klass->name_space, klass->name)); klass = klass->element_class; type = klass->byval_arg.type; goto handle_enum; case MONO_TYPE_SZARRAY: mono_type = &klass->byval_arg; if (!is_valid_cattr_type (mono_type)) FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %s:%s ",klass->name_space, klass->name)); if (!safe_read32 (element_count, ptr, end)) FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid class parameter type %s:%s ",klass->name_space, klass->name)); if (element_count == 0xFFFFFFFFu) { *_ptr = ptr; return TRUE; } for (i = 0; i < element_count; ++i) { if (!is_valid_fixed_param (ctx, mono_type, &ptr, end)) return FALSE; } *_ptr = ptr; return TRUE; default: FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid parameter type %x ", type)); } if (ADDP_IS_GREATER_OR_OVF (ptr, elem_size, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough space for element")); *_ptr = ptr + elem_size; return TRUE; } static gboolean is_valid_cattr_content (VerifyContext *ctx, MonoMethod *ctor, const char *ptr, guint32 size) { MonoError error; unsigned prolog = 0; const char *end; MonoMethodSignature *sig; int args, i; unsigned num_named; if (!ctor) FAIL (ctx, g_strdup ("CustomAttribute: Invalid constructor")); sig = mono_method_signature_checked (ctor, &error); if (!mono_error_ok (&error)) { ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("CustomAttribute: Invalid constructor signature %s", mono_error_get_message (&error))); mono_error_cleanup (&error); return FALSE; } if (sig->sentinelpos != -1 || sig->call_convention == MONO_CALL_VARARG) FAIL (ctx, g_strdup ("CustomAttribute: Constructor cannot have VARAG signature")); end = ptr + size; if (!safe_read16 (prolog, ptr, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for prolog")); if (prolog != 1) FAIL (ctx, g_strdup_printf ("CustomAttribute: Prolog is 0x%x, expected 0x1", prolog)); args = sig->param_count; for (i = 0; i < args; ++i) { MonoType *arg_type = sig->params [i]; if (!is_valid_fixed_param (ctx, arg_type, &ptr, end)) return FALSE; } if (!safe_read16 (num_named, ptr, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough space for num_named field")); for (i = 0; i < num_named; ++i) { MonoType *type, simple_type = {{0}}; unsigned kind; if (!safe_read8 (kind, ptr, end)) FAIL (ctx, g_strdup_printf ("CustomAttribute: Not enough space for named parameter %d kind", i)); if (kind != 0x53 && kind != 0x54) FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid named parameter %d kind %x", i, kind)); if (!safe_read8 (kind, ptr, end)) FAIL (ctx, g_strdup_printf ("CustomAttribute: Not enough space for named parameter %d type", i)); if (kind >= MONO_TYPE_BOOLEAN && kind <= MONO_TYPE_STRING) { simple_type.type = (MonoTypeEnum)kind; type = &simple_type; } else if (kind == MONO_TYPE_ENUM) { MonoClass *klass = get_enum_by_encoded_name (ctx, &ptr, end); if (!klass) return FALSE; type = &klass->byval_arg; } else if (kind == 0x50) { type = &mono_defaults.systemtype_class->byval_arg; } else if (kind == 0x51) { type = &mono_defaults.object_class->byval_arg; } else if (kind == MONO_TYPE_SZARRAY) { MonoClass *klass; unsigned etype = 0; if (!safe_read8 (etype, ptr, end)) FAIL (ctx, g_strdup ("CustomAttribute: Not enough room for array element type")); if (etype == MONO_TYPE_ENUM) { klass = get_enum_by_encoded_name (ctx, &ptr, end); if (!klass) return FALSE; } else if (etype == 0x50 || etype == MONO_TYPE_CLASS) { klass = mono_defaults.systemtype_class; } else if ((etype >= MONO_TYPE_BOOLEAN && etype <= MONO_TYPE_STRING) || etype == 0x51) { simple_type.type = etype == 0x51 ? MONO_TYPE_OBJECT : (MonoTypeEnum)etype; klass = mono_class_from_mono_type (&simple_type); } else FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid array element type %x", etype)); type = &mono_array_class_get (klass, 1)->byval_arg; } else { FAIL (ctx, g_strdup_printf ("CustomAttribute: Invalid named parameter type %x", kind)); } if (!is_valid_ser_string (ctx, &ptr, end)) return FALSE; if (!is_valid_fixed_param (ctx, type, &ptr, end)) return FALSE; } return TRUE; } static gboolean is_valid_marshal_spec (VerifyContext *ctx, guint32 offset) { OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); //TODO do proper verification return blob.size >= 1 && blob.size - 1 >= offset; } static gboolean is_valid_permission_set (VerifyContext *ctx, guint32 offset) { OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); //TODO do proper verification return blob.size >= 1 && blob.size - 1 >= offset; } static gboolean is_valid_standalonesig_blob (VerifyContext *ctx, guint32 offset) { guint32 size = 0; unsigned signature = 0; const char *ptr = NULL, *end; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("StandAloneSig: Could not decode signature header")); end = ptr + size; if (!safe_read8 (signature, ptr, end)) FAIL (ctx, g_strdup ("StandAloneSig: Not enough room for the call conv")); --ptr; if (signature == 0x07) return parse_locals_signature (ctx, &ptr, end); /*F# and managed C++ produce standalonesig for fields even thou the spec doesn't mention it.*/ if (signature == 0x06) return parse_field (ctx, &ptr, end); return parse_method_signature (ctx, &ptr, end, TRUE, TRUE); } static gboolean is_valid_property_sig_blob (VerifyContext *ctx, guint32 offset) { guint32 size = 0; const char *ptr = NULL, *end; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("PropertySig: Could not decode signature header")); end = ptr + size; return parse_property_signature (ctx, &ptr, end); } static gboolean is_valid_typespec_blob (VerifyContext *ctx, guint32 offset) { guint32 size = 0; const char *ptr = NULL, *end; unsigned type = 0; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("TypeSpec: Could not decode signature header")); end = ptr + size; if (!parse_custom_mods (ctx, &ptr, end)) return FALSE; if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("TypeSpec: Not enough room for type")); if (type == MONO_TYPE_BYREF) { if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("TypeSpec: Not enough room for byref type")); if (type == MONO_TYPE_TYPEDBYREF) FAIL (ctx, g_strdup ("TypeSpec: Invalid type typedref&")); } if (type == MONO_TYPE_TYPEDBYREF) return TRUE; --ptr; return parse_type (ctx, &ptr, end); } static gboolean is_valid_methodspec_blob (VerifyContext *ctx, guint32 offset) { guint32 size = 0; const char *ptr = NULL, *end; unsigned type = 0; unsigned count = 0, i; if (!decode_signature_header (ctx, offset, &size, &ptr)) FAIL (ctx, g_strdup ("MethodSpec: Could not decode signature header")); end = ptr + size; if (!safe_read8 (type, ptr, end)) FAIL (ctx, g_strdup ("MethodSpec: Not enough room for call convention")); if (type != 0x0A) FAIL (ctx, g_strdup_printf ("MethodSpec: Invalid call convention 0x%x, expected 0x0A", type)); if (!safe_read_cint (count, ptr, end)) FAIL (ctx, g_strdup ("MethodSpec: Not enough room for parameter count")); if (!count) FAIL (ctx, g_strdup ("MethodSpec: Zero generic argument count")); for (i = 0; i < count; ++i) { if (!parse_custom_mods (ctx, &ptr, end)) return FALSE; if (!parse_type (ctx, &ptr, end)) FAIL (ctx, g_strdup_printf ("MethodSpec: Could not parse parameter %d", i + 1)); } return TRUE; } static gboolean is_valid_blob_object (VerifyContext *ctx, guint32 offset, guint32 minsize) { OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); guint32 entry_size, bytes; if (blob.size < offset) return FALSE; if (!decode_value (ctx->data + offset + blob.offset, blob.size - blob.offset, &entry_size, &bytes)) return FALSE; if (entry_size < minsize) return FALSE; if (CHECK_ADD4_OVERFLOW_UN (entry_size, bytes)) return FALSE; entry_size += bytes; return !ADD_IS_GREATER_OR_OVF (offset, entry_size, blob.size); } static gboolean is_valid_constant (VerifyContext *ctx, guint32 type, guint32 offset) { OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); guint32 size, entry_size, bytes; if (blob.size < offset) FAIL (ctx, g_strdup ("ContantValue: invalid offset")); if (!decode_value (ctx->data + offset + blob.offset, blob.size - blob.offset, &entry_size, &bytes)) FAIL (ctx, g_strdup ("ContantValue: not enough space to decode size")); if (type == MONO_TYPE_STRING) { //String is encoded as: compressed_int:len len *bytes offset += bytes; if (ADD_IS_GREATER_OR_OVF (offset, entry_size, blob.size)) FAIL (ctx, g_strdup_printf ("ContantValue: not enough space for string, required %d but got %d", entry_size * 2, blob.size - offset)); return TRUE; } switch (type) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_I1: case MONO_TYPE_U1: size = 1; break; case MONO_TYPE_CHAR: case MONO_TYPE_I2: case MONO_TYPE_U2: size = 2; break; case MONO_TYPE_I4: case MONO_TYPE_U4: case MONO_TYPE_R4: case MONO_TYPE_CLASS: size = 4; break; case MONO_TYPE_I8: case MONO_TYPE_U8: case MONO_TYPE_R8: size = 8; break; default: g_assert_not_reached (); } if (size != entry_size) FAIL (ctx, g_strdup_printf ("ContantValue: Expected size %d but got %d", size, entry_size)); offset += bytes; if (ADD_IS_GREATER_OR_OVF (offset, size, blob.size)) FAIL (ctx, g_strdup_printf ("ContantValue: Not enough room for constant, required %d but have %d", size, blob.size - offset)); if (type == MONO_TYPE_CLASS && read32 (ctx->data + blob.offset + offset)) FAIL (ctx, g_strdup_printf ("ContantValue: Type is class but value is not null")); return TRUE; } #define FAT_HEADER_INVALID_FLAGS ~(0x3 | 0x8 | 0x10 | 0xF000) //only 0x01, 0x40 and 0x80 are allowed #define SECTION_HEADER_INVALID_FLAGS 0x3E static gboolean is_valid_method_header (VerifyContext *ctx, guint32 rva, guint32 *locals_token) { unsigned local_vars_tok, code_size, offset = mono_cli_rva_image_map (ctx->image, rva); unsigned header = 0; unsigned fat_header = 0, size = 0, max_stack; const char *ptr = NULL, *end; *locals_token = 0; if (offset == INVALID_ADDRESS) FAIL (ctx, g_strdup ("MethodHeader: Invalid RVA")); ptr = ctx->data + offset; end = ctx->data + ctx->size; /*no worries if it spawns multiple sections*/ if (!safe_read8 (header, ptr, end)) FAIL (ctx, g_strdup ("MethodHeader: Not enough room for header")); switch (header & 0x3) { case 0: case 1: FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid header type 0x%x", header & 0x3)); case 2: header >>= 2; if (ADDP_IS_GREATER_OR_OVF (ptr, header, end)) FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for method body. Required %d, but only %d is available", header, (int)(end - ptr))); return TRUE; } //FAT HEADER --ptr; if (!safe_read16 (fat_header, ptr, end)) FAIL (ctx, g_strdup ("MethodHeader: Not enough room for fat header")); size = (fat_header >> 12) & 0xF; if (size != 3) FAIL (ctx, g_strdup ("MethodHeader: header size must be 3")); if (!safe_read16 (max_stack, ptr, end)) FAIL (ctx, g_strdup ("MethodHeader: Not enough room for max stack")); if (!safe_read32 (code_size, ptr, end)) FAIL (ctx, g_strdup ("MethodHeader: Not enough room for code size")); if (!safe_read32 (local_vars_tok, ptr, end)) FAIL (ctx, g_strdup ("MethodHeader: Not enough room for local vars tok")); if (local_vars_tok) { if (((local_vars_tok >> 24) & 0xFF) != 0x11) FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature table 0x%x", ((local_vars_tok >> 24) & 0xFF))); if ((local_vars_tok & 0xFFFFFF) > ctx->image->tables [MONO_TABLE_STANDALONESIG].rows) FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature points to invalid row 0x%x", local_vars_tok & 0xFFFFFF)); if (!(local_vars_tok & 0xFFFFFF)) FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature with zero index")); *locals_token = local_vars_tok & 0xFFFFFF; } if (fat_header & FAT_HEADER_INVALID_FLAGS) FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid fat signature flags %x", fat_header & FAT_HEADER_INVALID_FLAGS)); if (ADDP_IS_GREATER_OR_OVF (ptr, code_size, end)) FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for code %d", code_size)); if (!(fat_header & 0x08)) return TRUE; ptr += code_size; do { unsigned section_header = 0, section_size = 0; gboolean is_fat; ptr = dword_align (ptr); if (!safe_read32 (section_header, ptr, end)) FAIL (ctx, g_strdup ("MethodHeader: Not enough room for data section header")); if (section_header & SECTION_HEADER_INVALID_FLAGS) FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section header flags 0x%x", section_header & SECTION_HEADER_INVALID_FLAGS)); is_fat = (section_header & METHOD_HEADER_SECTION_FAT_FORMAT) != 0; section_size = (section_header >> 8) & (is_fat ? 0xFFFFFF : 0xFF); if (section_size < 4) FAIL (ctx, g_strdup_printf ("MethodHeader: Section size too small")); if (ADDP_IS_GREATER_OR_OVF (ptr, section_size - 4, end)) /*must be section_size -4 as ptr was incremented by safe_read32*/ FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for section content %d", section_size)); if (section_header & METHOD_HEADER_SECTION_EHTABLE) { guint32 i, clauses = section_size / (is_fat ? 24 : 12); /* LAMEIMPL: MS emits section_size without accounting for header size. Mono does as the spec says. section_size is header + section MS's peverify happily accepts both. */ if ((clauses * (is_fat ? 24 : 12) != section_size) && (clauses * (is_fat ? 24 : 12) + 4 != section_size)) FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid EH section size %d, it's not of the expected size %d", section_size, clauses * (is_fat ? 24 : 12))); /* only verify the class token is verified as the rest is done by the IL verifier*/ for (i = 0; i < clauses; ++i) { unsigned flags = *(unsigned char*)ptr; unsigned class_token = 0; ptr += (is_fat ? 20 : 8); if (!safe_read32 (class_token, ptr, end)) FAIL (ctx, g_strdup_printf ("MethodHeader: Not enough room for section %d", i)); if (flags == MONO_EXCEPTION_CLAUSE_NONE && class_token) { guint table = mono_metadata_token_table (class_token); if (table != MONO_TABLE_TYPEREF && table != MONO_TABLE_TYPEDEF && table != MONO_TABLE_TYPESPEC) FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section %d class token table %x", i, table)); if (mono_metadata_token_index (class_token) > ctx->image->tables [table].rows) FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid section %d class token index %x", i, mono_metadata_token_index (class_token))); } } } if (!(section_header & METHOD_HEADER_SECTION_MORE_SECTS)) break; } while (1); return TRUE; } static void verify_module_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MODULE]; guint32 data [MONO_MODULE_SIZE]; if (table->rows != 1) ADD_ERROR (ctx, g_strdup_printf ("Module table must have exactly one row, but have %d", table->rows)); mono_metadata_decode_row (table, 0, data, MONO_MODULE_SIZE); if (!is_valid_non_empty_string (ctx, data [MONO_MODULE_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid name, string index 0x%08x", data [MONO_MODULE_NAME])); if (!is_valid_guid (ctx, data [MONO_MODULE_MVID])) ADD_ERROR (ctx, g_strdup_printf ("Module has an invalid Mvid, guid index %x", data [MONO_MODULE_MVID])); if (data [MONO_MODULE_ENC] != 0) ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero Enc field %x", data [MONO_MODULE_ENC])); if (data [MONO_MODULE_ENCBASE] != 0) ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero EncBase field %x", data [MONO_MODULE_ENCBASE])); } static void verify_typeref_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF]; MonoError error; guint32 i; for (i = 0; i < table->rows; ++i) { mono_verifier_verify_typeref_row (ctx->image, i, &error); add_from_mono_error (ctx, &error); } } /*bits 9,11,14,15,19,21,24-31 */ #define INVALID_TYPEDEF_FLAG_BITS ((1 << 6) | (1 << 9) | (1 << 15) | (1 << 19) | (1 << 21) | 0xFF000000) static void verify_typedef_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF]; guint32 data [MONO_TYPEDEF_SIZE]; guint32 fieldlist = 1, methodlist = 1, visibility; int i; if (table->rows == 0) ADD_ERROR (ctx, g_strdup_printf ("Typedef table must have exactly at least one row")); for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE); if (data [MONO_TYPEDEF_FLAGS] & INVALID_TYPEDEF_FLAG_BITS) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid flags field 0x%08x rejected bits: 0x%08x", i, data [MONO_TYPEDEF_FLAGS], data [MONO_TYPEDEF_FLAGS] & INVALID_TYPEDEF_FLAG_BITS)); if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_LAYOUT_MASK) == 0x18) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid class layout 0x18", i)); if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_STRING_FORMAT_MASK) == 0x30000) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i)); if ((data [MONO_TYPEDEF_FLAGS] & 0xC00000) != 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d mono doesn't support custom string format", i)); if ((data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_INTERFACE) && (data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_ABSTRACT) == 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must be abstract", i)); if (!data [MONO_TYPEDEF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEDEF_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid name token %08x", i, data [MONO_TYPEDEF_NAME])); if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d invalid namespace token %08x", i, data [MONO_TYPEREF_NAMESPACE])); if (data [MONO_TYPEDEF_EXTENDS] && !is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS])) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d extend field coded index 0x%08x", i, data [MONO_TYPEDEF_EXTENDS])); if (data [MONO_TYPEDEF_EXTENDS] && !get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS])) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d zero coded extend field coded index 0x%08x", i, data [MONO_TYPEDEF_EXTENDS])); visibility = data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; if ((visibility >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visibility <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) && search_sorted_table (ctx, MONO_TABLE_NESTEDCLASS, MONO_NESTED_CLASS_NESTED, i + 1) == -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d has nested visibility but no rows in the NestedClass table", i)); if (data [MONO_TYPEDEF_FIELD_LIST] == 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList be be >= 1", i)); if (data [MONO_TYPEDEF_FIELD_LIST] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d FieldList rowid 0x%08x is out of range", i, data [MONO_TYPEDEF_FIELD_LIST])); if (data [MONO_TYPEDEF_FIELD_LIST] < fieldlist) 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)); if (data [MONO_TYPEDEF_METHOD_LIST] == 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList be be >= 1", i)); if (data [MONO_TYPEDEF_METHOD_LIST] > ctx->image->tables [MONO_TABLE_METHOD].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d MethodList rowid 0x%08x is out of range", i, data [MONO_TYPEDEF_METHOD_LIST])); if (data [MONO_TYPEDEF_METHOD_LIST] < methodlist) 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)); fieldlist = data [MONO_TYPEDEF_FIELD_LIST]; methodlist = data [MONO_TYPEDEF_METHOD_LIST]; } } static void verify_typedef_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF]; guint32 data [MONO_TYPEDEF_SIZE]; int i; if (table->rows == 0) ADD_ERROR (ctx, g_strdup_printf ("Typedef table must have exactly at least one row")); for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE); if (i == 0) { /*XXX it's ok if extends object, or anything at all, actually. */ /*if (data [MONO_TYPEDEF_EXTENDS] != 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row 0 for the special type must have a null extend field")); */ continue; } if (data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_INTERFACE) { if (data [MONO_TYPEDEF_EXTENDS]) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for interface type must have a null extend field", i)); } else { gboolean is_sys_obj = typedef_is_system_object (ctx, data); gboolean has_parent = get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS]) != 0; if (is_sys_obj) { if (has_parent) ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for System.Object must have a null extend field", i)); } else { if (!has_parent) { ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for non-interface type must have a non-null extend field", i)); } } } } } /*bits 3,11,14 */ #define INVALID_FIELD_FLAG_BITS ((1 << 3) | (1 << 11) | (1 << 14)) static void verify_field_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELD]; guint32 data [MONO_FIELD_SIZE], flags, module_field_list; int i; module_field_list = (guint32)-1; if (ctx->image->tables [MONO_TABLE_TYPEDEF].rows > 1) { MonoTableInfo *type = &ctx->image->tables [MONO_TABLE_TYPEDEF]; module_field_list = mono_metadata_decode_row_col (type, 1, MONO_TYPEDEF_FIELD_LIST); } for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_FIELD_SIZE); flags = data [MONO_FIELD_FLAGS]; if (flags & INVALID_FIELD_FLAG_BITS) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid flags field 0x%08x", i, flags)); if ((flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) == 0x7) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid field visibility 0x7", i)); if ((flags & (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY)) == (FIELD_ATTRIBUTE_LITERAL | FIELD_ATTRIBUTE_INIT_ONLY)) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d cannot be InitOnly and Literal at the same time", i)); if ((flags & FIELD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & FIELD_ATTRIBUTE_SPECIAL_NAME)) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is RTSpecialName but not SpecialName", i)); if ((flags & FIELD_ATTRIBUTE_LITERAL) && !(flags & FIELD_ATTRIBUTE_STATIC)) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but not Static", i)); if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL) && search_sorted_table (ctx, MONO_TABLE_FIELDMARSHAL, MONO_FIELD_MARSHAL_PARENT, make_coded_token (HAS_FIELD_MARSHAL_DESC, MONO_TABLE_FIELD, i)) == -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has FieldMarshal but there is no corresponding row in the FieldMarshal table", i)); if ((flags & FIELD_ATTRIBUTE_HAS_DEFAULT) && search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i)); if ((flags & FIELD_ATTRIBUTE_LITERAL) && search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_FIELD, i)) == -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is Literal but there is no corresponding row in the Constant table", i)); if ((flags & FIELD_ATTRIBUTE_HAS_FIELD_RVA) && search_sorted_table (ctx, MONO_TABLE_FIELDRVA, MONO_FIELD_RVA_FIELD, i + 1) == -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d has Default but there is no corresponding row in the Constant table", i)); if (!data [MONO_FIELD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_FIELD_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid name token %08x", i, data [MONO_FIELD_NAME])); if (data [MONO_FIELD_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_FIELD_SIGNATURE], 1)) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid signature blob token 0x%x", i, data [MONO_FIELD_SIGNATURE])); //TODO verify contant flag if (i + 1 < module_field_list) { guint32 access = flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK; if (!(flags & FIELD_ATTRIBUTE_STATIC)) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is a global variable but is not static", i)); if (access != FIELD_ATTRIBUTE_COMPILER_CONTROLLED && access != FIELD_ATTRIBUTE_PRIVATE && access != FIELD_ATTRIBUTE_PUBLIC) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d is a global variable but have wrong visibility %x", i, access)); } } } static void verify_field_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELD]; guint32 data [MONO_FIELD_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_FIELD_SIZE); if (!data [MONO_FIELD_SIGNATURE] || !is_valid_field_signature (ctx, data [MONO_FIELD_SIGNATURE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid field row %d invalid signature token %08x", i, data [MONO_FIELD_SIGNATURE])); } } /*bits 8,9,10,11,13,14,15*/ #define INVALID_METHOD_IMPLFLAG_BITS ((1 << 9) | (1 << 10) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15)) static void verify_method_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD]; guint32 data [MONO_METHOD_SIZE], flags, implflags, rva, module_method_list, access, code_type; guint32 paramlist = 1; gboolean is_ctor, is_cctor; const char *name; int i; module_method_list = (guint32)-1; if (ctx->image->tables [MONO_TABLE_TYPEDEF].rows > 1) { MonoTableInfo *type = &ctx->image->tables [MONO_TABLE_TYPEDEF]; module_method_list = mono_metadata_decode_row_col (type, 1, MONO_TYPEDEF_METHOD_LIST); } for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_METHOD_SIZE); rva = data [MONO_METHOD_RVA]; implflags = data [MONO_METHOD_IMPLFLAGS]; flags = data [MONO_METHOD_FLAGS]; access = flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK; code_type = implflags & METHOD_IMPL_ATTRIBUTE_CODE_TYPE_MASK; if (implflags & INVALID_METHOD_IMPLFLAG_BITS) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid implflags field 0x%08x", i, implflags)); if (access == 0x7) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid MemberAccessMask 0x7", i)); if (!data [MONO_METHOD_NAME] || !is_valid_non_empty_string (ctx, data [MONO_METHOD_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid name field 0x%08x", i, data [MONO_METHOD_NAME])); name = get_string_ptr (ctx, data [MONO_METHOD_NAME]); is_ctor = !strcmp (".ctor", name); is_cctor = !strcmp (".cctor", name); if ((is_ctor || is_cctor) && search_sorted_table (ctx, MONO_TABLE_GENERICPARAM, MONO_GENERICPARAM_OWNER, make_coded_token (TYPE_OR_METHODDEF_DESC, MONO_TABLE_METHOD, i)) != -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d .ctor or .cctor has generic param", i)); if ((flags & METHOD_ATTRIBUTE_STATIC) && (flags & (METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_NEW_SLOT))) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is static and (final, virtual or new slot)", i)); if (flags & METHOD_ATTRIBUTE_ABSTRACT) { if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and PinvokeImpl", i)); if (flags & METHOD_ATTRIBUTE_FINAL) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and Final", i)); if (!(flags & METHOD_ATTRIBUTE_VIRTUAL)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract but not Virtual", i)); } if (access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED && (flags & (METHOD_ATTRIBUTE_RT_SPECIAL_NAME | METHOD_ATTRIBUTE_SPECIAL_NAME))) ADD_WARNING (ctx, g_strdup_printf ("Invalid method row %d is CompileControlled and SpecialName or RtSpecialName", i)); if ((flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & METHOD_ATTRIBUTE_SPECIAL_NAME)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is RTSpecialName but not SpecialName", i)); //XXX no checks against cas stuff 10,11,12,13) //TODO check iface with .ctor (15,16) if (i + 1 < module_method_list) { if (!(flags & METHOD_ATTRIBUTE_STATIC)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not Static", i)); if (flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_VIRTUAL)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but is Abstract or Virtual", i)); if (access == METHOD_ATTRIBUTE_FAMILY || access == METHOD_ATTRIBUTE_FAM_AND_ASSEM || access == METHOD_ATTRIBUTE_FAM_OR_ASSEM) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is a global method but not CompilerControled, Public, Private or Assembly", i)); } //TODO check valuetype for synchronized if ((flags & (METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_NEW_SLOT | METHOD_ATTRIBUTE_STRICT)) && !(flags & METHOD_ATTRIBUTE_VIRTUAL)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is (Final, NewSlot or Strict) but not Virtual", i)); if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) { if (flags & METHOD_ATTRIBUTE_VIRTUAL) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl and Virtual", i)); if (!(flags & METHOD_ATTRIBUTE_STATIC)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but not Static", i)); } if (!(flags & METHOD_ATTRIBUTE_ABSTRACT) && !rva && !(flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) && !(implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && code_type != METHOD_IMPL_ATTRIBUTE_RUNTIME) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is not Abstract and neither PinvokeImpl, Runtime, InternalCall or with RVA != 0", i)); if (access == METHOD_ATTRIBUTE_COMPILER_CONTROLLED && !(rva || (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is CompilerControlled but neither RVA != 0 or PinvokeImpl", i)); //TODO check signature contents if (rva) { if ((flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_PINVOKE_IMPL)) || (implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is either Abstract, InternalCall or PinvokeImpl", i)); if (code_type == METHOD_IMPL_ATTRIBUTE_OPTIL) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is CodeTypeMask is neither Native, CIL or Runtime", i)); } else { if (!(flags & (METHOD_ATTRIBUTE_ABSTRACT | METHOD_ATTRIBUTE_PINVOKE_IMPL)) && !(implflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && code_type != METHOD_IMPL_ATTRIBUTE_RUNTIME) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA = 0 but neither Abstract, InternalCall, Runtime or PinvokeImpl", i)); } if ((flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) { if (rva) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but has RVA != 0", i)); if (search_sorted_table (ctx, MONO_TABLE_IMPLMAP, MONO_IMPLMAP_MEMBER, make_coded_token (MEMBER_FORWARDED_DESC, MONO_TABLE_METHOD, i)) == -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl but has no row in the ImplMap table", i)); } if (flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME && !is_ctor && !is_cctor) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is RtSpecialName but not named .ctor or .cctor", i)); if ((is_ctor || is_cctor) && !(flags & METHOD_ATTRIBUTE_RT_SPECIAL_NAME)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is named .ctor or .cctor but is not RtSpecialName", i)); if (data [MONO_METHOD_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_METHOD_SIGNATURE], 1)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid signature blob token 0x%x", i, data [MONO_METHOD_SIGNATURE])); if (data [MONO_METHOD_PARAMLIST] == 0) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList be be >= 1", i)); if (data [MONO_METHOD_PARAMLIST] < paramlist) 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)); if (data [MONO_METHOD_PARAMLIST] > ctx->image->tables [MONO_TABLE_PARAM].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d ParamList rowid 0x%08x is out of range", i, data [MONO_METHOD_PARAMLIST])); paramlist = data [MONO_METHOD_PARAMLIST]; } } static void verify_method_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD]; guint32 data [MONO_METHOD_SIZE], rva, locals_token; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_METHOD_SIZE); rva = data [MONO_METHOD_RVA]; if (!data [MONO_METHOD_SIGNATURE] || !is_valid_method_signature (ctx, data [MONO_METHOD_SIGNATURE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid signature token 0x%08x", i, data [MONO_METHOD_SIGNATURE])); if (rva && !is_valid_method_header (ctx, rva, &locals_token)) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d RVA points to an invalid method header", i)); } } static guint32 get_next_param_count (VerifyContext *ctx, guint32 *current_method) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD]; guint32 row = *current_method; guint32 paramlist, tmp; paramlist = mono_metadata_decode_row_col (table, row++, MONO_METHOD_PARAMLIST); while (row < table->rows) { tmp = mono_metadata_decode_row_col (table, row, MONO_METHOD_PARAMLIST); if (tmp > paramlist) { *current_method = row; return tmp - paramlist; } ++row; } /*no more methods, all params apply to the last one*/ *current_method = table->rows; return (guint32)-1; } #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)) static void verify_param_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PARAM]; guint32 data [MONO_PARAM_SIZE], flags, sequence = 0, remaining_params, current_method = 0; gboolean first_param = TRUE; int i; if (ctx->image->tables [MONO_TABLE_METHOD].rows == 0) { if (table->rows > 0) ADD_ERROR (ctx, g_strdup ("Param table has rows while the method table has zero")); return; } remaining_params = get_next_param_count (ctx, ¤t_method); for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_PARAM_SIZE); flags = data [MONO_PARAM_FLAGS]; if (flags & INVALID_PARAM_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d bad Flags value 0x%08x", i, flags)); if (search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_PARAM, i)) == -1) { if (flags & PARAM_ATTRIBUTE_HAS_DEFAULT) ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasDefault = 1 but no owned row in Contant table", i)); } else { if (!(flags & PARAM_ATTRIBUTE_HAS_DEFAULT)) ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasDefault = 0 but has owned row in Contant table", i)); } 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) ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d HasFieldMarshal = 1 but no owned row in FieldMarshal table", i)); if (!is_valid_string (ctx, data [MONO_PARAM_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d Name = 1 bad token 0x%08x", i, data [MONO_PARAM_NAME])); if (!first_param && data [MONO_PARAM_SEQUENCE] <= sequence) ADD_ERROR (ctx, g_strdup_printf ("Invalid param row %d sequece = %d previus param has %d", i, data [MONO_PARAM_SEQUENCE], sequence)); first_param = FALSE; sequence = data [MONO_PARAM_SEQUENCE]; if (--remaining_params == 0) { remaining_params = get_next_param_count (ctx, ¤t_method); first_param = TRUE; } } } static void verify_interfaceimpl_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_INTERFACEIMPL]; guint32 data [MONO_INTERFACEIMPL_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_INTERFACEIMPL_SIZE); if (data [MONO_INTERFACEIMPL_CLASS] && data [MONO_INTERFACEIMPL_CLASS] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows) ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Class field 0x%08x", i, data [MONO_INTERFACEIMPL_CLASS])); if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_INTERFACEIMPL_INTERFACE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Inteface field coded index 0x%08x", i, data [MONO_INTERFACEIMPL_INTERFACE])); if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_INTERFACEIMPL_INTERFACE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid InterfaceImpl row %d Inteface field is null", i)); } } static void verify_memberref_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MEMBERREF]; guint32 data [MONO_MEMBERREF_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_MEMBERREF_SIZE); if (!is_valid_coded_index (ctx, MEMBERREF_PARENT_DESC, data [MONO_MEMBERREF_CLASS])) ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Class field coded index 0x%08x", i, data [MONO_MEMBERREF_CLASS])); if (!get_coded_index_token (MEMBERREF_PARENT_DESC, data [MONO_MEMBERREF_CLASS])) ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Class field coded is null", i)); if (!is_valid_non_empty_string (ctx, data [MONO_MEMBERREF_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Name field coded is invalid or empty 0x%08x", i, data [MONO_MEMBERREF_NAME])); if (data [MONO_MEMBERREF_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_MEMBERREF_SIGNATURE], 1)) ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d invalid signature blob token 0x%x", i, data [MONO_MEMBERREF_SIGNATURE])); } } static void verify_memberref_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MEMBERREF]; guint32 data [MONO_MEMBERREF_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_MEMBERREF_SIZE); if (!is_valid_method_or_field_signature (ctx, data [MONO_MEMBERREF_SIGNATURE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid MemberRef row %d Signature field 0x%08x", i, data [MONO_MEMBERREF_SIGNATURE])); } } static void verify_constant_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CONSTANT]; guint32 data [MONO_CONSTANT_SIZE], type; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_CONSTANT_SIZE); type = data [MONO_CONSTANT_TYPE]; if (!((type >= MONO_TYPE_BOOLEAN && type <= MONO_TYPE_STRING) || type == MONO_TYPE_CLASS)) ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Type field 0x%08x", i, type)); if (!is_valid_coded_index (ctx, HAS_CONSTANT_DESC, data [MONO_CONSTANT_PARENT])) ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Parent field coded index 0x%08x", i, data [MONO_CONSTANT_PARENT])); if (!get_coded_index_token (HAS_CONSTANT_DESC, data [MONO_CONSTANT_PARENT])) ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Parent field coded is null", i)); if (!is_valid_constant (ctx, type, data [MONO_CONSTANT_VALUE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid Constant row %d Value field 0x%08x", i, data [MONO_CONSTANT_VALUE])); } } static void verify_cattr_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; guint32 data [MONO_CUSTOM_ATTR_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_CUSTOM_ATTR_SIZE); if (!is_valid_coded_index (ctx, HAS_CATTR_DESC, data [MONO_CUSTOM_ATTR_PARENT])) ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Parent field 0x%08x", i, data [MONO_CUSTOM_ATTR_PARENT])); if (!is_valid_coded_index (ctx, CATTR_TYPE_DESC, data [MONO_CUSTOM_ATTR_TYPE]) || !get_coded_index_token (CATTR_TYPE_DESC, data [MONO_CUSTOM_ATTR_TYPE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Type field 0x%08x", i, data [MONO_CUSTOM_ATTR_TYPE])); if (data [MONO_CUSTOM_ATTR_VALUE] && !is_valid_blob_object (ctx, data [MONO_CUSTOM_ATTR_VALUE], 0)) ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d invalid value blob 0x%x", i, data [MONO_CUSTOM_ATTR_VALUE])); } } static void verify_cattr_table_full (VerifyContext *ctx) { MonoError error; MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CUSTOMATTRIBUTE]; MonoMethod *ctor; const char *ptr; guint32 data [MONO_CUSTOM_ATTR_SIZE], mtoken, size; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_CUSTOM_ATTR_SIZE); if (!is_valid_cattr_blob (ctx, data [MONO_CUSTOM_ATTR_VALUE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Value field 0x%08x", i, data [MONO_CUSTOM_ATTR_VALUE])); mtoken = data [MONO_CUSTOM_ATTR_TYPE] >> MONO_CUSTOM_ATTR_TYPE_BITS; switch (data [MONO_CUSTOM_ATTR_TYPE] & MONO_CUSTOM_ATTR_TYPE_MASK) { case MONO_CUSTOM_ATTR_TYPE_METHODDEF: mtoken |= MONO_TOKEN_METHOD_DEF; break; case MONO_CUSTOM_ATTR_TYPE_MEMBERREF: mtoken |= MONO_TOKEN_MEMBER_REF; break; default: ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute constructor row %d Token 0x%08x", i, data [MONO_CUSTOM_ATTR_TYPE])); } ctor = mono_get_method_checked (ctx->image, mtoken, NULL, NULL, &error); if (!ctor) { ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute content row %d Could not load ctor due to %s", i, mono_error_get_message (&error))); mono_error_cleanup (&error); } /*This can't fail since this is checked in is_valid_cattr_blob*/ g_assert (decode_signature_header (ctx, data [MONO_CUSTOM_ATTR_VALUE], &size, &ptr)); if (!is_valid_cattr_content (ctx, ctor, ptr, size)) { char *ctor_name = mono_method_full_name (ctor, TRUE); ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute content row %d Value field 0x%08x ctor: %s", i, data [MONO_CUSTOM_ATTR_VALUE], ctor_name)); g_free (ctor_name); } } } static void verify_field_marshal_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDMARSHAL]; guint32 data [MONO_FIELD_MARSHAL_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_FIELD_MARSHAL_SIZE); if (!is_valid_coded_index (ctx, HAS_FIELD_MARSHAL_DESC, data [MONO_FIELD_MARSHAL_PARENT])) ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d Parent field 0x%08x", i, data [MONO_FIELD_MARSHAL_PARENT])); if (!get_coded_index_token (HAS_FIELD_MARSHAL_DESC, data [MONO_FIELD_MARSHAL_PARENT])) ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d Parent field is null", i)); if (!data [MONO_FIELD_MARSHAL_NATIVE_TYPE]) ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d NativeType field is null", i)); if (!is_valid_blob_object (ctx, data [MONO_FIELD_MARSHAL_NATIVE_TYPE], 1)) ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d invalid NativeType blob 0x%x", i, data [MONO_FIELD_MARSHAL_NATIVE_TYPE])); } } static void verify_field_marshal_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDMARSHAL]; guint32 data [MONO_FIELD_MARSHAL_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_FIELD_MARSHAL_SIZE); if (!is_valid_marshal_spec (ctx, data [MONO_FIELD_MARSHAL_NATIVE_TYPE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldMarshal row %d NativeType field 0x%08x", i, data [MONO_FIELD_MARSHAL_NATIVE_TYPE])); } } static void verify_decl_security_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_DECLSECURITY]; guint32 data [MONO_DECL_SECURITY_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_DECL_SECURITY_SIZE); if (!is_valid_coded_index (ctx, HAS_DECL_SECURITY_DESC, data [MONO_DECL_SECURITY_PARENT])) ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d Parent field 0x%08x", i, data [MONO_DECL_SECURITY_PARENT])); if (!get_coded_index_token (HAS_DECL_SECURITY_DESC, data [MONO_DECL_SECURITY_PARENT])) ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d Parent field is null", i)); if (!data [MONO_DECL_SECURITY_PERMISSIONSET]) ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d PermissionSet field is null", i)); } } static void verify_decl_security_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_DECLSECURITY]; guint32 data [MONO_DECL_SECURITY_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_DECL_SECURITY_SIZE); if (!is_valid_permission_set (ctx, data [MONO_DECL_SECURITY_PERMISSIONSET])) ADD_ERROR (ctx, g_strdup_printf ("Invalid DeclSecurity row %d PermissionSet field 0x%08x", i, data [MONO_DECL_SECURITY_PERMISSIONSET])); } } static void verify_class_layout_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_CLASSLAYOUT]; guint32 data [MONO_CLASS_LAYOUT_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_CLASS_LAYOUT_SIZE); if (!data [MONO_CLASS_LAYOUT_PARENT] || data[MONO_CLASS_LAYOUT_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid ClassLayout row %d Parent field 0x%08x", i, data [MONO_TABLE_TYPEDEF])); switch (data [MONO_CLASS_LAYOUT_PACKING_SIZE]) { case 0: case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: break; default: ADD_ERROR (ctx, g_strdup_printf ("Invalid ClassLayout row %d Packing field %d", i, data [MONO_CLASS_LAYOUT_PACKING_SIZE])); } } } static void verify_field_layout_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDLAYOUT]; guint32 data [MONO_FIELD_LAYOUT_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_FIELD_LAYOUT_SIZE); if (!data [MONO_FIELD_LAYOUT_FIELD] || data[MONO_FIELD_LAYOUT_FIELD] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldLayout row %d Field field 0x%08x", i, data [MONO_FIELD_LAYOUT_FIELD])); } } static void verify_standalonesig_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_STANDALONESIG]; guint32 data [MONO_STAND_ALONE_SIGNATURE_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_STAND_ALONE_SIGNATURE_SIZE); if (data [MONO_STAND_ALONE_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_STAND_ALONE_SIGNATURE], 1)) ADD_ERROR (ctx, g_strdup_printf ("Invalid StandAloneSig row %d invalid signature 0x%x", i, data [MONO_STAND_ALONE_SIGNATURE])); } } static void verify_standalonesig_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_STANDALONESIG]; guint32 data [MONO_STAND_ALONE_SIGNATURE_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_STAND_ALONE_SIGNATURE_SIZE); if (!is_valid_standalonesig_blob (ctx, data [MONO_STAND_ALONE_SIGNATURE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid StandAloneSig row %d Signature field 0x%08x", i, data [MONO_STAND_ALONE_SIGNATURE])); } } static void verify_eventmap_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENTMAP]; guint32 data [MONO_EVENT_MAP_SIZE], eventlist = 0; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_EVENT_MAP_SIZE); if (!data [MONO_EVENT_MAP_PARENT] || data [MONO_EVENT_MAP_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid EventMap row %d Parent field 0x%08x", i, data [MONO_EVENT_MAP_PARENT])); if (!data [MONO_EVENT_MAP_EVENTLIST] || data [MONO_EVENT_MAP_EVENTLIST] <= eventlist) ADD_ERROR (ctx, g_strdup_printf ("Invalid EventMap row %d EventList field %d", i, data [MONO_EVENT_MAP_EVENTLIST])); eventlist = data [MONO_EVENT_MAP_EVENTLIST]; } } #define INVALID_EVENT_FLAGS_BITS ~((1 << 9) | (1 << 10)) static void verify_event_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENT]; guint32 data [MONO_EVENT_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_EVENT_SIZE); if (data [MONO_EVENT_FLAGS] & INVALID_EVENT_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d EventFlags field %08x", i, data [MONO_EVENT_FLAGS])); if (!is_valid_non_empty_string (ctx, data [MONO_EVENT_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d Name field %08x", i, data [MONO_EVENT_NAME])); if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_EVENT_TYPE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d EventType field %08x", i, data [MONO_EVENT_TYPE])); } } static void verify_event_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EVENT]; MonoTableInfo *sema_table = &ctx->image->tables [MONO_TABLE_METHODSEMANTICS]; guint32 data [MONO_EVENT_SIZE], sema_data [MONO_METHOD_SEMA_SIZE], token; gboolean found_add, found_remove; int i, idx; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_EVENT_SIZE); token = make_coded_token (HAS_SEMANTICS_DESC, MONO_TABLE_EVENT, i); idx = search_sorted_table (ctx, MONO_TABLE_METHODSEMANTICS, MONO_METHOD_SEMA_ASSOCIATION, token); if (idx == -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no AddOn or RemoveOn associated methods", i)); //first we move to the first row for this event while (idx > 0) { if (mono_metadata_decode_row_col (sema_table, idx - 1, MONO_METHOD_SEMA_ASSOCIATION) != token) break; --idx; } //now move forward looking for AddOn and RemoveOn rows found_add = found_remove = FALSE; while (idx < sema_table->rows) { mono_metadata_decode_row (sema_table, idx, sema_data, MONO_METHOD_SEMA_SIZE); if (sema_data [MONO_METHOD_SEMA_ASSOCIATION] != token) break; if (sema_data [MONO_METHOD_SEMA_SEMANTICS] & METHOD_SEMANTIC_ADD_ON) found_add = TRUE; if (sema_data [MONO_METHOD_SEMA_SEMANTICS] & METHOD_SEMANTIC_REMOVE_ON) found_remove = TRUE; if (found_add && found_remove) break; ++idx; } if (!found_add) ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no AddOn associated method", i)); if (!found_remove) ADD_ERROR (ctx, g_strdup_printf ("Invalid Event row %d has no RemoveOn associated method", i)); } } static void verify_propertymap_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PROPERTYMAP]; guint32 data [MONO_PROPERTY_MAP_SIZE], propertylist = 0; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_PROPERTY_MAP_SIZE); if (!data [MONO_PROPERTY_MAP_PARENT] || data [MONO_PROPERTY_MAP_PARENT] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid PropertyMap row %d Parent field 0x%08x", i, data [MONO_PROPERTY_MAP_PARENT])); if (!data [MONO_PROPERTY_MAP_PROPERTY_LIST] || data [MONO_PROPERTY_MAP_PROPERTY_LIST] <= propertylist) ADD_ERROR (ctx, g_strdup_printf ("Invalid PropertyMap row %d PropertyList field %d", i, data [MONO_PROPERTY_MAP_PROPERTY_LIST])); propertylist = data [MONO_PROPERTY_MAP_PROPERTY_LIST]; } } #define INVALID_PROPERTY_FLAGS_BITS ~((1 << 9) | (1 << 10) | (1 << 12)) static void verify_property_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_PROPERTY]; guint32 data [MONO_PROPERTY_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_PROPERTY_SIZE); if (data [MONO_PROPERTY_FLAGS] & INVALID_PROPERTY_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d PropertyFlags field %08x", i, data [MONO_PROPERTY_FLAGS])); if (!is_valid_non_empty_string (ctx, data [MONO_PROPERTY_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d Name field %08x", i, data [MONO_PROPERTY_NAME])); if (!is_valid_property_sig_blob (ctx, data [MONO_PROPERTY_TYPE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d Type field %08x", i, data [MONO_PROPERTY_TYPE])); if ((data [MONO_PROPERTY_FLAGS] & PROPERTY_ATTRIBUTE_HAS_DEFAULT) && search_sorted_table (ctx, MONO_TABLE_CONSTANT, MONO_CONSTANT_PARENT, make_coded_token (HAS_CONSTANT_DESC, MONO_TABLE_PROPERTY, i)) == -1) ADD_ERROR (ctx, g_strdup_printf ("Invalid Property row %d has HasDefault but there is no corresponding row in the Constant table", i)); } } static void verify_methodimpl_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODIMPL]; guint32 data [MONO_METHODIMPL_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_METHODIMPL_SIZE); if (!data [MONO_METHODIMPL_CLASS] || data [MONO_METHODIMPL_CLASS] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d Class field %08x", i, data [MONO_TABLE_TYPEDEF])); if (!get_coded_index_token (METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_BODY])) ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodBody field %08x", i, data [MONO_METHODIMPL_BODY])); if (!is_valid_coded_index (ctx, METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_BODY])) ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodBody field %08x", i, data [MONO_METHODIMPL_BODY])); if (!get_coded_index_token (METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_DECLARATION])) ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodDeclaration field %08x", i, data [MONO_METHODIMPL_DECLARATION])); if (!is_valid_coded_index (ctx, METHODDEF_OR_REF_DESC, data [MONO_METHODIMPL_DECLARATION])) ADD_ERROR (ctx, g_strdup_printf ("Invalid MethodImpl row %d MethodDeclaration field %08x", i, data [MONO_METHODIMPL_DECLARATION])); } } static void verify_moduleref_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MODULEREF]; guint32 data [MONO_MODULEREF_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_MODULEREF_SIZE); if (!is_valid_non_empty_string (ctx, data[MONO_MODULEREF_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid ModuleRef row %d name field %08x", i, data [MONO_MODULEREF_NAME])); } } static void verify_typespec_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPESPEC]; guint32 data [MONO_TYPESPEC_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_TYPESPEC_SIZE); if (data [MONO_TYPESPEC_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_TYPESPEC_SIGNATURE], 1)) ADD_ERROR (ctx, g_strdup_printf ("Invalid TypeSpec row %d Signature field %08x", i, data [MONO_TYPESPEC_SIGNATURE])); } } static void verify_typespec_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPESPEC]; guint32 data [MONO_TYPESPEC_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_TYPESPEC_SIZE); ctx->token = (i + 1) | MONO_TOKEN_TYPE_SPEC; if (!is_valid_typespec_blob (ctx, data [MONO_TYPESPEC_SIGNATURE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid TypeSpec row %d Signature field %08x", i, data [MONO_TYPESPEC_SIGNATURE])); } ctx->token = 0; } #define INVALID_IMPLMAP_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 12) | (1 << 13)) static void verify_implmap_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_IMPLMAP]; guint32 data [MONO_IMPLMAP_SIZE], cconv; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_IMPLMAP_SIZE); if (data [MONO_IMPLMAP_FLAGS] & INVALID_IMPLMAP_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Flags field %08x", i, data [MONO_IMPLMAP_FLAGS])); cconv = data [MONO_IMPLMAP_FLAGS] & PINVOKE_ATTRIBUTE_CALL_CONV_MASK; if (cconv == 0 || cconv == 0x0600 || cconv == 0x0700) ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid call conv field %x", i, cconv)); if (!is_valid_coded_index (ctx, MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER])) ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid MemberForward token %x", i, data [MONO_IMPLMAP_MEMBER])); if (get_coded_index_table (MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER]) != MONO_TABLE_METHOD) ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d only methods are supported token %x", i, data [MONO_IMPLMAP_MEMBER])); if (!get_coded_index_token (MEMBER_FORWARDED_DESC, data [MONO_IMPLMAP_MEMBER])) ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d null token", i)); if (!is_valid_non_empty_string (ctx, data [MONO_IMPLMAP_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d ImportName Token %x", i, data [MONO_IMPLMAP_NAME])); if (!data [MONO_IMPLMAP_SCOPE] || data [MONO_IMPLMAP_SCOPE] > ctx->image->tables [MONO_TABLE_MODULEREF].rows) ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid ImportScope token %x", i, data [MONO_IMPLMAP_SCOPE])); } } static void verify_fieldrva_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FIELDRVA]; guint32 data [MONO_FIELD_RVA_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_FIELD_RVA_SIZE); if (!data [MONO_FIELD_RVA_RVA] || mono_cli_rva_image_map (ctx->image, data [MONO_FIELD_RVA_RVA]) == INVALID_ADDRESS) ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldRVA row %d RVA %08x", i, data [MONO_FIELD_RVA_RVA])); if (!data [MONO_FIELD_RVA_FIELD] || data [MONO_FIELD_RVA_FIELD] > ctx->image->tables [MONO_TABLE_FIELD].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid FieldRVA row %d Field %08x", i, data [MONO_FIELD_RVA_FIELD])); } } #define INVALID_ASSEMBLY_FLAGS_BITS ~((1 << 0) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 14) | (1 << 15)) static void verify_assembly_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_ASSEMBLY]; guint32 data [MONO_ASSEMBLY_SIZE], hash; int i; if (table->rows > 1) ADD_ERROR (ctx, g_strdup_printf ("Assembly table can have zero or one rows, but now %d", table->rows)); for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_ASSEMBLY_SIZE); hash = data [MONO_ASSEMBLY_HASH_ALG]; if (!(hash == 0 || hash == 0x8003 || hash == 0x8004)) ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid HashAlgId %x", i, hash)); if (data [MONO_ASSEMBLY_FLAGS] & INVALID_ASSEMBLY_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Flags %08x", i, data [MONO_ASSEMBLY_FLAGS])); if (data [MONO_ASSEMBLY_PUBLIC_KEY] && !is_valid_blob_object (ctx, data [MONO_ASSEMBLY_PUBLIC_KEY], 1)) ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid PublicKey %08x", i, data [MONO_ASSEMBLY_FLAGS])); if (!is_valid_non_empty_string (ctx, data [MONO_ASSEMBLY_NAME])) ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Name %08x", i, data [MONO_ASSEMBLY_NAME])); if (data [MONO_ASSEMBLY_CULTURE] && !is_valid_string (ctx, data [MONO_ASSEMBLY_CULTURE])) ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Culture %08x", i, data [MONO_ASSEMBLY_CULTURE])); } } #define INVALID_ASSEMBLYREF_FLAGS_BITS ~((1 << 0) | (1 << 8) | (1 << 14) | (1 << 15)) static void verify_assemblyref_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_ASSEMBLYREF]; guint32 data [MONO_ASSEMBLYREF_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_ASSEMBLYREF_SIZE); if (data [MONO_ASSEMBLYREF_FLAGS] & INVALID_ASSEMBLYREF_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid Flags %08x", i, data [MONO_ASSEMBLYREF_FLAGS])); if (data [MONO_ASSEMBLYREF_PUBLIC_KEY] && !is_valid_blob_object (ctx, data [MONO_ASSEMBLYREF_PUBLIC_KEY], 1)) ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid PublicKeyOrToken %08x", i, data [MONO_ASSEMBLYREF_PUBLIC_KEY])); if (!is_valid_non_empty_string (ctx, data [MONO_ASSEMBLYREF_NAME])) ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid Name %08x", i, data [MONO_ASSEMBLYREF_NAME])); if (data [MONO_ASSEMBLYREF_CULTURE] && !is_valid_string (ctx, data [MONO_ASSEMBLYREF_CULTURE])) ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid Culture %08x", i, data [MONO_ASSEMBLYREF_CULTURE])); if (data [MONO_ASSEMBLYREF_HASH_VALUE] && !is_valid_blob_object (ctx, data [MONO_ASSEMBLYREF_HASH_VALUE], 1)) ADD_ERROR (ctx, g_strdup_printf ("AssemblyRef table row %d has invalid HashValue %08x", i, data [MONO_ASSEMBLYREF_HASH_VALUE])); } } #define INVALID_FILE_FLAGS_BITS ~(1) static void verify_file_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_FILE]; guint32 data [MONO_FILE_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_FILE_SIZE); if (data [MONO_FILE_FLAGS] & INVALID_FILE_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("File table row %d has invalid Flags %08x", i, data [MONO_FILE_FLAGS])); if (!is_valid_non_empty_string (ctx, data [MONO_FILE_NAME])) ADD_ERROR (ctx, g_strdup_printf ("File table row %d has invalid Name %08x", i, data [MONO_FILE_NAME])); if (!data [MONO_FILE_HASH_VALUE] || !is_valid_blob_object (ctx, data [MONO_FILE_HASH_VALUE], 1)) ADD_ERROR (ctx, g_strdup_printf ("File table row %d has invalid HashValue %08x", i, data [MONO_FILE_HASH_VALUE])); } } #define INVALID_EXPORTED_TYPE_FLAGS_BITS (INVALID_TYPEDEF_FLAG_BITS & ~TYPE_ATTRIBUTE_FORWARDER) static void verify_exportedtype_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_EXPORTEDTYPE]; guint32 data [MONO_EXP_TYPE_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_EXP_TYPE_SIZE); if (data [MONO_EXP_TYPE_FLAGS] & INVALID_EXPORTED_TYPE_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has invalid Flags %08x", i, data [MONO_EXP_TYPE_FLAGS])); if (!is_valid_non_empty_string (ctx, data [MONO_EXP_TYPE_NAME])) ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has invalid TypeName %08x", i, data [MONO_FILE_NAME])); if (data [MONO_EXP_TYPE_NAMESPACE] && !is_valid_string (ctx, data [MONO_EXP_TYPE_NAMESPACE])) ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has invalid TypeNamespace %08x", i, data [MONO_EXP_TYPE_NAMESPACE])); if (!is_valid_coded_index (ctx, IMPLEMENTATION_DESC, data [MONO_EXP_TYPE_IMPLEMENTATION])) ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has invalid Implementation token %08x", i, data [MONO_EXP_TYPE_IMPLEMENTATION])); if (!get_coded_index_token (IMPLEMENTATION_DESC, data [MONO_EXP_TYPE_IMPLEMENTATION])) ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has null Implementation token", i)); /*nested type can't have a namespace*/ if (get_coded_index_table (IMPLEMENTATION_DESC, data [MONO_EXP_TYPE_IMPLEMENTATION]) == MONO_TABLE_EXPORTEDTYPE && data [MONO_EXP_TYPE_NAMESPACE]) ADD_ERROR (ctx, g_strdup_printf ("ExportedType table row %d has denotes a nested type but has a non null TypeNamespace", i)); } } #define INVALID_MANIFEST_RESOURCE_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2)) static void verify_manifest_resource_table (VerifyContext *ctx) { MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)ctx->image->image_info; MonoCLIHeader *ch = &iinfo->cli_cli_header; MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MANIFESTRESOURCE]; guint32 data [MONO_MANIFEST_SIZE], impl_table, token, resources_size; int i; resources_size = ch->ch_resources.size; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_MANIFEST_SIZE); if (data [MONO_MANIFEST_FLAGS] & INVALID_MANIFEST_RESOURCE_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Flags %08x", i, data [MONO_MANIFEST_FLAGS])); if (data [MONO_MANIFEST_FLAGS] != 1 && data [MONO_MANIFEST_FLAGS] != 2) ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Flags VisibilityMask %08x", i, data [MONO_MANIFEST_FLAGS])); if (!is_valid_non_empty_string (ctx, data [MONO_MANIFEST_NAME])) ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Name %08x", i, data [MONO_MANIFEST_NAME])); if (!is_valid_coded_index (ctx, IMPLEMENTATION_DESC, data [MONO_MANIFEST_IMPLEMENTATION])) ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Implementation token %08x", i, data [MONO_MANIFEST_IMPLEMENTATION])); impl_table = get_coded_index_table (IMPLEMENTATION_DESC, data [MONO_MANIFEST_IMPLEMENTATION]); token = get_coded_index_token (IMPLEMENTATION_DESC, data [MONO_MANIFEST_IMPLEMENTATION]); if (impl_table == MONO_TABLE_EXPORTEDTYPE) ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d has invalid Implementation token table %08x", i, get_coded_index_table (IMPLEMENTATION_DESC, data [MONO_MANIFEST_IMPLEMENTATION]))); if (impl_table == MONO_TABLE_FILE && token && data [MONO_MANIFEST_OFFSET]) ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d points to a file but has non-zero offset", i)); if (!token && data [MONO_MANIFEST_OFFSET] >= resources_size) ADD_ERROR (ctx, g_strdup_printf ("ManifestResource table row %d invalid Offset field %08x ", i, data [MONO_MANIFEST_OFFSET])); } } static void verify_nested_class_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_NESTEDCLASS]; guint32 data [MONO_NESTED_CLASS_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_NESTED_CLASS_SIZE); if (!data [MONO_NESTED_CLASS_NESTED] || data [MONO_NESTED_CLASS_NESTED] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows) ADD_ERROR (ctx, g_strdup_printf ("NestedClass table row %d has invalid NestedClass token %08x", i, data [MONO_NESTED_CLASS_NESTED])); if (!data [MONO_NESTED_CLASS_ENCLOSING] || data [MONO_NESTED_CLASS_ENCLOSING] > ctx->image->tables [MONO_TABLE_TYPEDEF].rows) ADD_ERROR (ctx, g_strdup_printf ("NestedClass table row %d has invalid EnclosingClass token %08x", i, data [MONO_NESTED_CLASS_ENCLOSING])); if (data [MONO_NESTED_CLASS_ENCLOSING] == data [MONO_NESTED_CLASS_NESTED]) ADD_ERROR (ctx, g_strdup_printf ("NestedClass table row %d has same token for NestedClass and EnclosingClass %08x", i, data [MONO_NESTED_CLASS_ENCLOSING])); } } #define INVALID_GENERIC_PARAM_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4)) static void verify_generic_param_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_GENERICPARAM]; guint32 data [MONO_GENERICPARAM_SIZE], token, last_token = 0; int i, param_number = 0; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_GENERICPARAM_SIZE); if (data [MONO_GENERICPARAM_FLAGS] & INVALID_GENERIC_PARAM_FLAGS_BITS) ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has invalid Flags token %08x", i, data [MONO_GENERICPARAM_FLAGS])); if ((data [MONO_GENERICPARAM_FLAGS] & MONO_GEN_PARAM_VARIANCE_MASK) == 0x3) ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has invalid VarianceMask 0x3", i)); if (!is_valid_non_empty_string (ctx, data [MONO_GENERICPARAM_NAME])) ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has invalid Name token %08x", i, data [MONO_GENERICPARAM_NAME])); token = data [MONO_GENERICPARAM_OWNER]; if (!is_valid_coded_index (ctx, TYPE_OR_METHODDEF_DESC, token)) ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has invalid Owner token %08x", i, token)); if (!get_coded_index_token (TYPE_OR_METHODDEF_DESC, token)) ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d has null Owner token", i)); if (token != last_token) { param_number = 0; last_token = token; } if (data [MONO_GENERICPARAM_NUMBER] != param_number) ADD_ERROR (ctx, g_strdup_printf ("GenericParam table row %d Number is out of order %d expected %d", i, data [MONO_GENERICPARAM_NUMBER], param_number)); ++param_number; } } static void verify_method_spec_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODSPEC]; guint32 data [MONO_METHODSPEC_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_METHODSPEC_SIZE); if (!is_valid_coded_index (ctx, METHODDEF_OR_REF_DESC, data [MONO_METHODSPEC_METHOD])) ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has invalid Method token %08x", i, data [MONO_METHODSPEC_METHOD])); if (!get_coded_index_token (METHODDEF_OR_REF_DESC, data [MONO_METHODSPEC_METHOD])) ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has null Method token", i)); if (data [MONO_METHODSPEC_SIGNATURE] && !is_valid_blob_object (ctx, data [MONO_METHODSPEC_SIGNATURE], 1)) ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has invalid signature token %08x", i, data [MONO_METHODSPEC_SIGNATURE])); } } static void verify_method_spec_table_full (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODSPEC]; guint32 data [MONO_METHODSPEC_SIZE]; int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_METHODSPEC_SIZE); if (!is_valid_methodspec_blob (ctx, data [MONO_METHODSPEC_SIGNATURE])) ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has invalid Instantiation token %08x", i, data [MONO_METHODSPEC_SIGNATURE])); } } static void verify_generic_param_constraint_table (VerifyContext *ctx) { MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_GENERICPARAMCONSTRAINT]; guint32 data [MONO_GENPARCONSTRAINT_SIZE]; int i; guint32 last_owner = 0, last_constraint = 0; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_GENPARCONSTRAINT_SIZE); if (!data [MONO_GENPARCONSTRAINT_GENERICPAR] || data [MONO_GENPARCONSTRAINT_GENERICPAR] > ctx->image->tables [MONO_TABLE_GENERICPARAM].rows) ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d has invalid Owner token %08x", i, data [MONO_GENPARCONSTRAINT_GENERICPAR])); if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, data [MONO_GENPARCONSTRAINT_CONSTRAINT])) ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d has invalid Constraint token %08x", i, data [MONO_GENPARCONSTRAINT_CONSTRAINT])); if (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_GENPARCONSTRAINT_CONSTRAINT])) ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d has null Constraint token", i)); if (last_owner > data [MONO_GENPARCONSTRAINT_GENERICPAR]) ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d is not properly sorted. Previous value of the owner column is 0x%08x current value is 0x%08x", i, last_owner, data [MONO_GENPARCONSTRAINT_GENERICPAR])); if (last_owner == data [MONO_GENPARCONSTRAINT_GENERICPAR]) { if (last_constraint == data [MONO_GENPARCONSTRAINT_CONSTRAINT]) ADD_ERROR (ctx, g_strdup_printf ("GenericParamConstraint table row %d has duplicate constraint 0x%08x", i, last_constraint)); } else { last_owner = data [MONO_GENPARCONSTRAINT_GENERICPAR]; } last_constraint = data [MONO_GENPARCONSTRAINT_CONSTRAINT]; } } typedef struct { const char *name; const char *name_space; guint32 resolution_scope; } TypeDefUniqueId; static guint typedef_hash (gconstpointer _key) { const TypeDefUniqueId *key = (const TypeDefUniqueId *)_key; return g_str_hash (key->name) ^ g_str_hash (key->name_space) ^ key->resolution_scope; /*XXX better salt the int key*/ } static gboolean typedef_equals (gconstpointer _a, gconstpointer _b) { const TypeDefUniqueId *a = (const TypeDefUniqueId *)_a; const TypeDefUniqueId *b = (const TypeDefUniqueId *)_b; return !strcmp (a->name, b->name) && !strcmp (a->name_space, b->name_space) && a->resolution_scope == b->resolution_scope; } static void verify_typedef_table_global_constraints (VerifyContext *ctx) { int i; guint32 data [MONO_TYPEDEF_SIZE]; guint32 nested_data [MONO_NESTED_CLASS_SIZE]; MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF]; MonoTableInfo *nested_table = &ctx->image->tables [MONO_TABLE_NESTEDCLASS]; GHashTable *unique_types = g_hash_table_new_full (&typedef_hash, &typedef_equals, g_free, NULL); for (i = 0; i < table->rows; ++i) { guint visibility; TypeDefUniqueId *type = g_new (TypeDefUniqueId, 1); mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE); type->name = mono_metadata_string_heap (ctx->image, data [MONO_TYPEDEF_NAME]); type->name_space = mono_metadata_string_heap (ctx->image, data [MONO_TYPEDEF_NAMESPACE]); type->resolution_scope = 0; visibility = data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK; if (visibility >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visibility <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) { int res = search_sorted_table (ctx, MONO_TABLE_NESTEDCLASS, MONO_NESTED_CLASS_NESTED, i + 1); g_assert (res >= 0); mono_metadata_decode_row (nested_table, res, nested_data, MONO_NESTED_CLASS_SIZE); type->resolution_scope = nested_data [MONO_NESTED_CLASS_ENCLOSING]; } if (g_hash_table_lookup (unique_types, type)) { ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("TypeDef table row %d has duplicate for tuple (%s,%s,%x)", i, type->name, type->name_space, type->resolution_scope)); g_hash_table_destroy (unique_types); g_free (type); return; } g_hash_table_insert (unique_types, type, GUINT_TO_POINTER (1)); } g_hash_table_destroy (unique_types); } static void verify_typeref_table_global_constraints (VerifyContext *ctx) { int i; guint32 data [MONO_TYPEREF_SIZE]; MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF]; GHashTable *unique_types = g_hash_table_new_full (&typedef_hash, &typedef_equals, g_free, NULL); for (i = 0; i < table->rows; ++i) { TypeDefUniqueId *type = g_new (TypeDefUniqueId, 1); mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE); type->resolution_scope = data [MONO_TYPEREF_SCOPE]; type->name = mono_metadata_string_heap (ctx->image, data [MONO_TYPEREF_NAME]); type->name_space = mono_metadata_string_heap (ctx->image, data [MONO_TYPEREF_NAMESPACE]); if (g_hash_table_lookup (unique_types, type)) { ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("TypeRef table row %d has duplicate for tuple (%s,%s,%x)", i, type->name, type->name_space, type->resolution_scope)); g_hash_table_destroy (unique_types); g_free (type); return; } g_hash_table_insert (unique_types, type, GUINT_TO_POINTER (1)); } g_hash_table_destroy (unique_types); } typedef struct { guint32 klass; guint32 method_declaration; } MethodImplUniqueId; static guint methodimpl_hash (gconstpointer _key) { const MethodImplUniqueId *key = (const MethodImplUniqueId *)_key; return key->klass ^ key->method_declaration; } static gboolean methodimpl_equals (gconstpointer _a, gconstpointer _b) { const MethodImplUniqueId *a = (const MethodImplUniqueId *)_a; const MethodImplUniqueId *b = (const MethodImplUniqueId *)_b; return a->klass == b->klass && a->method_declaration == b->method_declaration; } static void verify_methodimpl_table_global_constraints (VerifyContext *ctx) { int i; guint32 data [MONO_METHODIMPL_SIZE]; MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHODIMPL]; GHashTable *unique_impls = g_hash_table_new_full (&methodimpl_hash, &methodimpl_equals, g_free, NULL); for (i = 0; i < table->rows; ++i) { MethodImplUniqueId *impl = g_new (MethodImplUniqueId, 1); mono_metadata_decode_row (table, i, data, MONO_METHODIMPL_SIZE); impl->klass = data [MONO_METHODIMPL_CLASS]; impl->method_declaration = data [MONO_METHODIMPL_DECLARATION]; if (g_hash_table_lookup (unique_impls, impl)) { ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("MethodImpl table row %d has duplicate for tuple (0x%x, 0x%x)", impl->klass, impl->method_declaration)); g_hash_table_destroy (unique_impls); g_free (impl); return; } g_hash_table_insert (unique_impls, impl, GUINT_TO_POINTER (1)); } g_hash_table_destroy (unique_impls); } static void verify_tables_data_global_constraints (VerifyContext *ctx) { verify_typedef_table_global_constraints (ctx); } static void verify_tables_data_global_constraints_full (VerifyContext *ctx) { verify_typeref_table (ctx); verify_typeref_table_global_constraints (ctx); verify_methodimpl_table_global_constraints (ctx); } static void verify_tables_data (VerifyContext *ctx) { OffsetAndSize tables_area = get_metadata_stream (ctx, &ctx->image->heap_tables); guint32 size = 0, tables_offset; int i; for (i = 0; i < 0x2D; ++i) { MonoTableInfo *table = &ctx->image->tables [i]; guint32 tmp_size; tmp_size = size + (guint32)table->row_size * (guint32)table->rows; if (tmp_size < size) { size = 0; break; } size = tmp_size; } if (size == 0) ADD_ERROR (ctx, g_strdup_printf ("table space is either empty or overflowed")); tables_offset = ctx->image->tables_base - ctx->data; if (!bounds_check_offset (&tables_area, tables_offset, size)) 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))); verify_module_table (ctx); CHECK_ERROR (); /*Obfuscators love to place broken stuff in the typeref table verify_typeref_table (ctx); CHECK_ERROR ();*/ verify_typedef_table (ctx); CHECK_ERROR (); verify_field_table (ctx); CHECK_ERROR (); verify_method_table (ctx); CHECK_ERROR (); verify_param_table (ctx); CHECK_ERROR (); verify_interfaceimpl_table (ctx); CHECK_ERROR (); verify_memberref_table (ctx); CHECK_ERROR (); verify_constant_table (ctx); CHECK_ERROR (); verify_cattr_table (ctx); CHECK_ERROR (); verify_field_marshal_table (ctx); CHECK_ERROR (); verify_decl_security_table (ctx); CHECK_ERROR (); verify_class_layout_table (ctx); CHECK_ERROR (); verify_field_layout_table (ctx); CHECK_ERROR (); verify_standalonesig_table (ctx); CHECK_ERROR (); verify_eventmap_table (ctx); CHECK_ERROR (); verify_event_table (ctx); CHECK_ERROR (); verify_propertymap_table (ctx); CHECK_ERROR (); verify_property_table (ctx); CHECK_ERROR (); verify_methodimpl_table (ctx); CHECK_ERROR (); verify_moduleref_table (ctx); CHECK_ERROR (); verify_typespec_table (ctx); CHECK_ERROR (); verify_implmap_table (ctx); CHECK_ERROR (); verify_fieldrva_table (ctx); CHECK_ERROR (); verify_assembly_table (ctx); CHECK_ERROR (); verify_assemblyref_table (ctx); CHECK_ERROR (); verify_file_table (ctx); CHECK_ERROR (); verify_exportedtype_table (ctx); CHECK_ERROR (); verify_manifest_resource_table (ctx); CHECK_ERROR (); verify_nested_class_table (ctx); CHECK_ERROR (); verify_generic_param_table (ctx); CHECK_ERROR (); verify_method_spec_table (ctx); CHECK_ERROR (); verify_generic_param_constraint_table (ctx); CHECK_ERROR (); verify_tables_data_global_constraints (ctx); } static void init_verify_context (VerifyContext *ctx, MonoImage *image, gboolean report_error) { memset (ctx, 0, sizeof (VerifyContext)); ctx->image = image; ctx->report_error = report_error; ctx->report_warning = FALSE; //export this setting in the API ctx->valid = 1; ctx->size = image->raw_data_len; ctx->data = image->raw_data; } static gboolean cleanup_context (VerifyContext *ctx, GSList **error_list) { g_free (ctx->sections); if (error_list) *error_list = ctx->errors; else mono_free_verify_list (ctx->errors); return ctx->valid; } static gboolean cleanup_context_checked (VerifyContext *ctx, MonoError *error) { g_free (ctx->sections); if (ctx->errors) { MonoVerifyInfo *info = (MonoVerifyInfo *)ctx->errors->data; mono_error_set_bad_image (error, ctx->image, "%s", info->message); mono_free_verify_list (ctx->errors); } return ctx->valid; } gboolean mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_PE; verify_msdos_header (&ctx); CHECK_STATE(); verify_pe_header (&ctx); CHECK_STATE(); verify_pe_optional_header (&ctx); CHECK_STATE(); load_section_table (&ctx); CHECK_STATE(); load_data_directories (&ctx); CHECK_STATE(); verify_import_table (&ctx); CHECK_STATE(); /*No need to check the IAT directory entry, it's content is indirectly verified by verify_import_table*/ verify_resources_table (&ctx); cleanup: return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_cli_data (MonoImage *image, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_CLI; verify_cli_header (&ctx); CHECK_STATE(); verify_metadata_header (&ctx); CHECK_STATE(); verify_tables_schema (&ctx); cleanup: return cleanup_context (&ctx, error_list); } /* * Verifies basic table constraints such as global table invariants (sorting, field monotonicity, etc). * Other verification checks are meant to be done lazily by the runtime. Those include: * blob items (signatures, method headers, custom attributes, etc) * type semantics related * vtable related * stuff that should not block other pieces from running such as bad types/methods/fields/etc. * * The whole idea is that if this succeed the runtime is free to play around safely but any complex * operation still need more checking. */ gboolean mono_verifier_verify_table_data (MonoImage *image, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; verify_tables_data (&ctx); return cleanup_context (&ctx, error_list); } /* * Verifies all other constraints. */ gboolean mono_verifier_verify_full_table_data (MonoImage *image, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; verify_typedef_table_full (&ctx); CHECK_STATE (); verify_field_table_full (&ctx); CHECK_STATE (); verify_method_table_full (&ctx); CHECK_STATE (); verify_memberref_table_full (&ctx); CHECK_STATE (); verify_cattr_table_full (&ctx); CHECK_STATE (); verify_field_marshal_table_full (&ctx); CHECK_STATE (); verify_decl_security_table_full (&ctx); CHECK_STATE (); verify_standalonesig_table_full (&ctx); CHECK_STATE (); verify_event_table_full (&ctx); CHECK_STATE (); verify_typespec_table_full (&ctx); CHECK_STATE (); verify_method_spec_table_full (&ctx); CHECK_STATE (); verify_tables_data_global_constraints_full (&ctx); cleanup: return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_field_signature (MonoImage *image, guint32 offset, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; is_valid_field_signature (&ctx, offset); return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_method_header (MonoImage *image, guint32 offset, GSList **error_list) { VerifyContext ctx; guint32 locals_token; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; is_valid_method_header (&ctx, offset, &locals_token); if (locals_token) { guint32 sig_offset = mono_metadata_decode_row_col (&image->tables [MONO_TABLE_STANDALONESIG], locals_token - 1, MONO_STAND_ALONE_SIGNATURE); is_valid_standalonesig_blob (&ctx, sig_offset); } return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, MonoError *error) { VerifyContext ctx; error_init (error); if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, TRUE); ctx.stage = STAGE_TABLES; is_valid_method_signature (&ctx, offset); /*XXX This returns a bad image exception, it might be the case that the right exception is method load.*/ return cleanup_context_checked (&ctx, error); } gboolean mono_verifier_verify_memberref_method_signature (MonoImage *image, guint32 offset, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; is_valid_memberref_method_signature (&ctx, offset); return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_memberref_field_signature (MonoImage *image, guint32 offset, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; is_valid_field_signature (&ctx, offset); return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_standalone_signature (MonoImage *image, guint32 offset, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; is_valid_standalonesig_blob (&ctx, offset); return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, guint32 token, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; ctx.token = token; is_valid_typespec_blob (&ctx, offset); return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_methodspec_signature (MonoImage *image, guint32 offset, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; is_valid_methodspec_blob (&ctx, offset); return cleanup_context (&ctx, error_list); } static void verify_user_string (VerifyContext *ctx, guint32 offset) { OffsetAndSize heap_us = get_metadata_stream (ctx, &ctx->image->heap_us); guint32 entry_size, bytes; if (heap_us.size < offset) ADD_ERROR (ctx, g_strdup ("User string offset beyond heap_us size")); if (!decode_value (ctx->data + offset + heap_us.offset, heap_us.size - heap_us.offset, &entry_size, &bytes)) ADD_ERROR (ctx, g_strdup ("Could not decode user string blob size")); if (CHECK_ADD4_OVERFLOW_UN (entry_size, bytes)) ADD_ERROR (ctx, g_strdup ("User string size overflow")); entry_size += bytes; if (ADD_IS_GREATER_OR_OVF (offset, entry_size, heap_us.size)) ADD_ERROR (ctx, g_strdup ("User string oveflow heap_us")); } gboolean mono_verifier_verify_string_signature (MonoImage *image, guint32 offset, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; verify_user_string (&ctx, offset); return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_cattr_blob (MonoImage *image, guint32 offset, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; is_valid_cattr_blob (&ctx, offset); return cleanup_context (&ctx, error_list); } gboolean mono_verifier_verify_cattr_content (MonoImage *image, MonoMethod *ctor, const guchar *data, guint32 size, GSList **error_list) { VerifyContext ctx; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; init_verify_context (&ctx, image, error_list != NULL); ctx.stage = STAGE_TABLES; is_valid_cattr_content (&ctx, ctor, (const char*)data, size); return cleanup_context (&ctx, error_list); } gboolean mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMethodSignature *signature) { MonoMethodSignature *original_sig; if (!mono_verifier_is_enabled_for_image (image)) return TRUE; original_sig = mono_method_signature (method); if (original_sig->call_convention == MONO_CALL_VARARG) { if (original_sig->hasthis != signature->hasthis) return FALSE; if (original_sig->call_convention != signature->call_convention) return FALSE; if (original_sig->explicit_this != signature->explicit_this) return FALSE; if (original_sig->pinvoke != signature->pinvoke) return FALSE; if (original_sig->sentinelpos != signature->sentinelpos) return FALSE; } else if (!mono_metadata_signature_equal (signature, original_sig)) { return FALSE; } return TRUE; } gboolean mono_verifier_verify_typeref_row (MonoImage *image, guint32 row, MonoError *error) { MonoTableInfo *table = &image->tables [MONO_TABLE_TYPEREF]; guint32 data [MONO_TYPEREF_SIZE]; error_init (error); if (!mono_verifier_is_enabled_for_image (image)) return TRUE; if (row >= table->rows) { mono_error_set_bad_image (error, image, "Invalid typeref row %d - table has %d rows", row, table->rows); return FALSE; } mono_metadata_decode_row (table, row, data, MONO_TYPEREF_SIZE); if (!is_valid_coded_index_with_image (image, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) { mono_error_set_bad_image (error, image, "Invalid typeref row %d coded index 0x%08x", row, data [MONO_TYPEREF_SCOPE]); return FALSE; } if (!get_coded_index_token (RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) { mono_error_set_bad_image (error, image, "The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", row); return FALSE; } if (!data [MONO_TYPEREF_NAME] || !is_valid_string_full_with_image (image, data [MONO_TYPEREF_NAME], FALSE)) { mono_error_set_bad_image (error, image, "Invalid typeref row %d name token 0x%08x", row, data [MONO_TYPEREF_NAME]); return FALSE; } if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_string_full_with_image (image, data [MONO_TYPEREF_NAMESPACE], FALSE)) { mono_error_set_bad_image (error, image, "Invalid typeref row %d namespace token 0x%08x", row, data [MONO_TYPEREF_NAMESPACE]); return FALSE; } return TRUE; } /*Perform additional verification including metadata ones*/ gboolean mono_verifier_verify_methodimpl_row (MonoImage *image, guint32 row, MonoError *error) { MonoMethod *declaration, *body; MonoMethodSignature *body_sig, *decl_sig; MonoTableInfo *table = &image->tables [MONO_TABLE_METHODIMPL]; guint32 data [MONO_METHODIMPL_SIZE]; error_init (error); if (!mono_verifier_is_enabled_for_image (image)) return TRUE; if (row >= table->rows) { mono_error_set_bad_image (error, image, "Invalid methodimpl row %d - table has %d rows", row, table->rows); return FALSE; } mono_metadata_decode_row (table, row, data, MONO_METHODIMPL_SIZE); body = method_from_method_def_or_ref (image, data [MONO_METHODIMPL_BODY], NULL, error); if (!body) return FALSE; declaration = method_from_method_def_or_ref (image, data [MONO_METHODIMPL_DECLARATION], NULL, error); if (!declaration) return FALSE; /* FIXME mono_class_setup_supertypes (class); if (!mono_class_has_parent (class, body->klass)) { mono_error_set_bad_image (error, image, "Invalid methodimpl body doesn't belong to parent for row %x", row); return FALSE; }*/ if (!(body_sig = mono_method_signature_checked (body, error))) { return FALSE; } if (!(decl_sig = mono_method_signature_checked (declaration, error))) { return FALSE; } if (!mono_verifier_is_signature_compatible (decl_sig, body_sig)) { mono_error_set_bad_image (error, image, "Invalid methodimpl body signature not compatible with declaration row %x", row); return FALSE; } return TRUE; } #else gboolean mono_verifier_verify_table_data (MonoImage *image, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_cli_data (MonoImage *image, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_full_table_data (MonoImage *image, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_field_signature (MonoImage *image, guint32 offset, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_method_header (MonoImage *image, guint32 offset, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, MonoError *error) { error_init (error); return TRUE; } gboolean mono_verifier_verify_standalone_signature (MonoImage *image, guint32 offset, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, guint32 token, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_methodspec_signature (MonoImage *image, guint32 offset, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_string_signature (MonoImage *image, guint32 offset, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_cattr_blob (MonoImage *image, guint32 offset, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_cattr_content (MonoImage *image, MonoMethod *ctor, const guchar *data, guint32 size, GSList **error_list) { return TRUE; } gboolean mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMethodSignature *signature) { return TRUE; } gboolean mono_verifier_verify_typeref_row (MonoImage *image, guint32 row, MonoError *error) { error_init (error); return TRUE; } gboolean mono_verifier_verify_methodimpl_row (MonoImage *image, guint32 row, MonoError *error) { error_init (error); return TRUE; } gboolean mono_verifier_verify_memberref_method_signature (MonoImage *image, guint32 offset, GSList **error_list) { return TRUE; } gboolean mono_verifier_verify_memberref_field_signature (MonoImage *image, guint32 offset, GSList **error_list) { return TRUE; } #endif /* DISABLE_VERIFIER */