X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fmetadata-verify.c;h=0ffb428a92d5b64c8396455bea201d06e2f82540;hb=3a5592eabcf055d5da5bafa46bff8fa72bd649c7;hp=2fd4b7a12b0706f6eeab416c10b8ddd5b21ac1a1;hpb=c2543d8597eb4a54d99b64b3fffbaf8899d4a6b3;p=mono.git diff --git a/mono/metadata/metadata-verify.c b/mono/metadata/metadata-verify.c index 2fd4b7a12b0..0ffb428a92d 100644 --- a/mono/metadata/metadata-verify.c +++ b/mono/metadata/metadata-verify.c @@ -21,9 +21,10 @@ #include #include #include +#include #include #include -#include +//#include #include #ifndef DISABLE_VERIFIER @@ -96,7 +97,7 @@ const static unsigned char coded_index_desc[] = { #define HAS_CATTR_DESC (HAS_CONSTANT_DESC + 5) 5, /*bits*/ - 19, /*tables*/ + 20, /*tables*/ MONO_TABLE_METHOD, MONO_TABLE_FIELD, MONO_TABLE_TYPEREF, @@ -116,8 +117,9 @@ const static unsigned char coded_index_desc[] = { MONO_TABLE_FILE, MONO_TABLE_EXPORTEDTYPE, MONO_TABLE_MANIFESTRESOURCE, + MONO_TABLE_GENERICPARAM, -#define HAS_FIELD_MARSHAL_DESC (HAS_CATTR_DESC + 21) +#define HAS_FIELD_MARSHAL_DESC (HAS_CATTR_DESC + 22) 1, /*bits*/ 2, /*tables*/ MONO_TABLE_FIELD, @@ -215,12 +217,12 @@ typedef struct { typedef struct { const char *data; - guint32 size; + guint32 size, token; GSList *errors; int valid; - gboolean is_corlib; MonoImage *image; gboolean report_error; + gboolean report_warning; int stage; DataDirectory data_directories [16]; @@ -239,6 +241,21 @@ typedef struct { (__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 { \ @@ -248,10 +265,40 @@ typedef struct { 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 guint32 pe_signature_offset (VerifyContext *ctx) { @@ -556,34 +603,42 @@ verify_import_table (VerifyContext *ctx) ADD_ERROR (ctx, g_strdup_printf ("Import table size %d is smaller than 40", it.size)); ilt_rva = read32 (ptr); - if (!bounds_check_virtual_address (ctx, ilt_rva, 8)) + 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 (!bounds_check_virtual_address (ctx, name_rva, SIZE_OF_MSCOREE)) + 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 (!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 (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)); - name_rva = translate_rva (ctx, name_rva); - g_assert (name_rva != INVALID_OFFSET); - ptr = ctx->data + name_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 (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 (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)); + } } - verify_hint_name_table (ctx, ilt_rva, "Import Lookup Table"); - CHECK_ERROR (); - verify_hint_name_table (ctx, iat_rva, "Import Address Table"); + 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 @@ -694,7 +749,7 @@ verify_metadata_header (VerifyContext *ctx) { int i; DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX); - guint32 offset; + guint32 offset, section_count; const char *ptr; offset = it.translated_offset; @@ -724,13 +779,14 @@ verify_metadata_header (VerifyContext *ctx) ptr = ctx->data + offset; //move to streams header - if (read16 (ptr + 2) < 3) + section_count = read16 (ptr + 2); + if (section_count < 3) ADD_ERROR (ctx, g_strdup_printf ("Metadata root section must have at least 3 streams (#~, #GUID and #Blob")); ptr += 4; offset += 4; - for (i = 0; i < 5; ++i) { + for (i = 0; i < section_count; ++i) { guint32 stream_off, stream_size; int string_size, stream_idx; @@ -766,8 +822,12 @@ verify_metadata_header (VerifyContext *ctx) stream_idx = GUID_STREAM; else if (!strncmp ("#~", ptr, 3)) stream_idx = TILDE_STREAM; - else - ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr)); + 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)); @@ -821,7 +881,7 @@ verify_tables_schema (VerifyContext *ctx) 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 verifies doesn't support MS specific table %x", i)); + 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; @@ -1013,85 +1073,22 @@ string_cmp (VerifyContext *ctx, const char *str, guint offset) } static gboolean -typedef_is_system_object (VerifyContext *ctx, guint32 *data) -{ - return ctx->is_corlib && !string_cmp (ctx, "System", data [MONO_TYPEDEF_NAME]) && !string_cmp (ctx, "Object", data [MONO_TYPEDEF_NAMESPACE]); -} - -static gboolean -is_valid_field_signature (VerifyContext *ctx, guint32 offset) -{ - OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); - //TODO do proper verification - return blob.size >= 2 && blob.size - 2 >= offset; -} - -static gboolean -is_valid_method_signature (VerifyContext *ctx, guint32 offset) -{ - OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); - //TODO do proper verification - return blob.size >= 2 && blob.size - 2 >= offset; -} - -static gboolean -is_valid_method_or_field_signature (VerifyContext *ctx, guint32 offset) -{ - OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); - //TODO do proper verification - return blob.size >= 2 && blob.size - 2 >= offset; -} - -static gboolean -is_vald_cattr_blob (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_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) +mono_verifier_is_corlib (MonoImage *image) { - OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); - //TODO do proper verification - return blob.size >= 1 && blob.size - 1 >= offset; -} + gboolean trusted_location = (mono_security_get_mode () != MONO_SECURITY_MODE_CORE_CLR) ? + TRUE : mono_security_core_clr_is_platform_image (image); -static gboolean -is_valid_property_sig_blob (VerifyContext *ctx, guint32 offset) -{ - OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); - //TODO do proper verification - return offset > 0 && blob.size >= 1 && blob.size - 1 >= offset; + return trusted_location && image->module_name && !strcmp ("mscorlib.dll", image->module_name); } static gboolean -is_valid_typespec_blob (VerifyContext *ctx, guint32 offset) +typedef_is_system_object (VerifyContext *ctx, guint32 *data) { - OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); - //TODO do proper verification - return offset > 0 && blob.size >= 1 && blob.size - 1 >= offset; + 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, guint32 available, guint32 *value, guint32 *size) +decode_value (const char *_ptr, unsigned available, unsigned *value, unsigned *size) { unsigned char b; const unsigned char *ptr = (const unsigned char *)_ptr; @@ -1124,201 +1121,960 @@ decode_value (const char *_ptr, guint32 available, guint32 *value, guint32 *size } static gboolean -is_valid_blob_object (VerifyContext *ctx, guint32 offset) +decode_signature_header (VerifyContext *ctx, guint32 offset, int *size, const char **first_byte) { - OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); - guint32 entry_size, bytes; + MonoStreamHeader blob = ctx->image->heap_blob; + guint32 value, enc_size; - if (blob.size < offset) { - printf ("1\n"); + if (offset >= blob.size) return FALSE; - } - if (!decode_value (ctx->data + offset + blob.offset, blob.size - blob.offset, &entry_size, &bytes)) + if (!decode_value (blob.data + offset, blob.size - offset, &value, &enc_size)) + return FALSE; + + if (offset + enc_size + value < offset) return FALSE; - if (offset + entry_size < offset) + if (offset + enc_size + value > blob.size) return FALSE; - return blob.size >= offset + entry_size; + *size = value; + *first_byte = blob.data + offset + enc_size; + return TRUE; } static gboolean -is_valid_constant (VerifyContext *ctx, guint32 type, guint32 offset) +safe_read (const char **_ptr, const char *limit, unsigned *dest, int size) { - OffsetAndSize blob = get_metadata_stream (ctx, &ctx->image->heap_blob); - guint32 size, entry_size, bytes; - - if (blob.size < offset) { - printf ("1\n"); - return FALSE; - } - - - if (!decode_value (ctx->data + offset + blob.offset, blob.size - blob.offset, &entry_size, &bytes)) + const char *ptr = *_ptr; + if (ptr + size > limit) return FALSE; - - if (type == MONO_TYPE_STRING) { - //String is encoded as: compressed_int:len len *chars - - offset += bytes; - if (offset > offset + entry_size * 2) //overflow - return FALSE; - offset += offset + entry_size * 2; - return offset <= blob.size; - } - - 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; + switch (size) { + case 1: + *dest = *((guint8*)ptr); + ++ptr; break; - case MONO_TYPE_I4: - case MONO_TYPE_U4: - case MONO_TYPE_R4: - case MONO_TYPE_CLASS: - size = 4; + case 2: + *dest = read16 (ptr); + ptr += 2; break; - - case MONO_TYPE_I8: - case MONO_TYPE_U8: - case MONO_TYPE_R8: - size = 8; + case 4: + *dest = read32 (ptr); + ptr += 4; break; - default: - g_assert_not_reached (); } + *_ptr = ptr; + return TRUE; +} - if (size != entry_size) - return FALSE; - offset += bytes; +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; +} - if(offset > offset + size) //overflow - return FALSE; +#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) - if (offset + size > blob.size) - return FALSE; +static gboolean +parse_type (VerifyContext *ctx, const char **_ptr, const char *end); - if (type == MONO_TYPE_CLASS && read32 (ctx->data + offset)) - return FALSE; - return TRUE; -} +static gboolean +parse_method_signature (VerifyContext *ctx, const char **_ptr, const char *end, gboolean allow_sentinel, gboolean allow_unmanaged); static gboolean -is_valid_method_header (VerifyContext *ctx, guint32 rva) +parse_custom_mods (VerifyContext *ctx, const char **_ptr, const char *end) { - //TODO do proper method header validation - return mono_cli_rva_image_map (ctx->image, rva) != INVALID_ADDRESS; + 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)) + FAIL (ctx, g_strdup_printf ("CustomMod: invalid TypeDefOrRef token %x", token)); + } + + *_ptr = ptr; + return TRUE; } -static void -verify_module_table (VerifyContext *ctx) +static gboolean +parse_array_shape (VerifyContext *ctx, const char **_ptr, const char *end) { - MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_MODULE]; - guint32 data [MONO_MODULE_SIZE]; + const char *ptr = *_ptr; + unsigned val = 0; + unsigned size, num, i; - if (table->rows != 1) - ADD_ERROR (ctx, g_strdup_printf ("Module table must have exactly one row, but have %d", table->rows)); + if (!safe_read8 (val, ptr, end)) + FAIL (ctx, g_strdup ("ArrayShape: Not enough room for Rank")); - mono_metadata_decode_row (table, 0, data, MONO_MODULE_SIZE); + if (val == 0) + FAIL (ctx, g_strdup ("ArrayShape: Invalid shape with zero Rank")); - 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 (!safe_read_cint (size, ptr, end)) + FAIL (ctx, g_strdup ("ArrayShape: Not enough room for NumSizes")); - 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])); + 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 (data [MONO_MODULE_ENC] != 0) - ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero Enc field %x", data [MONO_MODULE_ENC])); + if (!safe_read_cint (size, ptr, end)) + FAIL (ctx, g_strdup ("ArrayShape: Not enough room for NumLoBounds")); - if (data [MONO_MODULE_ENCBASE] != 0) - ADD_ERROR (ctx, g_strdup_printf ("Module has a non zero EncBase field %x", data [MONO_MODULE_ENCBASE])); + 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 void -verify_typeref_table (VerifyContext *ctx) +static gboolean +parse_generic_inst (VerifyContext *ctx, const char **_ptr, const char *end) { - MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF]; - guint32 data [MONO_TYPEREF_SIZE]; - int i; + const char *ptr = *_ptr; + unsigned type; + unsigned count, token, i; - for (i = 0; i < table->rows; ++i) { - mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE); - if (!is_valid_coded_index (ctx, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) - ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d coded index 0x%08x", i, data [MONO_TYPEREF_SCOPE])); - - if (!get_coded_index_token (RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) - ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", i)); + if (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("GenericInst: Not enough room for kind")); - if (!data [MONO_TYPEREF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAME])) - ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d name token 0x%08x", i, data [MONO_TYPEREF_NAME])); + if (type != MONO_TYPE_CLASS && type != MONO_TYPE_VALUETYPE) + FAIL (ctx, g_strdup_printf ("GenericInst: Invalid GenericInst kind %x\n", type)); - if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE])) - ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d namespace token 0x%08x", i, data [MONO_TYPEREF_NAMESPACE])); + 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)) + 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)); } -} -/*bits 9,11,14,15,19,21,24-31 */ -#define INVALID_TYPEDEF_FLAG_BITS ((1 << 9) | (1 << 11) | (1 << 14) | (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; - int i; + if (!safe_read_cint (count, ptr, end)) + FAIL (ctx, g_strdup ("GenericInst: Not enough room for argument count")); - if (table->rows == 0) - ADD_ERROR (ctx, g_strdup_printf ("Typedef table must have exactly at least one row")); + if (count == 0) + FAIL (ctx, g_strdup ("GenericInst: Zero arguments generic instance")); - 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", i, data [MONO_TYPEDEF_FLAGS])); + for (i = 0; i < count; ++i) { + if (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup_printf ("GenericInst: invalid generic argument %d", i + 1)); + } + *_ptr = ptr; + return TRUE; +} - 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)); +static gboolean +parse_type (VerifyContext *ctx, const char **_ptr, const char *end) +{ + const char *ptr = *_ptr; + unsigned type; + unsigned token = 0; - 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 (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("Type: Not enough room for the type")); - 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 (!((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)); - 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])); + 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 (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 (!safe_read8 (type, ptr, end)) + FAIL (ctx, g_strdup ("Type: Not enough room to parse the pointer type")); - if (i == 0) { - 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")); - } else { - if (typedef_is_system_object (ctx, data) && data [MONO_TYPEDEF_EXTENDS] != 0) - ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for System.Object must have a null extend field", i)); - - 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)); - if ((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)); - } else { - if (!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 (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 (!get_coded_index_token (TYPEDEF_OR_REF_DESC, data [MONO_TYPEDEF_EXTENDS])) - ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row %d for non-interface type must have a non-null extend field", i)); - } + if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token)) + FAIL (ctx, g_strdup_printf ("Type: 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 type specification (%x). A type signature can't reference itself", ctx->token)); } + break; - if (data [MONO_TYPEDEF_FIELD_LIST] == 0) + 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; + + 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 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 (!parse_type (ctx, &ptr, end)) + FAIL (ctx, g_strdup ("PropertySig: Could not parse property type")); + + for (i = 0; i < param_count; ++i) { + 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) +{ + int 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) +{ + int 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_method_or_field_signature (VerifyContext *ctx, guint32 offset) +{ + int 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_vald_cattr_blob (VerifyContext *ctx, guint32 offset) +{ + int 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_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) +{ + int 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) +{ + int 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) +{ + int 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) +{ + int 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_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) +{ + 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; + + 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 (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]; + guint32 data [MONO_TYPEREF_SIZE]; + int i; + + for (i = 0; i < table->rows; ++i) { + mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE); + if (!is_valid_coded_index (ctx, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d coded index 0x%08x", i, data [MONO_TYPEREF_SCOPE])); + + if (!get_coded_index_token (RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) + ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", i)); + + if (!data [MONO_TYPEREF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAME])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d name token 0x%08x", i, data [MONO_TYPEREF_NAME])); + + if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE])) + ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d namespace token 0x%08x", i, data [MONO_TYPEREF_NAMESPACE])); + } +} + +/*bits 9,11,14,15,19,21,24-31 */ +#define INVALID_TYPEDEF_FLAG_BITS ((1 << 6) | (1 << 9) | (1 << 14) | (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", i, data [MONO_TYPEDEF_FLAGS])); + + 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])); + + 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) @@ -1336,12 +2092,51 @@ verify_typedef_table (VerifyContext *ctx) 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 @@ -1395,9 +2190,10 @@ verify_field_table (VerifyContext *ctx) 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 (!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])); if (i + 1 < module_field_list) { guint32 access = flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK; @@ -1409,8 +2205,23 @@ verify_field_table (VerifyContext *ctx) } } -/*bits 6,8,9,10,11,13,14,15*/ -#define INVALID_METHOD_IMPLFLAG_BITS ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15)) +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 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15)) static void verify_method_table (VerifyContext *ctx) { @@ -1459,12 +2270,14 @@ verify_method_table (VerifyContext *ctx) 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_ERROR (ctx, g_strdup_printf ("Invalid method row %d is CompileControlled and SpecialName or RtSpecialName", i)); + 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)); @@ -1473,9 +2286,6 @@ verify_method_table (VerifyContext *ctx) //TODO check iface with .ctor (15,16) - 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 %08x", i, data [MONO_METHOD_SIGNATURE])); - 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)); @@ -1490,8 +2300,12 @@ verify_method_table (VerifyContext *ctx) 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) && (flags & METHOD_ATTRIBUTE_VIRTUAL)) - ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is PinvokeImpl and 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) @@ -1507,8 +2321,6 @@ verify_method_table (VerifyContext *ctx) ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d has RVA != 0 but is Abstract", 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)); - if (!is_valid_method_header (ctx, rva)) - ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d RVA points to an invalid method header", 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)); @@ -1526,6 +2338,9 @@ verify_method_table (VerifyContext *ctx) 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)); @@ -1540,6 +2355,25 @@ verify_method_table (VerifyContext *ctx) } } +static void +verify_method_table_full (VerifyContext *ctx) +{ + MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD]; + guint32 data [MONO_METHOD_SIZE], rva; + 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)) + 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) { @@ -1573,6 +2407,12 @@ verify_param_table (VerifyContext *ctx) 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) { @@ -1638,15 +2478,31 @@ verify_memberref_table (VerifyContext *ctx) 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 (!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])); } @@ -1693,9 +2549,23 @@ verify_cattr_table (VerifyContext *ctx) if (!is_valid_coded_index (ctx, CATTR_TYPE_DESC, data [MONO_CUSTOM_ATTR_TYPE])) ADD_ERROR (ctx, g_strdup_printf ("Invalid CustomAttribute row %d Parent field 0x%08x", i, data [MONO_CUSTOM_ATTR_PARENT])); + 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) +{ + 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_vald_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])); - } } @@ -1718,9 +2588,23 @@ verify_field_marshal_table (VerifyContext *ctx) 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])); - } } @@ -1742,10 +2626,21 @@ verify_decl_security_table (VerifyContext *ctx) 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])); - } } @@ -1801,6 +2696,21 @@ verify_standalonesig_table (VerifyContext *ctx) 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); @@ -1834,10 +2744,8 @@ static void verify_event_table (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; + guint32 data [MONO_EVENT_SIZE]; + int i; for (i = 0; i < table->rows; ++i) { mono_metadata_decode_row (table, i, data, MONO_EVENT_SIZE); @@ -1850,8 +2758,21 @@ verify_event_table (VerifyContext *ctx) 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); - //check for Add and Remove 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) @@ -1984,12 +2905,28 @@ verify_typespec_table (VerifyContext *ctx) 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 << 7) | (1 << 8) | (1 << 9) | (1 << 10)) +#define INVALID_IMPLMAP_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 6) | (1 << 8) | (1 << 9) | (1 << 10)) static void verify_implmap_table (VerifyContext *ctx) { @@ -2019,7 +2956,7 @@ verify_implmap_table (VerifyContext *ctx) 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_MODULE].rows + 1) + if (!data [MONO_IMPLMAP_SCOPE] || data [MONO_IMPLMAP_SCOPE] > ctx->image->tables [MONO_TABLE_MODULEREF].rows + 1) ADD_ERROR (ctx, g_strdup_printf ("Invalid ImplMap row %d Invalid ImportScope token %x", i, data [MONO_IMPLMAP_SCOPE])); } } @@ -2042,38 +2979,375 @@ verify_fieldrva_table (VerifyContext *ctx) } } -#define INVALID_ASSEMBLY_FLAGS_BITS ~((1 << 8) | (1 << 14) | (1 << 15)) +#define INVALID_ASSEMBLY_FLAGS_BITS ~((1 << 0) | (1 << 4) | (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) +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 = 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; + + 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_TABLE_GENERICPARAM])); + + 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)); + } +} + + +typedef struct { + const char *name; + const char *name_space; + guint32 resolution_scope; +} TypeDefUniqueId; + +static guint +typedef_hash (gconstpointer _key) +{ + const TypeDefUniqueId *key = _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 = _a; + const TypeDefUniqueId *b = _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_assembly_table (VerifyContext *ctx) +verify_typeref_table_global_constraints (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)); + 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) { - 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])) - ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid PublicKey %08x", i, data [MONO_ASSEMBLY_FLAGS])); + TypeDefUniqueId *type = g_new (TypeDefUniqueId, 1); + mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE); - 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])); + 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 (data [MONO_ASSEMBLY_CULTURE] && !is_valid_string (ctx, data [MONO_ASSEMBLY_CULTURE])) - ADD_ERROR (ctx, g_strdup_printf ("Assembly table row %d has invalid Name %08x", i, data [MONO_ASSEMBLY_NAME])); + 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); } +static void +verify_tables_data_global_constraints (VerifyContext *ctx) +{ + verify_typeref_table_global_constraints (ctx); + CHECK_ERROR (); + verify_typedef_table_global_constraints (ctx); +} + static void verify_tables_data (VerifyContext *ctx) { @@ -2148,15 +3422,24 @@ verify_tables_data (VerifyContext *ctx) verify_fieldrva_table (ctx); CHECK_ERROR (); verify_assembly_table (ctx); -} - -static gboolean -mono_verifier_is_corlib (MonoImage *image) -{ - gboolean trusted_location = (mono_security_get_mode () != MONO_SECURITY_MODE_CORE_CLR) ? - TRUE : mono_security_core_clr_is_platform_image (image); - - return trusted_location && !strcmp ("mscorlib.dll", image->name); + 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 @@ -2165,10 +3448,10 @@ init_verify_context (VerifyContext *ctx, MonoImage *image, GSList **error_list) memset (ctx, 0, sizeof (VerifyContext)); ctx->image = image; ctx->report_error = error_list != NULL; + ctx->report_warning = FALSE; //export this setting in the API ctx->valid = 1; ctx->size = image->raw_data_len; ctx->data = image->raw_data; - ctx->is_corlib = mono_verifier_is_corlib (image); } static gboolean @@ -2233,6 +3516,18 @@ 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) { @@ -2248,6 +3543,219 @@ mono_verifier_verify_table_data (MonoImage *image, GSList **error_list) 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); + 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); + +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); + 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; + + if (!mono_verifier_is_enabled_for_image (image)) + return TRUE; + + init_verify_context (&ctx, image, error_list); + ctx.stage = STAGE_TABLES; + + is_valid_method_header (&ctx, offset); + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_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); + ctx.stage = STAGE_TABLES; + + is_valid_method_signature (&ctx, offset); + return cleanup_context (&ctx, error_list); +} + +gboolean +mono_verifier_verify_memberref_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); + ctx.stage = STAGE_TABLES; + + is_valid_method_or_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); + 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); + 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); + 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); + ctx.stage = STAGE_TABLES; + + verify_user_string (&ctx, offset); + + 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->call_convention != signature->call_convention) + 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; +} + #else gboolean mono_verifier_verify_table_data (MonoImage *image, GSList **error_list) @@ -2266,4 +3774,66 @@ 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, GSList **error_list) +{ + return TRUE; +} + +gboolean +mono_verifier_verify_memberref_signature (MonoImage *image, guint32 offset, GSList **error_list) +{ + 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_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMethodSignature *signature) +{ + return TRUE; +} + + #endif /* DISABLE_VERIFIER */