#include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Pull the list of opcodes */ #define OPDEF(a,b,c,d,e,f,g,h,i,j) \ a = i, enum { #include "mono/cil/opcode.def" LAST = 0xff }; #undef OPDEF #if DISABLE_LOGGING #define VERIFIER_DEBUG(code) #else #define VERIFIER_DEBUG(code) do { code } while (0) #endif ////////////////////////////////////////////////////////////////// #define ADD_VERIFY_INFO(__ctx, __msg, __status) \ do { \ MonoVerifyInfo *vinfo = g_new (MonoVerifyInfo, 1); \ vinfo->status = __status; \ vinfo->message = ( __msg ); \ (__ctx)->list = g_slist_prepend ((__ctx)->list, vinfo); \ } while (0) #define ADD_VERIFY_ERROR(__ctx, __msg) \ do { \ ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR); \ (__ctx)->valid = 0; \ } while (0) #define CODE_NOT_VERIFIABLE(__ctx, __msg) \ do { \ if ((__ctx)->verifiable) { \ ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_NOT_VERIFIABLE); \ (__ctx)->verifiable = 0; \ } \ } while (0) enum { IL_CODE_FLAG_NOT_PROCESSED = 0, IL_CODE_FLAG_SEEN = 1 }; typedef struct { MonoType *type; int stype; } ILStackDesc; typedef struct { ILStackDesc *stack; guint16 size; guint16 flags; } ILCodeDesc; typedef struct { int max_args; int max_stack; int verifiable; int valid; int code_size; ILCodeDesc *code; ILCodeDesc eval; MonoType **params; GSList *list; int num_locals; MonoType **locals; int target; guint32 ip_offset; MonoMethodSignature *signature; MonoMethodHeader *header; MonoGenericContext *generic_context; MonoImage *image; } VerifyContext; ////////////////////////////////////////////////////////////////// enum { TYPE_INV = 0, /* leave at 0. */ TYPE_I4 = 1, TYPE_I8 = 2, TYPE_NATIVE_INT = 3, TYPE_R8 = 4, /* Only used by operator tables*/ TYPE_PTR = 5, /* Method pointer, value types and classes */ TYPE_COMPLEX = 6, /* Number of types, used to define the size of the tables*/ TYPE_MAX = 8, /* FIXME: This should probably be 7, but would require all the tables to be updated */ /* Used by tables to signal that a result is not verifiable*/ NON_VERIFIABLE_RESULT = 0x80, /*Mask used to extract just the type, excluding flags */ TYPE_MASK = 0x0F, /* The stack type is a pointer, unmask the value to res */ POINTER_MASK = 0x100, /* Controlled Mutability Manager Pointer */ CMMP_MASK = 0x200, }; static const char* const type_names [TYPE_MAX] = { "Invalid", "Int32", "Int64", "Native Int", "Float64", "TYPE_PTR", /* FIXME: Give an appropriate name */ "Complex" }; enum { PREFIX_UNALIGNED = 1, PREFIX_VOLATILE = 2, PREFIX_TAIL = 4, PREFIX_ADDR_MASK = 3, PREFIX_FUNC_MASK = 4 }; ////////////////////////////////////////////////////////////////// void mono_free_verify_list (GSList *list) { MonoVerifyInfo *info; GSList *tmp; for (tmp = list; tmp; tmp = tmp->next) { info = tmp->data; g_free (info->message); g_free (info); } g_slist_free (list); } #define ADD_ERROR(list,msg) \ do { \ MonoVerifyInfo *vinfo = g_new (MonoVerifyInfo, 1); \ vinfo->status = MONO_VERIFY_ERROR; \ vinfo->message = (msg); \ (list) = g_slist_prepend ((list), vinfo); \ } while (0) #define ADD_WARN(list,code,msg) \ do { \ MonoVerifyInfo *vinfo = g_new (MonoVerifyInfo, 1); \ vinfo->status = (code); \ vinfo->message = (msg); \ (list) = g_slist_prepend ((list), vinfo); \ } while (0) static const char valid_cultures[][9] = { "ar-SA", "ar-IQ", "ar-EG", "ar-LY", "ar-DZ", "ar-MA", "ar-TN", "ar-OM", "ar-YE", "ar-SY", "ar-JO", "ar-LB", "ar-KW", "ar-AE", "ar-BH", "ar-QA", "bg-BG", "ca-ES", "zh-TW", "zh-CN", "zh-HK", "zh-SG", "zh-MO", "cs-CZ", "da-DK", "de-DE", "de-CH", "de-AT", "de-LU", "de-LI", "el-GR", "en-US", "en-GB", "en-AU", "en-CA", "en-NZ", "en-IE", "en-ZA", "en-JM", "en-CB", "en-BZ", "en-TT", "en-ZW", "en-PH", "es-ES-Ts", "es-MX", "es-ES-Is", "es-GT", "es-CR", "es-PA", "es-DO", "es-VE", "es-CO", "es-PE", "es-AR", "es-EC", "es-CL", "es-UY", "es-PY", "es-BO", "es-SV", "es-HN", "es-NI", "es-PR", "Fi-FI", "fr-FR", "fr-BE", "fr-CA", "Fr-CH", "fr-LU", "fr-MC", "he-IL", "hu-HU", "is-IS", "it-IT", "it-CH", "Ja-JP", "ko-KR", "nl-NL", "nl-BE", "nb-NO", "nn-NO", "pl-PL", "pt-BR", "pt-PT", "ro-RO", "ru-RU", "hr-HR", "Lt-sr-SP", "Cy-sr-SP", "sk-SK", "sq-AL", "sv-SE", "sv-FI", "th-TH", "tr-TR", "ur-PK", "id-ID", "uk-UA", "be-BY", "sl-SI", "et-EE", "lv-LV", "lt-LT", "fa-IR", "vi-VN", "hy-AM", "Lt-az-AZ", "Cy-az-AZ", "eu-ES", "mk-MK", "af-ZA", "ka-GE", "fo-FO", "hi-IN", "ms-MY", "ms-BN", "kk-KZ", "ky-KZ", "sw-KE", "Lt-uz-UZ", "Cy-uz-UZ", "tt-TA", "pa-IN", "gu-IN", "ta-IN", "te-IN", "kn-IN", "mr-IN", "sa-IN", "mn-MN", "gl-ES", "kok-IN", "syr-SY", "div-MV" }; static int is_valid_culture (const char *cname) { int i; int found; found = *cname == 0; for (i = 0; i < G_N_ELEMENTS (valid_cultures); ++i) { if (g_strcasecmp (valid_cultures [i], cname)) { found = 1; break; } } return found; } static int is_valid_assembly_flags (guint32 flags) { /* Metadata: 22.1.2 */ flags &= ~(0x8000 | 0x4000); /* ignore reserved bits 0x0030? */ return ((flags == 1) || (flags == 0)); } static int is_valid_blob (MonoImage *image, guint32 blob_index, int notnull) { guint32 size; const char *p, *blob_end; if (blob_index >= image->heap_blob.size) return 0; p = mono_metadata_blob_heap (image, blob_index); size = mono_metadata_decode_blob_size (p, &blob_end); if (blob_index + size + (blob_end-p) > image->heap_blob.size) return 0; if (notnull && !size) return 0; return 1; } static const char* is_valid_string (MonoImage *image, guint32 str_index, int notnull) { const char *p, *blob_end, *res; if (str_index >= image->heap_strings.size) return NULL; res = p = mono_metadata_string_heap (image, str_index); blob_end = mono_metadata_string_heap (image, image->heap_strings.size - 1); if (notnull && !*p) return 0; /* * FIXME: should check it's a valid utf8 string, too. */ while (p <= blob_end) { if (!*p) return res; ++p; } return *p? NULL: res; } static int is_valid_cls_ident (const char *p) { /* * FIXME: we need the full unicode glib support for this. * Check: http://www.unicode.org/unicode/reports/tr15/Identifier.java * We do the lame thing for now. */ if (!isalpha (*p)) return 0; ++p; while (*p) { if (!isalnum (*p) && *p != '_') return 0; ++p; } return 1; } static int is_valid_filename (const char *p) { if (!*p) return 0; return strpbrk (p, "\\//:")? 0: 1; } static GSList* verify_assembly_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLY]; guint32 cols [MONO_ASSEMBLY_SIZE]; const char *p; if (level & MONO_VERIFY_ERROR) { if (t->rows > 1) ADD_ERROR (list, g_strdup ("Assembly table may only have 0 or 1 rows")); mono_metadata_decode_row (t, 0, cols, MONO_ASSEMBLY_SIZE); switch (cols [MONO_ASSEMBLY_HASH_ALG]) { case ASSEMBLY_HASH_NONE: case ASSEMBLY_HASH_MD5: case ASSEMBLY_HASH_SHA1: break; default: ADD_ERROR (list, g_strdup_printf ("Hash algorithm 0x%x unknown", cols [MONO_ASSEMBLY_HASH_ALG])); } if (!is_valid_assembly_flags (cols [MONO_ASSEMBLY_FLAGS])) ADD_ERROR (list, g_strdup_printf ("Invalid flags in assembly: 0x%x", cols [MONO_ASSEMBLY_FLAGS])); if (!is_valid_blob (image, cols [MONO_ASSEMBLY_PUBLIC_KEY], FALSE)) ADD_ERROR (list, g_strdup ("Assembly public key is an invalid index")); if (!(p = is_valid_string (image, cols [MONO_ASSEMBLY_NAME], TRUE))) { ADD_ERROR (list, g_strdup ("Assembly name is invalid")); } else { if (strpbrk (p, ":\\/.")) ADD_ERROR (list, g_strdup_printf ("Assembly name `%s' contains invalid chars", p)); } if (!(p = is_valid_string (image, cols [MONO_ASSEMBLY_CULTURE], FALSE))) { ADD_ERROR (list, g_strdup ("Assembly culture is an invalid index")); } else { if (!is_valid_culture (p)) ADD_ERROR (list, g_strdup_printf ("Assembly culture `%s' is invalid", p)); } } return list; } static GSList* verify_assemblyref_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_ASSEMBLYREF]; guint32 cols [MONO_ASSEMBLYREF_SIZE]; const char *p; int i; if (level & MONO_VERIFY_ERROR) { for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_ASSEMBLYREF_SIZE); if (!is_valid_assembly_flags (cols [MONO_ASSEMBLYREF_FLAGS])) ADD_ERROR (list, g_strdup_printf ("Invalid flags in assemblyref row %d: 0x%x", i + 1, cols [MONO_ASSEMBLY_FLAGS])); if (!is_valid_blob (image, cols [MONO_ASSEMBLYREF_PUBLIC_KEY], FALSE)) ADD_ERROR (list, g_strdup_printf ("AssemblyRef public key in row %d is an invalid index", i + 1)); if (!(p = is_valid_string (image, cols [MONO_ASSEMBLYREF_CULTURE], FALSE))) { ADD_ERROR (list, g_strdup_printf ("AssemblyRef culture in row %d is invalid", i + 1)); } else { if (!is_valid_culture (p)) ADD_ERROR (list, g_strdup_printf ("AssemblyRef culture `%s' in row %d is invalid", p, i + 1)); } if (cols [MONO_ASSEMBLYREF_HASH_VALUE] && !is_valid_blob (image, cols [MONO_ASSEMBLYREF_HASH_VALUE], TRUE)) ADD_ERROR (list, g_strdup_printf ("AssemblyRef hash value in row %d is invalid or not null and empty", i + 1)); } } if (level & MONO_VERIFY_WARNING) { /* check for duplicated rows */ for (i = 0; i < t->rows; ++i) { } } return list; } static GSList* verify_class_layout_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_CLASSLAYOUT]; MonoTableInfo *tdef = &image->tables [MONO_TABLE_TYPEDEF]; guint32 cols [MONO_CLASS_LAYOUT_SIZE]; guint32 value, i; if (level & MONO_VERIFY_ERROR) { for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_CLASS_LAYOUT_SIZE); if (cols [MONO_CLASS_LAYOUT_PARENT] > tdef->rows || !cols [MONO_CLASS_LAYOUT_PARENT]) { ADD_ERROR (list, g_strdup_printf ("Parent in class layout is invalid in row %d", i + 1)); } else { value = mono_metadata_decode_row_col (tdef, cols [MONO_CLASS_LAYOUT_PARENT] - 1, MONO_TYPEDEF_FLAGS); if (value & TYPE_ATTRIBUTE_INTERFACE) ADD_ERROR (list, g_strdup_printf ("Parent in class layout row %d is an interface", i + 1)); if (value & TYPE_ATTRIBUTE_AUTO_LAYOUT) ADD_ERROR (list, g_strdup_printf ("Parent in class layout row %d is AutoLayout", i + 1)); if (value & TYPE_ATTRIBUTE_SEQUENTIAL_LAYOUT) { switch (cols [MONO_CLASS_LAYOUT_PACKING_SIZE]) { case 0: case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: break; default: ADD_ERROR (list, g_strdup_printf ("Packing size %d in class layout row %d is invalid", cols [MONO_CLASS_LAYOUT_PACKING_SIZE], i + 1)); } } else if (value & TYPE_ATTRIBUTE_EXPLICIT_LAYOUT) { /* * FIXME: LAMESPEC: it claims it must be 0 (it's 1, instead). if (cols [MONO_CLASS_LAYOUT_PACKING_SIZE]) ADD_ERROR (list, g_strdup_printf ("Packing size %d in class layout row %d is invalid with explicit layout", cols [MONO_CLASS_LAYOUT_PACKING_SIZE], i + 1)); */ } /* * FIXME: we need to check that if class size != 0, * it needs to be greater than the class calculated size. * If parent is a valuetype it also needs to be smaller than * 1 MByte (0x100000 bytes). * To do both these checks we need to load the referenced * assemblies, though (the spec claims we didn't have to, bah). */ /* * We need to check that the parent types have the samme layout * type as well. */ } } } return list; } static GSList* verify_constant_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_CONSTANT]; guint32 cols [MONO_CONSTANT_SIZE]; guint32 value, i; GHashTable *dups = g_hash_table_new (NULL, NULL); for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_CONSTANT_SIZE); if (level & MONO_VERIFY_ERROR) if (g_hash_table_lookup (dups, GUINT_TO_POINTER (cols [MONO_CONSTANT_PARENT]))) ADD_ERROR (list, g_strdup_printf ("Parent 0x%08x is duplicated in Constant row %d", cols [MONO_CONSTANT_PARENT], i + 1)); g_hash_table_insert (dups, GUINT_TO_POINTER (cols [MONO_CONSTANT_PARENT]), GUINT_TO_POINTER (cols [MONO_CONSTANT_PARENT])); switch (cols [MONO_CONSTANT_TYPE]) { case MONO_TYPE_U1: /* LAMESPEC: it says I1...*/ case MONO_TYPE_U2: case MONO_TYPE_U4: case MONO_TYPE_U8: if (level & MONO_VERIFY_CLS) ADD_WARN (list, MONO_VERIFY_CLS, g_strdup_printf ("Type 0x%x not CLS compliant in Constant row %d", cols [MONO_CONSTANT_TYPE], i + 1)); case MONO_TYPE_BOOLEAN: case MONO_TYPE_CHAR: case MONO_TYPE_I1: case MONO_TYPE_I2: case MONO_TYPE_I4: case MONO_TYPE_I8: case MONO_TYPE_R4: case MONO_TYPE_R8: case MONO_TYPE_STRING: case MONO_TYPE_CLASS: break; default: if (level & MONO_VERIFY_ERROR) ADD_ERROR (list, g_strdup_printf ("Type 0x%x is invalid in Constant row %d", cols [MONO_CONSTANT_TYPE], i + 1)); } if (level & MONO_VERIFY_ERROR) { value = cols [MONO_CONSTANT_PARENT] >> MONO_HASCONSTANT_BITS; switch (cols [MONO_CONSTANT_PARENT] & MONO_HASCONSTANT_MASK) { case MONO_HASCONSTANT_FIEDDEF: if (value > image->tables [MONO_TABLE_FIELD].rows) ADD_ERROR (list, g_strdup_printf ("Parent (field) is invalid in Constant row %d", i + 1)); break; case MONO_HASCONSTANT_PARAM: if (value > image->tables [MONO_TABLE_PARAM].rows) ADD_ERROR (list, g_strdup_printf ("Parent (param) is invalid in Constant row %d", i + 1)); break; case MONO_HASCONSTANT_PROPERTY: if (value > image->tables [MONO_TABLE_PROPERTY].rows) ADD_ERROR (list, g_strdup_printf ("Parent (property) is invalid in Constant row %d", i + 1)); break; default: ADD_ERROR (list, g_strdup_printf ("Parent is invalid in Constant row %d", i + 1)); break; } } if (level & MONO_VERIFY_CLS) { /* * FIXME: verify types is consistent with the enum type * is parent is an enum. */ } } g_hash_table_destroy (dups); return list; } static GSList* verify_event_map_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_EVENTMAP]; guint32 cols [MONO_EVENT_MAP_SIZE]; guint32 i, last_event; GHashTable *dups = g_hash_table_new (NULL, NULL); last_event = 0; for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_EVENT_MAP_SIZE); if (level & MONO_VERIFY_ERROR) if (g_hash_table_lookup (dups, GUINT_TO_POINTER (cols [MONO_EVENT_MAP_PARENT]))) ADD_ERROR (list, g_strdup_printf ("Parent 0x%08x is duplicated in Event Map row %d", cols [MONO_EVENT_MAP_PARENT], i + 1)); g_hash_table_insert (dups, GUINT_TO_POINTER (cols [MONO_EVENT_MAP_PARENT]), GUINT_TO_POINTER (cols [MONO_EVENT_MAP_PARENT])); if (level & MONO_VERIFY_ERROR) { if (cols [MONO_EVENT_MAP_PARENT] > image->tables [MONO_TABLE_TYPEDEF].rows) ADD_ERROR (list, g_strdup_printf ("Parent 0x%08x is invalid in Event Map row %d", cols [MONO_EVENT_MAP_PARENT], i + 1)); if (cols [MONO_EVENT_MAP_EVENTLIST] > image->tables [MONO_TABLE_EVENT].rows) ADD_ERROR (list, g_strdup_printf ("EventList 0x%08x is invalid in Event Map row %d", cols [MONO_EVENT_MAP_EVENTLIST], i + 1)); if (cols [MONO_EVENT_MAP_EVENTLIST] <= last_event) ADD_ERROR (list, g_strdup_printf ("EventList overlap in Event Map row %d", i + 1)); last_event = cols [MONO_EVENT_MAP_EVENTLIST]; } } g_hash_table_destroy (dups); return list; } static GSList* verify_event_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_EVENT]; guint32 cols [MONO_EVENT_SIZE]; const char *p; guint32 value, i; for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_EVENT_SIZE); if (cols [MONO_EVENT_FLAGS] & ~(EVENT_SPECIALNAME|EVENT_RTSPECIALNAME)) { if (level & MONO_VERIFY_ERROR) ADD_ERROR (list, g_strdup_printf ("Flags 0x%04x invalid in Event row %d", cols [MONO_EVENT_FLAGS], i + 1)); } if (!(p = is_valid_string (image, cols [MONO_EVENT_NAME], TRUE))) { if (level & MONO_VERIFY_ERROR) ADD_ERROR (list, g_strdup_printf ("Invalid name in Event row %d", i + 1)); } else { if (level & MONO_VERIFY_CLS) { if (!is_valid_cls_ident (p)) ADD_WARN (list, MONO_VERIFY_CLS, g_strdup_printf ("Invalid CLS name '%s` in Event row %d", p, i + 1)); } } if (level & MONO_VERIFY_ERROR && cols [MONO_EVENT_TYPE]) { value = cols [MONO_EVENT_TYPE] >> MONO_TYPEDEFORREF_BITS; switch (cols [MONO_EVENT_TYPE] & MONO_TYPEDEFORREF_MASK) { case MONO_TYPEDEFORREF_TYPEDEF: if (!value || value > image->tables [MONO_TABLE_TYPEDEF].rows) ADD_ERROR (list, g_strdup_printf ("Type invalid in Event row %d", i + 1)); break; case MONO_TYPEDEFORREF_TYPEREF: if (!value || value > image->tables [MONO_TABLE_TYPEREF].rows) ADD_ERROR (list, g_strdup_printf ("Type invalid in Event row %d", i + 1)); break; case MONO_TYPEDEFORREF_TYPESPEC: if (!value || value > image->tables [MONO_TABLE_TYPESPEC].rows) ADD_ERROR (list, g_strdup_printf ("Type invalid in Event row %d", i + 1)); break; default: ADD_ERROR (list, g_strdup_printf ("Type invalid in Event row %d", i + 1)); } } /* * FIXME: check that there is 1 add and remove row in methodsemantics * and 0 or 1 raise and 0 or more other (maybe it's better to check for * these while checking methodsemantics). * check for duplicated names for the same type [ERROR] * check for CLS duplicate names for the same type [CLS] */ } return list; } static GSList* verify_field_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_FIELD]; guint32 cols [MONO_FIELD_SIZE]; const char *p; guint32 i, flags; for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_FIELD_SIZE); /* * Check this field has only one owner and that the owner is not * an interface (done in verify_typedef_table() ) */ flags = cols [MONO_FIELD_FLAGS]; switch (flags & FIELD_ATTRIBUTE_FIELD_ACCESS_MASK) { case FIELD_ATTRIBUTE_COMPILER_CONTROLLED: case FIELD_ATTRIBUTE_PRIVATE: case FIELD_ATTRIBUTE_FAM_AND_ASSEM: case FIELD_ATTRIBUTE_ASSEMBLY: case FIELD_ATTRIBUTE_FAMILY: case FIELD_ATTRIBUTE_FAM_OR_ASSEM: case FIELD_ATTRIBUTE_PUBLIC: break; default: if (level & MONO_VERIFY_ERROR) ADD_ERROR (list, g_strdup_printf ("Invalid access mask in Field row %d", i + 1)); break; } if (level & MONO_VERIFY_ERROR) { if ((flags & FIELD_ATTRIBUTE_LITERAL) && (flags & FIELD_ATTRIBUTE_INIT_ONLY)) ADD_ERROR (list, g_strdup_printf ("Literal and InitOnly cannot be both set in Field row %d", i + 1)); if ((flags & FIELD_ATTRIBUTE_LITERAL) && !(flags & FIELD_ATTRIBUTE_STATIC)) ADD_ERROR (list, g_strdup_printf ("Literal needs also Static set in Field row %d", i + 1)); if ((flags & FIELD_ATTRIBUTE_RT_SPECIAL_NAME) && !(flags & FIELD_ATTRIBUTE_SPECIAL_NAME)) ADD_ERROR (list, g_strdup_printf ("RTSpecialName needs also SpecialName set in Field row %d", i + 1)); /* * FIXME: check there is only ono owner in the respective table. * if (flags & FIELD_ATTRIBUTE_HAS_FIELD_MARSHAL) * if (flags & FIELD_ATTRIBUTE_HAS_DEFAULT) * if (flags & FIELD_ATTRIBUTE_HAS_FIELD_RVA) */ } if (!(p = is_valid_string (image, cols [MONO_FIELD_NAME], TRUE))) { if (level & MONO_VERIFY_ERROR) ADD_ERROR (list, g_strdup_printf ("Invalid name in Field row %d", i + 1)); } else { if (level & MONO_VERIFY_CLS) { if (!is_valid_cls_ident (p)) ADD_WARN (list, MONO_VERIFY_CLS, g_strdup_printf ("Invalid CLS name '%s` in Field row %d", p, i + 1)); } } /* * check signature. * if owner is module needs to be static, access mask needs to be compilercontrolled, * public or private (not allowed in cls mode). * if owner is an enum ... */ } return list; } static GSList* verify_file_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_FILE]; guint32 cols [MONO_FILE_SIZE]; const char *p; guint32 i; GHashTable *dups = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_FILE_SIZE); if (level & MONO_VERIFY_ERROR) { if (cols [MONO_FILE_FLAGS] != FILE_CONTAINS_METADATA && cols [MONO_FILE_FLAGS] != FILE_CONTAINS_NO_METADATA) ADD_ERROR (list, g_strdup_printf ("Invalid flags in File row %d", i + 1)); if (!is_valid_blob (image, cols [MONO_FILE_HASH_VALUE], TRUE)) ADD_ERROR (list, g_strdup_printf ("File hash value in row %d is invalid or not null and empty", i + 1)); } if (!(p = is_valid_string (image, cols [MONO_FILE_NAME], TRUE))) { if (level & MONO_VERIFY_ERROR) ADD_ERROR (list, g_strdup_printf ("Invalid name in File row %d", i + 1)); } else { if (level & MONO_VERIFY_ERROR) { if (!is_valid_filename (p)) ADD_ERROR (list, g_strdup_printf ("Invalid name '%s` in File row %d", p, i + 1)); else if (g_hash_table_lookup (dups, p)) { ADD_ERROR (list, g_strdup_printf ("Duplicate name '%s` in File row %d", p, i + 1)); } g_hash_table_insert (dups, (gpointer)p, (gpointer)p); } } /* * FIXME: I don't understand what this means: * If this module contains a row in the Assembly table (that is, if this module "holds the manifest") * then there shall not be any row in the File table for this module - i.e., no self-reference [ERROR] */ } if (level & MONO_VERIFY_WARNING) { if (!t->rows && image->tables [MONO_TABLE_EXPORTEDTYPE].rows) ADD_WARN (list, MONO_VERIFY_WARNING, g_strdup ("ExportedType table should be empty if File table is empty")); } g_hash_table_destroy (dups); return list; } static GSList* verify_moduleref_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_MODULEREF]; MonoTableInfo *tfile = &image->tables [MONO_TABLE_FILE]; guint32 cols [MONO_MODULEREF_SIZE]; const char *p, *pf; guint32 found, i, j, value; GHashTable *dups = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_MODULEREF_SIZE); if (!(p = is_valid_string (image, cols [MONO_MODULEREF_NAME], TRUE))) { if (level & MONO_VERIFY_ERROR) ADD_ERROR (list, g_strdup_printf ("Invalid name in ModuleRef row %d", i + 1)); } else { if (level & MONO_VERIFY_ERROR) { if (!is_valid_filename (p)) ADD_ERROR (list, g_strdup_printf ("Invalid name '%s` in ModuleRef row %d", p, i + 1)); else if (g_hash_table_lookup (dups, p)) { ADD_WARN (list, MONO_VERIFY_WARNING, g_strdup_printf ("Duplicate name '%s` in ModuleRef row %d", p, i + 1)); g_hash_table_insert (dups, (gpointer)p, (gpointer)p); found = 0; for (j = 0; j < tfile->rows; ++j) { value = mono_metadata_decode_row_col (tfile, j, MONO_FILE_NAME); if ((pf = is_valid_string (image, value, TRUE))) if (strcmp (p, pf) == 0) { found = 1; break; } } if (!found) ADD_ERROR (list, g_strdup_printf ("Name '%s` in ModuleRef row %d doesn't have a match in File table", p, i + 1)); } } } } g_hash_table_destroy (dups); return list; } static GSList* verify_standalonesig_table (MonoImage *image, GSList *list, int level) { MonoTableInfo *t = &image->tables [MONO_TABLE_STANDALONESIG]; guint32 cols [MONO_STAND_ALONE_SIGNATURE_SIZE]; const char *p; guint32 i; for (i = 0; i < t->rows; ++i) { mono_metadata_decode_row (t, i, cols, MONO_STAND_ALONE_SIGNATURE_SIZE); if (level & MONO_VERIFY_ERROR) { if (!is_valid_blob (image, cols [MONO_STAND_ALONE_SIGNATURE], TRUE)) { ADD_ERROR (list, g_strdup_printf ("Signature is invalid in StandAloneSig row %d", i + 1)); } else { p = mono_metadata_blob_heap (image, cols [MONO_STAND_ALONE_SIGNATURE]); /* FIXME: check it's a valid locals or method sig.*/ } } } return list; } GSList* mono_image_verify_tables (MonoImage *image, int level) { GSList *error_list = NULL; error_list = verify_assembly_table (image, error_list, level); /* * AssemblyOS, AssemblyProcessor, AssemblyRefOs and * AssemblyRefProcessor should be ignored, * though we may want to emit a warning, since it should not * be present in a PE file. */ error_list = verify_assemblyref_table (image, error_list, level); error_list = verify_class_layout_table (image, error_list, level); error_list = verify_constant_table (image, error_list, level); /* * cutom attribute, declsecurity */ error_list = verify_event_map_table (image, error_list, level); error_list = verify_event_table (image, error_list, level); error_list = verify_field_table (image, error_list, level); error_list = verify_file_table (image, error_list, level); error_list = verify_moduleref_table (image, error_list, level); error_list = verify_standalonesig_table (image, error_list, level); return g_slist_reverse (error_list); } static const char bin_num_table [TYPE_MAX] [TYPE_MAX] = { {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I4, TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_COMPLEX, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_COMPLEX, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_R8, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_COMPLEX, TYPE_INV, TYPE_COMPLEX, TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV} }; static const char neg_table [] = { TYPE_INV, TYPE_I4, TYPE_I8, TYPE_PTR, TYPE_R8, TYPE_INV, TYPE_INV, TYPE_INV }; /* reduce the size of this table */ static const char bin_int_table [TYPE_MAX] [TYPE_MAX] = { {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I4, TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV} }; static const char bin_comp_table [TYPE_MAX] [TYPE_MAX] = { {0}, {0, 1, 0, 1, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0}, {0, 1, 0, 1, 0, 2, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 2, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 3, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, }; /* reduce the size of this table */ static const char shift_table [TYPE_MAX] [TYPE_MAX] = { {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_PTR, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV} }; static const char ldind_type [] = { TYPE_I4, TYPE_I4, TYPE_I4, TYPE_I4, TYPE_I4, TYPE_I4, TYPE_I8, TYPE_PTR, TYPE_R8, TYPE_R8, TYPE_COMPLEX }; static const char ldelem_type [] = { TYPE_I4, TYPE_I4, TYPE_I4, TYPE_I4, TYPE_I4, TYPE_I4, TYPE_I8, TYPE_PTR, TYPE_R8, TYPE_R8, TYPE_COMPLEX }; #define ADD_INVALID(list,msg) \ do { \ MonoVerifyInfo *vinfo = g_new (MonoVerifyInfo, 1); \ vinfo->status = MONO_VERIFY_ERROR; \ vinfo->message = (msg); \ (list) = g_slist_prepend ((list), vinfo); \ /*G_BREAKPOINT ();*/ \ goto invalid_cil; \ } while (0) #define CHECK_STACK_UNDERFLOW(num) \ do { \ if (cur_stack < (num)) \ ADD_INVALID (list, g_strdup_printf ("Stack underflow at 0x%04x (%d items instead of %d)", ip_offset, cur_stack, (num))); \ } while (0) #define CHECK_STACK_OVERFLOW() \ do { \ if (cur_stack >= max_stack) \ ADD_INVALID (list, g_strdup_printf ("Maxstack exceeded at 0x%04x", ip_offset)); \ } while (0) static void type_to_eval_stack_type (MonoType *type, ILStackDesc *stack, int take_addr) { int t = type->type; stack->type = type; if (type->byref || take_addr) { /* fix double addr issue */ stack->stype = TYPE_COMPLEX; return; } handle_enum: switch (t) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: stack->stype = TYPE_I4; return; case MONO_TYPE_I: case MONO_TYPE_U: case MONO_TYPE_PTR: stack->stype = TYPE_PTR; return; case MONO_TYPE_CLASS: case MONO_TYPE_STRING: case MONO_TYPE_OBJECT: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: stack->stype = TYPE_COMPLEX; return; case MONO_TYPE_I8: case MONO_TYPE_U8: stack->stype = TYPE_I8; return; case MONO_TYPE_R4: case MONO_TYPE_R8: stack->stype = TYPE_R8; return; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { t = type->data.klass->enum_basetype->type; goto handle_enum; } else { stack->stype = TYPE_COMPLEX; return; } default: g_error ("unknown type %02x in eval stack type", type->type); } return; } static int type_from_op (int ins, ILStackDesc *arg) { switch (ins) { /* binops */ case CEE_ADD: case CEE_SUB: case CEE_MUL: case CEE_DIV: case CEE_REM: /* FIXME: check unverifiable args for TYPE_COMPLEX */ return arg->stype = bin_num_table [arg->stype] [arg [1].stype]; case CEE_DIV_UN: case CEE_REM_UN: case CEE_AND: case CEE_OR: case CEE_XOR: return arg->stype = bin_int_table [arg->stype] [arg [1].stype]; case CEE_SHL: case CEE_SHR: case CEE_SHR_UN: return arg->stype = shift_table [arg->stype] [arg [1].stype]; case CEE_BEQ_S: case CEE_BGE_S: case CEE_BGT_S: case CEE_BLE_S: case CEE_BLT_S: case CEE_BNE_UN_S: case CEE_BGE_UN_S: case CEE_BGT_UN_S: case CEE_BLE_UN_S: case CEE_BLT_UN_S: case CEE_BEQ: case CEE_BGE: case CEE_BGT: case CEE_BLE: case CEE_BLT: case CEE_BNE_UN: case CEE_BGE_UN: case CEE_BGT_UN: case CEE_BLE_UN: case CEE_BLT_UN: /* FIXME: handle some specifics with ins->next->type */ return bin_comp_table [arg->stype] [arg [1].stype] ? TYPE_I4: TYPE_INV; case 256+CEE_CEQ: case 256+CEE_CGT: case 256+CEE_CGT_UN: case 256+CEE_CLT: case 256+CEE_CLT_UN: return arg->stype = bin_comp_table [arg->stype] [arg [1].stype] ? TYPE_I4: TYPE_INV; /* unops */ case CEE_NEG: return arg->stype = neg_table [arg->stype]; case CEE_NOT: if (arg->stype >= TYPE_I4 && arg->stype <= TYPE_PTR) return arg->stype; else return arg->stype = TYPE_INV; case CEE_CONV_I1: case CEE_CONV_U1: case CEE_CONV_I2: case CEE_CONV_U2: case CEE_CONV_I4: case CEE_CONV_U4: case CEE_CONV_OVF_I1: case CEE_CONV_OVF_U1: case CEE_CONV_OVF_I2: case CEE_CONV_OVF_U2: case CEE_CONV_OVF_I4: case CEE_CONV_OVF_U4: case CEE_CONV_OVF_I1_UN: case CEE_CONV_OVF_U1_UN: case CEE_CONV_OVF_I2_UN: case CEE_CONV_OVF_U2_UN: case CEE_CONV_OVF_I4_UN: case CEE_CONV_OVF_U4_UN: if (arg->stype == TYPE_INV || arg->stype >= TYPE_COMPLEX) return arg->stype = TYPE_INV; return arg->stype = TYPE_I4; case CEE_CONV_I: case CEE_CONV_U: case CEE_CONV_OVF_I: case CEE_CONV_OVF_U: case CEE_CONV_OVF_I_UN: case CEE_CONV_OVF_U_UN: if (arg->stype == TYPE_INV || arg->stype == TYPE_COMPLEX) return arg->stype = TYPE_INV; return arg->stype = TYPE_PTR; case CEE_CONV_I8: case CEE_CONV_U8: case CEE_CONV_OVF_I8: case CEE_CONV_OVF_U8: case CEE_CONV_OVF_I8_UN: case CEE_CONV_OVF_U8_UN: return arg->stype = TYPE_I8; case CEE_CONV_R4: case CEE_CONV_R8: return arg->stype = TYPE_R8; default: g_error ("opcode 0x%04x not handled in type from op", ins); break; } return FALSE; } static int in_any_block (MonoMethodHeader *header, guint offset) { int i; MonoExceptionClause *clause; for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (MONO_OFFSET_IN_CLAUSE (clause, offset)) return 1; if (MONO_OFFSET_IN_HANDLER (clause, offset)) return 1; /* need to check filter ... */ } return 0; } static int in_same_block (MonoMethodHeader *header, guint offset, guint target) { int i; MonoExceptionClause *clause; for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (MONO_OFFSET_IN_CLAUSE (clause, offset) && !MONO_OFFSET_IN_CLAUSE (clause, target)) return 0; if (MONO_OFFSET_IN_HANDLER (clause, offset) && !MONO_OFFSET_IN_HANDLER (clause, target)) return 0; /* need to check filter ... */ } return 1; } /* * A leave can't escape a finally block */ static int is_correct_leave (MonoMethodHeader *header, guint offset, guint target) { int i; MonoExceptionClause *clause; for (i = 0; i < header->num_clauses; ++i) { clause = &header->clauses [i]; if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY && MONO_OFFSET_IN_HANDLER (clause, offset) && !MONO_OFFSET_IN_HANDLER (clause, target)) return 0; /* need to check filter ... */ } return 1; } static int can_merge_stack (ILCodeDesc *a, ILCodeDesc *b) { if (!b->flags & IL_CODE_FLAG_SEEN) { b->flags |= IL_CODE_FLAG_SEEN; b->size = a->size; /* merge types */ return 1; } if (a->size != b->size) return 0; /* merge types */ return 1; } static gboolean is_valid_bool_arg (ILStackDesc *arg) { if (arg->stype & POINTER_MASK) return TRUE; switch (arg->stype) { case TYPE_I4: case TYPE_I8: case TYPE_NATIVE_INT: return TRUE; case TYPE_COMPLEX: g_assert (arg->type); switch (arg->type->type) { case MONO_TYPE_CLASS: case MONO_TYPE_STRING: case MONO_TYPE_OBJECT: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: case MONO_TYPE_FNPTR: case MONO_TYPE_PTR: return TRUE; case MONO_TYPE_GENERICINST: /*We need to check if the container class * of the generic type is a valuetype, iow: * is it a "class Foo" or a "struct Foo"? */ return !arg->type->data.generic_class->container_class->valuetype; } default: return FALSE; } } static int can_store_type (ILStackDesc *arg, MonoType *type) { int t = type->type; if (type->byref && arg->stype != TYPE_COMPLEX) return FALSE; handle_enum: switch (t) { case MONO_TYPE_VOID: return FALSE; case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: if (arg->stype == TYPE_I4 || arg->stype == TYPE_PTR) return TRUE; return FALSE; case MONO_TYPE_I: case MONO_TYPE_U: case MONO_TYPE_PTR: return TRUE; case MONO_TYPE_CLASS: case MONO_TYPE_STRING: case MONO_TYPE_OBJECT: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: return TRUE; /* FIXME */ case MONO_TYPE_I8: case MONO_TYPE_U8: if (arg->stype == TYPE_I8) return TRUE; return FALSE; case MONO_TYPE_R4: case MONO_TYPE_R8: if (arg->stype == TYPE_R8) return TRUE; return FALSE; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { t = type->data.klass->enum_basetype->type; goto handle_enum; } else { if (arg->type->data.klass != type->data.klass) return FALSE; return TRUE; } default: g_error ("unknown type %02x in store type", type->type); } return FALSE; } static int stind_type (int op, int type) { switch (op) { case CEE_STIND_REF: return type == TYPE_COMPLEX; case CEE_STIND_I1: case CEE_STIND_I2: case CEE_STIND_I4: return type == TYPE_I4; case CEE_STIND_I8: return type == TYPE_I8; case CEE_STIND_R4: case CEE_STIND_R8: return type == TYPE_R8; default: g_assert_not_reached (); } return FALSE; } /*Stack manipulation code*/ static void stack_init (VerifyContext *ctx, ILCodeDesc *state) { state->size = 0; if (!state->stack) { state->stack = g_new0 (ILStackDesc, ctx->max_stack); } } static void stack_copy (ILCodeDesc *to, ILCodeDesc *from) { to->size = from->size; memcpy (to->stack, from->stack, sizeof (ILStackDesc) * from->size); } static void copy_stack_value (ILStackDesc *to, ILStackDesc *from) { to->stype = from->stype; to->type = from->type; } static int check_underflow (VerifyContext *ctx, int size) { if (ctx->eval.size < size) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Stack underflow, required %d, but have %d", size, ctx->eval.size)); return 0; } return 1; } static int check_overflow (VerifyContext *ctx) { if (ctx->eval.size >= ctx->max_stack) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have stack-depth %d", ctx->eval.size + 1)); return 0; } return 1; } static ILStackDesc * stack_push (VerifyContext *ctx) { return & ctx->eval.stack [ctx->eval.size++]; } static ILStackDesc * stack_push_val (VerifyContext *ctx, int stype, MonoType *type) { ILStackDesc *top = stack_push (ctx); top->stype = stype; top->type = type; return top; } static ILStackDesc * stack_pop (VerifyContext *ctx) { return ctx->eval.stack + --ctx->eval.size; } static inline ILStackDesc * stack_top (VerifyContext *ctx) { return ctx->eval.stack + (ctx->eval.size - 1); } static inline ILStackDesc * stack_get (VerifyContext *ctx, int distance) { return ctx->eval.stack + (ctx->eval.size - distance - 1); } /*operation result tables */ static const unsigned char bin_op_table [TYPE_MAX][TYPE_MAX] = { {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_R8, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, }; static const unsigned char add_table [TYPE_MAX][TYPE_MAX] = { {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV}, {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_R8, TYPE_INV, TYPE_INV}, {TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, }; static const unsigned char sub_table [TYPE_MAX][TYPE_MAX] = { {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_R8, TYPE_INV, TYPE_INV}, {TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_PTR | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_NATIVE_INT | NON_VERIFIABLE_RESULT, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, }; static const unsigned char int_bin_op_table [TYPE_MAX][TYPE_MAX] = { {TYPE_I4, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, }; static const unsigned char shift_op_table [TYPE_MAX][TYPE_MAX] = { {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_I8, TYPE_INV, TYPE_I8, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_NATIVE_INT, TYPE_INV, TYPE_NATIVE_INT, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, }; static const unsigned char cmp_br_op [TYPE_MAX][TYPE_MAX] = { {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_I4, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, }; static const unsigned char cmp_br_eq_op [TYPE_MAX][TYPE_MAX] = { {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV}, {TYPE_I4, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_I4 | NON_VERIFIABLE_RESULT, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_I4, TYPE_INV, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_I4 | NON_VERIFIABLE_RESULT, TYPE_INV, TYPE_I4, TYPE_INV}, {TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_INV, TYPE_I4}, }; /*debug helpers */ static void dump_stack_value (ILStackDesc *value) { printf ("[(%d)(%d)", value->type->type, value->stype); if (value->stype & CMMP_MASK) printf ("Controled Mutability MP: "); if (value->stype & POINTER_MASK) printf ("Managed Pointer: "); switch (value->stype & TYPE_MASK) { case TYPE_INV: printf ("invalid type]"); return; case TYPE_I4: printf ("int32]"); return; case TYPE_I8: printf ("int64]"); return; case TYPE_NATIVE_INT: printf ("native int]"); return; case TYPE_R8: printf ("float64]"); return; case TYPE_PTR: printf ("pointer]"); return; case TYPE_COMPLEX: printf ("complex]"); return; default: printf ("unknown %d type]", value->stype); g_assert_not_reached (); } } static void dump_stack_state (ILCodeDesc *state) { int i; printf ("(%d) ", state->size); for (i = 0; i < state->size; ++i) dump_stack_value (state->stack + i); printf ("\n"); } static void dump_context (VerifyContext *ctx, int code_size) { int i; for (i = 0; i < code_size; ++i) { if (ctx->code [i].flags & IL_CODE_FLAG_SEEN) { printf ("opcode [%d]:\n\t", i); dump_stack_state (&ctx->code [i]); } } } /*Returns TRUE if candidate array type can be assigned to target. *Both parameters MUST be of type MONO_TYPE_ARRAY (target->type == MONO_TYPE_ARRAY) */ static gboolean is_array_type_compatible (MonoType *target, MonoType *candidate) { int i; MonoArrayType *left = target->data.array; MonoArrayType *right = candidate->data.array; g_assert (target->type == MONO_TYPE_ARRAY); g_assert (candidate->type == MONO_TYPE_ARRAY); if ((left->rank != right->rank) || (left->numsizes != right->numsizes) || (left->numlobounds != right->numlobounds)) return FALSE; for (i = 0; i < left->numsizes; ++i) if (left->sizes [i] != right->sizes [i]) return FALSE; for (i = 0; i < left->numlobounds; ++i) if (left->lobounds [i] != right->lobounds [i]) return FALSE; return mono_class_is_assignable_from (left->eklass, right->eklass); } static int get_stack_type (MonoType *type) { int mask = 0; int type_kind = type->type; if (type->byref) mask = POINTER_MASK; /*TODO handle CMMP_MASK */ handle_enum: switch (type_kind) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: return TYPE_I4 | mask; case MONO_TYPE_I: case MONO_TYPE_U: return TYPE_NATIVE_INT | mask; case MONO_TYPE_PTR: /*FIXME: We should flag this as an unmanaged pointer. should we mark the method as unverifiable right now? */ mask = POINTER_MASK; type = type->data.type; type_kind = type->type; goto handle_enum; /* FIXME: the spec says that you cannot have a pointer to method pointer, do we need to check this here? */ case MONO_TYPE_FNPTR: case MONO_TYPE_CLASS: case MONO_TYPE_STRING: case MONO_TYPE_OBJECT: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: case MONO_TYPE_TYPEDBYREF: case MONO_TYPE_GENERICINST: return TYPE_COMPLEX | mask; case MONO_TYPE_I8: case MONO_TYPE_U8: return TYPE_I8 | mask; case MONO_TYPE_R4: case MONO_TYPE_R8: return TYPE_R8 | mask; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { type = type->data.klass->enum_basetype; type_kind = type->type; goto handle_enum; } else return TYPE_COMPLEX | mask; default: VERIFIER_DEBUG ( printf ("unknown type %02x in eval stack type\n", type->type); ); g_assert_not_reached (); return 0; } } /* convert MonoType to ILStackDesc format (stype) */ static void set_stack_value (ILStackDesc *stack, MonoType *type, int take_addr) { int mask = 0; int type_kind = type->type; if (type->byref || take_addr) mask = POINTER_MASK; /* TODO handle CMMP_MASK */ handle_enum: stack->type = type; switch (type_kind) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: stack->stype = TYPE_I4 | mask; return; case MONO_TYPE_I: case MONO_TYPE_U: stack->stype = TYPE_NATIVE_INT | mask; return; case MONO_TYPE_PTR: /*FIXME: We should flag this as an unmanaged pointer. should we mark the method as unverifiable right now? */ mask = POINTER_MASK; type = type->data.type; type_kind = type->type; goto handle_enum; case MONO_TYPE_CLASS: case MONO_TYPE_STRING: case MONO_TYPE_OBJECT: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: case MONO_TYPE_TYPEDBYREF: /*FIXME: Do we need to check if it's a pointer to the method pointer? The spec says it' illegal to have that.*/ case MONO_TYPE_FNPTR: case MONO_TYPE_GENERICINST: stack->stype = TYPE_COMPLEX | mask; return; case MONO_TYPE_I8: case MONO_TYPE_U8: stack->stype = TYPE_I8 | mask; return; case MONO_TYPE_R4: case MONO_TYPE_R8: stack->stype = TYPE_R8 | mask; return; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { type = type->data.klass->enum_basetype; type_kind = type->type; goto handle_enum; } else { stack->stype = TYPE_COMPLEX | mask; return; } default: VERIFIER_DEBUG ( printf ("unknown type %02x in eval stack type\n", type->type); ); g_assert_not_reached (); } return; } /* Generics validation stuff, should be moved to another metadata/? file */ static gboolean mono_is_generic_type_compatible (MonoType *target, MonoType *candidate) { if (target->byref != candidate->byref) return FALSE; handle_enum: switch (target->type) { case MONO_TYPE_STRING: if (candidate->type == MONO_TYPE_STRING) return TRUE; return FALSE; case MONO_TYPE_CLASS: if (candidate->type != MONO_TYPE_CLASS) return FALSE; VERIFIER_DEBUG ( printf ("verifying type class %p %p\n", target->data.klass, candidate->data.klass); ); return mono_class_is_assignable_from (target->data.klass, candidate->data.klass); case MONO_TYPE_OBJECT: return MONO_TYPE_IS_REFERENCE (candidate); case MONO_TYPE_SZARRAY: if (candidate->type != MONO_TYPE_SZARRAY) return FALSE; return mono_class_is_assignable_from (target->data.klass, candidate->data.klass); case MONO_TYPE_VALUETYPE: if (target->data.klass->enumtype) { target = target->data.klass->enum_basetype; goto handle_enum; } else { if (candidate->type != MONO_TYPE_VALUETYPE) return FALSE; return candidate->data.klass == target->data.klass; } case MONO_TYPE_ARRAY: if (candidate->type != MONO_TYPE_ARRAY) return FALSE; return is_array_type_compatible (target, candidate); default: VERIFIER_DEBUG ( printf ("unknown target type %d\n", target->type); ); g_assert_not_reached (); } return FALSE; } static gboolean mono_is_generic_instance_compatible (MonoGenericClass *target, MonoGenericClass *candidate, MonoGenericClass *root_candidate) { MonoGenericContainer *container; int i; VERIFIER_DEBUG ( printf ("candidate container %p\n", candidate->container_class->generic_container); ); if (target->container_class != candidate->container_class) { MonoType *param_class; MonoClass *cand_class; VERIFIER_DEBUG ( printf ("generic class != target\n"); ); param_class = candidate->context.class_inst->type_argv [0]; VERIFIER_DEBUG ( printf ("param 0 %d\n", param_class->type); ); cand_class = candidate->container_class; /* We must check if it's an interface type*/ if (MONO_CLASS_IS_INTERFACE (target->container_class)) { VERIFIER_DEBUG ( printf ("generic type is an interface\n"); ); do { int iface_count = cand_class->interface_count; MonoClass **ifaces = cand_class->interfaces; int i; VERIFIER_DEBUG ( printf ("type has %d interfaces\n", iface_count); ); for (i = 0; i< iface_count; ++i) { MonoClass *ifc = ifaces[i]; VERIFIER_DEBUG ( printf ("analysing %s\n", ifc->name); ); if (ifc->generic_class) { VERIFIER_DEBUG ( printf ("interface has generic info\n"); ); } if (mono_is_generic_instance_compatible (target, ifc->generic_class, root_candidate)) { VERIFIER_DEBUG ( printf ("we got compatible stuff!\n"); ); return TRUE; } } cand_class = cand_class->parent; } while (cand_class); VERIFIER_DEBUG ( printf ("don't implements an interface\n"); ); } else { VERIFIER_DEBUG ( printf ("verifying upper classes\n"); ); cand_class = cand_class->parent; while (cand_class) { VERIFIER_DEBUG ( printf ("verifying parent class name %s\n", cand_class->name); ); if (cand_class->generic_class) { VERIFIER_DEBUG ( printf ("super type has generic context\n"); ); /* TODO break loop if target->container_class == cand_class->generic_class->container_class */ return mono_is_generic_instance_compatible (target, cand_class->generic_class, root_candidate); } else VERIFIER_DEBUG ( printf ("super class has no generic context\n"); ); cand_class = cand_class->parent; } } return FALSE; } /* now we verify if the instantiations are compatible*/ if (target->context.class_inst == candidate->context.class_inst) { VERIFIER_DEBUG ( printf ("generic types are compatible, both have the same instantiation\n"); ); return TRUE; } if (target->context.class_inst->type_argc != candidate->context.class_inst->type_argc) { VERIFIER_DEBUG ( printf ("generic instantiations with diferent arg counts\n"); ); return FALSE; } //verify if open instance -- none should be container = target->container_class->generic_container; for (i = 0; i < container->type_argc; ++i) { MonoGenericParam *param = container->type_params + i; MonoType *target_type = target->context.class_inst->type_argv [i]; MonoType *candidate_type = candidate->context.class_inst->type_argv [i]; /* We resolve TYPE_VAR types before proceeding */ if (candidate_type->type == MONO_TYPE_VAR) { MonoGenericParam *var_param = candidate_type->data.generic_param; candidate_type = root_candidate->context.class_inst->type_argv [var_param->num]; } if ((param->flags & GENERIC_PARAMETER_ATTRIBUTE_VARIANCE_MASK) == 0) { VERIFIER_DEBUG ( printf ("generic type have no variance flag, checking each type %d %d \n",target_type->type, candidate_type->type); ); if (!mono_metadata_type_equal (target_type, candidate_type)) return FALSE; } else { VERIFIER_DEBUG ( printf ("generic type has variance flag, need to perform deeper check\n"); ); /* first we check if they are the same kind */ /* byref generic params are forbiden, but better safe than sorry.*/ if ((param->flags & GENERIC_PARAMETER_ATTRIBUTE_COVARIANT) == GENERIC_PARAMETER_ATTRIBUTE_COVARIANT) { if (!mono_is_generic_type_compatible (target_type, candidate_type)) return FALSE; /* the attribute must be contravariant */ } else if (!mono_is_generic_type_compatible (candidate_type, target_type)) return FALSE; } } return TRUE; } /*Verify if type 'candidate' can be stored in type 'target'. * * If strict, check for the underlying type and not the verification stack types */ static gboolean verify_stack_type_compatibility (VerifyContext *ctx, MonoType *target, MonoType *candidate, gboolean strict) { VERIFIER_DEBUG ( printf ("checking type compatibility %p %p %p\n", ctx, target, candidate); ); /*only one is byref */ if (candidate->byref ^ target->byref) { /* converting from native int to byref*/ if (get_stack_type (candidate) == TYPE_NATIVE_INT && target->byref) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("using byref native int at 0x%04x", ctx->ip_offset)); return TRUE; } return FALSE; } handle_enum: switch (target->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: if (strict) return candidate->type == target->type; return get_stack_type (candidate) == TYPE_I4; case MONO_TYPE_I8: case MONO_TYPE_U8: if (strict) return candidate->type == target->type; return get_stack_type (candidate) == TYPE_I8; case MONO_TYPE_R4: case MONO_TYPE_R8: if (strict) return candidate->type == target->type; return get_stack_type (target) == TYPE_R8; case MONO_TYPE_I: case MONO_TYPE_U: if (strict) return candidate->type == target->type; return get_stack_type (target) == TYPE_NATIVE_INT; case MONO_TYPE_PTR: if (candidate->type != MONO_TYPE_PTR) return FALSE; /* check the underlying type */ return verify_stack_type_compatibility (ctx, target->data.type, candidate->data.type, TRUE); case MONO_TYPE_GENERICINST: { MonoGenericClass *left; MonoGenericClass *right; if (target->type != MONO_TYPE_GENERICINST) return FALSE; left = target->data.generic_class; right = candidate->data.generic_class; return mono_is_generic_instance_compatible (left, right, right); } case MONO_TYPE_STRING: return candidate->type == MONO_TYPE_STRING; case MONO_TYPE_CLASS: if (candidate->type != MONO_TYPE_CLASS) return FALSE; return mono_class_is_assignable_from (target->data.klass, candidate->data.klass); case MONO_TYPE_OBJECT: return MONO_TYPE_IS_REFERENCE (candidate); case MONO_TYPE_SZARRAY: { MonoClass *left; MonoClass *right; if (target->type != MONO_TYPE_SZARRAY) return FALSE; left = target->data.klass; right = candidate->data.klass; return mono_class_is_assignable_from(left, right); } case MONO_TYPE_ARRAY: if (candidate->type != MONO_TYPE_ARRAY) return FALSE; return is_array_type_compatible (target, candidate); //TODO verify aditional checks that needs to be done case MONO_TYPE_TYPEDBYREF: return candidate->type != MONO_TYPE_TYPEDBYREF; case MONO_TYPE_VALUETYPE: if (target->data.klass->enumtype) { target = target->data.klass->enum_basetype; goto handle_enum; } else { if (candidate->type != MONO_TYPE_VALUETYPE) return FALSE; return target->data.klass == candidate->data.klass; } default: VERIFIER_DEBUG ( printf ("unknown store type %d\n", target->type); ); g_assert_not_reached (); return FALSE; } return 1; } /* TODO MONO_TYPE_PTR: TODO MONO_TYPE_FNPTR: */ static int verify_type_compat (VerifyContext *ctx, MonoType *type, ILStackDesc *stack) { int stack_type = stack->stype; VERIFIER_DEBUG ( printf ("checking compatibility %p %p %p\n", ctx, stack, type); ); if (type->byref) { if (stack_type == TYPE_NATIVE_INT) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("using byref native int at 0x%04x", ctx->ip_offset)); return TRUE; } return FALSE; } handle_enum: switch (type->type) { case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_BOOLEAN: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: return stack_type == TYPE_I4; case MONO_TYPE_I8: case MONO_TYPE_U8: return stack_type == TYPE_I8; case MONO_TYPE_R4: case MONO_TYPE_R8: return stack_type == TYPE_R8; case MONO_TYPE_I: case MONO_TYPE_U: return stack_type == TYPE_NATIVE_INT; case MONO_TYPE_PTR: if ((stack_type & POINTER_MASK) == 0) return FALSE; stack_type &= ~POINTER_MASK; if (stack_type != TYPE_COMPLEX) { type = type->data.type; goto handle_enum; } return verify_stack_type_compatibility (ctx, type->data.type, stack->type, TRUE); case MONO_TYPE_GENERICINST: { MonoGenericClass *left; MonoGenericClass *right; if (stack_type != TYPE_COMPLEX) return FALSE; g_assert (stack->type); if (stack->type->type != MONO_TYPE_GENERICINST) return FALSE; left = type->data.generic_class; right = stack->type->data.generic_class; return mono_is_generic_instance_compatible (left, right, right); } case MONO_TYPE_STRING: if (stack_type != TYPE_COMPLEX) return FALSE; g_assert (stack->type); return stack->type->type == MONO_TYPE_STRING; case MONO_TYPE_CLASS: if (stack_type != TYPE_COMPLEX) return FALSE; g_assert (stack->type); if (stack->type->type != MONO_TYPE_CLASS) return FALSE; return mono_class_is_assignable_from (type->data.klass, stack->type->data.klass); case MONO_TYPE_OBJECT: if (stack_type != TYPE_COMPLEX) return FALSE; g_assert (stack->type); return MONO_TYPE_IS_REFERENCE (stack->type); case MONO_TYPE_SZARRAY: { MonoClass *left; MonoClass *right; if (stack_type != TYPE_COMPLEX) return FALSE; g_assert (stack->type); if (stack->type->type != type->type) return FALSE; left = type->data.klass ; right = stack->type->data.klass; return mono_class_is_assignable_from (left, right); } case MONO_TYPE_ARRAY: if (stack_type != TYPE_COMPLEX) return FALSE; g_assert (stack->type); if (stack->type->type != type->type) return FALSE; return is_array_type_compatible (type, stack->type); /*TODO verify aditional checks that needs to be done */ case MONO_TYPE_TYPEDBYREF: if (stack_type != TYPE_COMPLEX) return FALSE; g_assert (stack->type); if (stack->type->type != type->type) return FALSE; return TRUE; case MONO_TYPE_VALUETYPE: if (type->data.klass->enumtype) { type = type->data.klass->enum_basetype; goto handle_enum; } else { if (stack_type != TYPE_COMPLEX) return FALSE; g_assert (stack->type); if (stack->type->type != type->type) return FALSE; return stack->type->data.klass == type->data.klass; } default: printf("unknown store type %d\n", type->type); g_assert_not_reached (); return FALSE; } return 1; } /* implement the opcode checks*/ static void push_arg (VerifyContext *ctx, unsigned int arg, int take_addr) { if (arg >= ctx->max_args) { ADD_VERIFY_ERROR(ctx, g_strdup_printf ("Method doesn't have argument %d", arg + 1)); } else if (check_overflow (ctx)) { set_stack_value (stack_push (ctx), ctx->params [arg], FALSE); } } static void push_local (VerifyContext *ctx, guint32 arg, int take_addr) { if (arg >= ctx->num_locals) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have local %d", arg + 1)); } else if (check_overflow (ctx)) { set_stack_value (stack_push (ctx), ctx->locals [arg], take_addr); } } static void store_arg (VerifyContext *ctx, guint32 arg) { ILStackDesc *value; if (arg >= ctx->max_args) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", arg + 1, ctx->ip_offset)); return; } if (check_underflow (ctx, 1)) { value = stack_pop (ctx); if (!verify_type_compat (ctx, ctx->params [arg], value)) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible type %s in local store at 0x%04x", type_names [value->stype & TYPE_MASK], ctx->ip_offset)); } } } static void store_local (VerifyContext *ctx, guint32 arg) { ILStackDesc *value; if (arg >= ctx->num_locals) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have local var %d at 0x%04x", arg + 1, ctx->ip_offset)); return; } /*TODO verify definite assigment */ if (check_underflow (ctx, 1)) { value = stack_pop(ctx); if (!verify_type_compat (ctx, ctx->locals [arg], value)) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible type %s in local store at 0x%04x", type_names [value->stype & TYPE_MASK], ctx->ip_offset)); } } } static void do_binop (VerifyContext *ctx, unsigned int opcode, const unsigned char table [TYPE_MAX][TYPE_MAX]) { ILStackDesc *a, *b; int idxa, idxb, complexMerge = 0; unsigned char res; if (!check_underflow (ctx, 2)) return; a = stack_get (ctx, 1); b = stack_top (ctx); idxa = a->stype; if (idxa & POINTER_MASK) { idxa = TYPE_PTR; complexMerge = 1; } idxb = b->stype; if (idxb & POINTER_MASK) { idxb = TYPE_PTR; complexMerge = 2; } --idxa; --idxb; res = table [idxa][idxb]; VERIFIER_DEBUG ( printf ("binop res %d\n", res); ); VERIFIER_DEBUG ( printf ("idxa %d idxb %d\n", idxa, idxb); ); if (res == TYPE_INV) { ADD_VERIFY_ERROR(ctx, g_strdup_printf ( "Binary instruction applyed to ill formed stack (%s x %s)", type_names [idxa & TYPE_MASK], type_names [idxb & TYPE_MASK])); return; } if (res & NON_VERIFIABLE_RESULT) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Binary instruction is not verifiable (%s x %s)", type_names [idxa & TYPE_MASK], type_names [idxb & TYPE_MASK])); res = res & ~NON_VERIFIABLE_RESULT; } if (complexMerge && res == TYPE_PTR) { if (complexMerge == 1) copy_stack_value (stack_top (ctx), a); else if (complexMerge == 2) copy_stack_value (stack_top (ctx), b); /* * There is no need to merge the type of two pointers. * The only valid operation is subtraction, that returns a native * int as result and can be used with any 2 pointer kinds. * This is valid acording to Patition III 1.1.4 */ } else stack_top (ctx)->stype = res; ctx->eval.size--; } static void do_boolean_branch_op (VerifyContext *ctx, int delta) { int target = ctx->ip_offset + delta; VERIFIER_DEBUG ( printf ("boolean branch offset %d delta %d target %d\n", ctx->ip_offset, delta, target); ); if (target < 0 || target >= ctx->code_size) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Boolean branch target out of code at 0x%04x", ctx->ip_offset)); return; } if (!in_same_block (ctx->header, ctx->ip_offset, target)) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ctx->ip_offset)); return; } ctx->target = target; if (!check_underflow (ctx, 1)) return; if (!is_valid_bool_arg (stack_pop (ctx))) ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Argument type %s not valid for brtrue/brfalse at 0x%04x", type_names [stack_get (ctx, -1)->stype & TYPE_MASK], ctx->ip_offset)); } static void do_branch_op (VerifyContext *ctx, signed int delta, const unsigned char table [TYPE_MAX][TYPE_MAX]) { ILStackDesc *a, *b; int idxa, idxb; unsigned char res; int target = ctx->ip_offset + delta; VERIFIER_DEBUG ( printf ("branch offset %d delta %d target %d\n", ctx->ip_offset, delta, target); ); if (target < 0 || target >= ctx->code_size) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Branch target out of code at 0x%04x", ctx->ip_offset)); return; } if (!in_same_block (ctx->header, ctx->ip_offset, target)) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ctx->ip_offset)); return; } ctx->target = target; if (!check_underflow (ctx, 2)) return; b = stack_pop (ctx); a = stack_pop (ctx); idxa = a->stype; if (idxa & POINTER_MASK) idxa = TYPE_PTR; idxb = b->stype; if (idxb & POINTER_MASK) idxb = TYPE_PTR; --idxa; --idxb; res = table [idxa][idxb]; VERIFIER_DEBUG ( printf ("branch res %d\n", res); ); VERIFIER_DEBUG ( printf ("idxa %d idxb %d\n", idxa, idxb); ); if (res == TYPE_INV) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Compare and Branch instruction applyed to ill formed stack (%s x %s) at 0x%04x", type_names [idxa & TYPE_MASK], type_names [idxb & TYPE_MASK], ctx->ip_offset)); } else if (res & NON_VERIFIABLE_RESULT) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Compare and Branch instruction is not verifiable (%s x %s) at 0x%04x", type_names [idxa & TYPE_MASK], type_names [idxb & TYPE_MASK], ctx->ip_offset)); res = res & ~NON_VERIFIABLE_RESULT; } } static void do_cmp_op (VerifyContext *ctx, const unsigned char table [TYPE_MAX][TYPE_MAX]) { ILStackDesc *a, *b; int idxa, idxb; unsigned char res; if (!check_underflow (ctx, 2)) return; b = stack_pop (ctx); a = stack_pop (ctx); idxa = a->stype; if (idxa & POINTER_MASK) idxa = TYPE_PTR; idxb = b->stype; if (idxb & POINTER_MASK) idxb = TYPE_PTR; --idxa; --idxb; res = table [idxa][idxb]; printf("binop res %d\n", res); printf("idxa %d idxb %d\n", idxa, idxb); if(res == TYPE_INV) { ADD_VERIFY_ERROR (ctx, g_strdup_printf("Compare instruction applyed to ill formed stack (%s x %s) at 0x%04x", type_names [idxa & TYPE_MASK], type_names [idxb & TYPE_MASK], ctx->ip_offset)); return; } else if (res & NON_VERIFIABLE_RESULT) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Compare instruction is not verifiable (%s x %s) at 0x%04x", type_names [idxa & TYPE_MASK], type_names [idxb & TYPE_MASK], ctx->ip_offset)); res = res & ~NON_VERIFIABLE_RESULT; } stack_push_val (ctx, TYPE_I4, &mono_defaults.int_class->byval_arg); } static void do_ret (VerifyContext *ctx) { VERIFIER_DEBUG ( printf ("checking ret\n"); ); if (ctx->signature->ret->type != MONO_TYPE_VOID) { ILStackDesc *top; if (!check_underflow (ctx, 1)) return; top = stack_pop(ctx); if (!verify_type_compat (ctx, ctx->signature->ret, top)) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible return value on stack with method signature ret at 0x%04x", ctx->ip_offset)); return; } } if (ctx->eval.size > 0) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Stack not empty (%d) after ret at 0x%04x", ctx->eval.size, ctx->ip_offset)); } else if (in_any_block (ctx->header, ctx->ip_offset)) ADD_VERIFY_ERROR (ctx, g_strdup_printf ("ret cannot escape exception blocks at 0x%04x", ctx->ip_offset)); } /* FIXME: we could just load the signature instead of the whole MonoMethod * TODO handle vararg calls * TODO handle non virt calls to non-final virtual calls (from the verifiability clause in page 52 of partition 3) */ static void do_invoke_method (VerifyContext *ctx, int method_token) { int param_count, i; MonoMethodSignature *sig; ILStackDesc *value; MonoMethod *method = mono_get_method_full (ctx->image, method_token, NULL, ctx->generic_context); if (!method) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method 0x%08x not found at 0x%04x", method_token, ctx->ip_offset)); return; } if (!(sig = mono_method_signature (method))) sig = mono_method_get_signature (method, ctx->image, method_token); param_count = sig->param_count + sig->hasthis; if (!check_underflow (ctx, param_count)) return; for (i = sig->param_count - 1; i >= 0; --i) { VERIFIER_DEBUG ( printf ("verifying argument %d\n", i); ); value = stack_pop (ctx); if (!verify_type_compat (ctx, sig->params[i], value)) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Incompatible parameter value with function signature at 0x%04x", ctx->ip_offset)); return; } } if (sig->hasthis) { MonoType * type = method->klass->valuetype ? &method->klass->this_arg : &method->klass->byval_arg; VERIFIER_DEBUG ( printf ("verifying this argument\n"); ); value = stack_pop (ctx); if (!verify_type_compat (ctx, type, value)) { ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Incompatible return value on stack with method signature ret at 0x%04x", ctx->ip_offset)); return; } } if (sig->ret->type != MONO_TYPE_VOID) { if (check_overflow (ctx)) set_stack_value (stack_push (ctx), sig->ret, FALSE); } } /*Merge the stacks and perform compat checks*/ static void merge_stacks (VerifyContext *ctx, ILCodeDesc *from, ILCodeDesc *to, int start) { int i; if (to->flags == IL_CODE_FLAG_NOT_PROCESSED) stack_init (ctx, to); if (start) { if (to->flags == IL_CODE_FLAG_NOT_PROCESSED) from->size = 0; else stack_copy (&ctx->eval, to); goto end_verify; } else if (to->flags == IL_CODE_FLAG_NOT_PROCESSED) { stack_copy (to, from); goto end_verify; } VERIFIER_DEBUG ( printf ("performing stack merge %d x %d\n", from->size, to->size); ); if (from->size != to->size) { VERIFIER_DEBUG ( printf ("diferent stack sizes %d x %d\n", from->size, to->size); ); CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Could not merge stacks, diferent sizes (%d x %d)", from->size, to->size)); goto end_verify; } for (i = 0; i < from->size; ++i) { ILStackDesc *from_slot = from->stack + i; ILStackDesc *to_slot = to->stack + i; int from_stype = from_slot->stype; int to_stype = to_slot->stype; if (from_stype != to_stype) { VERIFIER_DEBUG ( printf ("diferent stack types %d x %d\n", from_stype, to_stype); ); CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Could not merge stacks, diferent verification types (%s x %s)", type_names [from_stype & TYPE_MASK], type_names [to_stype & TYPE_MASK])); goto end_verify; } if (from_stype & POINTER_MASK) { from_stype &= ~POINTER_MASK; to_stype &= ~POINTER_MASK; if (from_slot->type && !verify_stack_type_compatibility (ctx, to_slot->type, from_slot->type, TRUE)) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Could not merge stacks, pointer types not compatible")); goto end_verify; } else copy_stack_value (to_slot, from_slot); continue; } if (from_stype == TYPE_COMPLEX) { if (!to->stack [i].type) { ctx->verifiable = 0; g_assert (0); } else if (!verify_type_compat (ctx, to_slot->type, from_slot)) { CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Could not merge stacks, types not compatible")); goto end_verify; } else { /*TODO we need to choose the base class for merging */ copy_stack_value (to_slot, from_slot); } continue; } copy_stack_value (to_slot, from_slot); } end_verify: to->flags = IL_CODE_FLAG_SEEN; } /* * FIXME: need to distinguish between valid and verifiable. * Need to keep track of types on the stack. * Verify types for opcodes. */ GSList* mono_method_verify (MonoMethod *method, int level) { MonoMethodSignature *csig; const unsigned char *ip; const unsigned char *end; const unsigned char *target = NULL; /* branch target */ int i, n, need_merge = 0, start = 0; guint token, ip_offset = 0, prefix = 0; MonoClass *klass; MonoMethod *cmethod; MonoClassField *field; MonoGenericContext *generic_context = NULL; MonoImage *image; VerifyContext ctx; if (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME) || (method->flags & (METHOD_ATTRIBUTE_PINVOKE_IMPL | METHOD_ATTRIBUTE_ABSTRACT))) { return NULL; } memset (&ctx, 0, sizeof (VerifyContext)); ctx.signature = mono_method_signature (method); ctx.header = mono_method_get_header (method); ip = ctx.header->code; end = ip + ctx.header->code_size; ctx.image = image = method->klass->image; ctx.max_args = ctx.signature->param_count + ctx.signature->hasthis; ctx.max_stack = ctx.header->max_stack; ctx.verifiable = ctx.valid = 1; ctx.code = g_new0 (ILCodeDesc, ctx.header->code_size); ctx.code_size = ctx.header->code_size; memset(ctx.code, 0, sizeof (ILCodeDesc) * ctx.header->code_size); ctx.num_locals = ctx.header->num_locals; ctx.locals = ctx.header->locals; if (ctx.signature->hasthis) { ctx.params = g_new0 (MonoType*, ctx.max_args); ctx.params [0] = &method->klass->this_arg; memcpy (ctx.params + 1, ctx.signature->params, sizeof (MonoType *) * ctx.signature->param_count); } else { ctx.params = ctx.signature->params; } if (ctx.signature->is_inflated) ctx.generic_context = generic_context = mono_method_get_context (method); stack_init(&ctx, &ctx.eval); /* TODO implement exception entry for (i = 0; i < header->num_clauses; ++i) { MonoExceptionClause *clause = &header->clauses [i]; // catch blocks have the exception on the stack. if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE) { code [clause->handler_offset].size = 1; code [clause->handler_offset].flags |= IL_CODE_FLAG_SEEN; } }*/ while (ip < end && ctx.valid) { ctx.ip_offset = ip_offset = ip - ctx.header->code; /*TODO id stack merge fails, we break, should't we - or only on errors?? TODO verify need_merge */ if (need_merge) { VERIFIER_DEBUG ( printf ("extra merge needed! %d \n", ctx.target); ); merge_stacks (&ctx, &ctx.eval, &ctx.code [ctx.target], FALSE); need_merge = 0; } merge_stacks (&ctx, &ctx.eval, &ctx.code[ip_offset], start); start = 0; /*TODO rename to zero */ #if 1 { char *discode; discode = mono_disasm_code_one (NULL, method, ip, NULL); discode [strlen (discode) - 1] = 0; /* no \n */ g_print ("[%d] %-29s (%d)\n", ip_offset, discode, ctx.eval.size); g_free (discode); } dump_stack_state(&ctx.code[ip_offset]); #endif switch (*ip) { case CEE_NOP: case CEE_BREAK: ++ip; break; case CEE_LDARG_0: case CEE_LDARG_1: case CEE_LDARG_2: case CEE_LDARG_3: push_arg (&ctx, *ip - CEE_LDARG_0, FALSE); ++ip; break; case CEE_LDARG_S: case CEE_LDARGA_S: push_arg (&ctx, ip [1], *ip == CEE_LDARGA_S); ip += 2; break; case CEE_ADD: do_binop (&ctx, *ip, add_table); ++ip; break; case CEE_SUB: do_binop (&ctx, *ip, sub_table); ++ip; break; case CEE_MUL: case CEE_DIV: case CEE_REM: do_binop (&ctx, *ip, bin_op_table); ++ip; break; case CEE_AND: case CEE_DIV_UN: case CEE_OR: case CEE_REM_UN: case CEE_XOR: do_binop (&ctx, *ip, int_bin_op_table); ++ip; break; case CEE_SHL: case CEE_SHR: case CEE_SHR_UN: do_binop (&ctx, *ip, shift_op_table); ++ip; break; case CEE_POP: if (!check_underflow (&ctx, 1)) break; stack_pop (&ctx); ++ip; break; case CEE_RET: do_ret (&ctx); ++ip; start = 1; break; case CEE_LDLOC_0: case CEE_LDLOC_1: case CEE_LDLOC_2: case CEE_LDLOC_3: /*TODO support definite assignment verification? */ push_local (&ctx, *ip - CEE_LDLOC_0, FALSE); ++ip; break; case CEE_STLOC_0: case CEE_STLOC_1: case CEE_STLOC_2: case CEE_STLOC_3: store_local (&ctx, *ip - CEE_STLOC_0); ++ip; break; case CEE_STLOC_S: store_local (&ctx, ip [1]); ip += 2; break; case CEE_STARG_S: store_arg (&ctx, ip [1]); ip += 2; break; case CEE_LDC_I4_M1: case CEE_LDC_I4_0: case CEE_LDC_I4_1: case CEE_LDC_I4_2: case CEE_LDC_I4_3: case CEE_LDC_I4_4: case CEE_LDC_I4_5: case CEE_LDC_I4_6: case CEE_LDC_I4_7: case CEE_LDC_I4_8: if (check_overflow (&ctx)) stack_push_val (&ctx, TYPE_I4, &mono_defaults.int_class->byval_arg); ++ip; break; case CEE_LDC_I4_S: if (check_overflow (&ctx)) stack_push_val (&ctx, TYPE_I4, &mono_defaults.int_class->byval_arg); ip += 2; break; case CEE_LDC_I4: if (check_overflow (&ctx)) stack_push_val (&ctx,TYPE_I4, &mono_defaults.int_class->byval_arg); ip += 5; break; case CEE_LDC_I8: if (check_overflow (&ctx)) stack_push_val (&ctx,TYPE_I8, &mono_defaults.int64_class->byval_arg); ip += 9; break; case CEE_LDC_R4: if (check_overflow (&ctx)) stack_push_val (&ctx, TYPE_R8, &mono_defaults.double_class->byval_arg); ip += 5; break; case CEE_LDC_R8: if (check_overflow (&ctx)) stack_push_val (&ctx, TYPE_R8, &mono_defaults.double_class->byval_arg); ip += 9; break; case CEE_LDNULL: if (check_overflow (&ctx)) stack_push_val (&ctx,TYPE_COMPLEX, &mono_defaults.object_class->byval_arg); ++ip; break; case CEE_BEQ_S: case CEE_BNE_UN_S: do_branch_op (&ctx, (signed char)ip [1] + 2, cmp_br_eq_op); ip += 2; need_merge = 1; break; case CEE_BGE_S: case CEE_BGT_S: case CEE_BLE_S: case CEE_BLT_S: case CEE_BGE_UN_S: case CEE_BGT_UN_S: case CEE_BLE_UN_S: case CEE_BLT_UN_S: do_branch_op (&ctx, (signed char)ip [1] + 2, cmp_br_op); ip += 2; need_merge = 1; break; case CEE_BEQ: case CEE_BNE_UN: do_branch_op (&ctx, (gint32)read32 (ip + 1) + 5, cmp_br_eq_op); ip += 5; need_merge = 1; break; case CEE_BGE: case CEE_BGT: case CEE_BLE: case CEE_BLT: case CEE_BGE_UN: case CEE_BGT_UN: case CEE_BLE_UN: case CEE_BLT_UN: do_branch_op (&ctx, (gint32)read32 (ip + 1) + 5, cmp_br_op); ip += 5; need_merge = 1; break; case CEE_LDLOC_S: case CEE_LDLOCA_S: push_local (&ctx, ip[1], *ip == CEE_LDLOCA_S); ip += 2; break; /* FIXME: warn/error instead? */ case CEE_UNUSED99: ++ip; break; case CEE_DUP: if (!check_underflow (&ctx, 1)) break; if (!check_overflow (&ctx)) break; copy_stack_value (stack_push (&ctx), stack_get (&ctx, 1)); ++ip; break; case CEE_JMP: if (ctx.eval.size) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Eval stack must be empty in jmp at 0x%04x", ip_offset)); token = read32 (ip + 1); if (in_any_block (ctx.header, ip_offset)) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("jmp cannot escape exception blocks at 0x%04x", ip_offset)); /* * FIXME: check signature, retval, arguments etc. */ ip += 5; break; case CEE_CALL: case CEE_CALLVIRT: do_invoke_method (&ctx, read32 (ip + 1)); ip += 5; break; case CEE_CALLI: token = read32 (ip + 1); /* * FIXME: check signature, retval, arguments etc. */ ip += 5; break; case CEE_BR_S: target = ip + (signed char)ip [1] + 2; if (target >= end || target < ctx.header->code) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset)); if (!in_same_block (ctx.header, ip_offset, target - ctx.header->code)) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ip_offset)); ip += 2; start = 1; break; case CEE_BRFALSE_S: case CEE_BRTRUE_S: do_boolean_branch_op (&ctx, (signed char)ip [1] + 2); ip += 2; need_merge = 1; break; case CEE_BR: target = ip + (gint32)read32 (ip + 1) + 5; if (target >= end || target < ctx.header->code) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset)); if (!in_same_block (ctx.header, ip_offset, target - ctx.header->code)) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch target escapes out of exception block at 0x%04x", ip_offset)); ip += 5; start = 1; break; case CEE_BRFALSE: case CEE_BRTRUE: do_boolean_branch_op (&ctx, (gint32)read32 (ip + 1) + 5); ip += 5; need_merge = 1; break; case CEE_SWITCH: n = read32 (ip + 1); target = ip + sizeof (guint32) * n; /* FIXME: check that ip is in range (and within the same exception block) */ for (i = 0; i < n; ++i) if (target + (gint32) read32 (ip + 5 + i * sizeof (gint32)) >= end || target + (gint32) read32 (ip + 5 + i * sizeof (gint32)) < ctx.header->code) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset)); if (!check_underflow (&ctx, 1)) break; if (stack_pop (&ctx)->stype != TYPE_I4) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument to switch at 0x%04x", ip_offset)); ip += 5 + sizeof (guint32) * n; break; case CEE_LDIND_I1: case CEE_LDIND_U1: case CEE_LDIND_I2: case CEE_LDIND_U2: case CEE_LDIND_I4: case CEE_LDIND_U4: case CEE_LDIND_I8: case CEE_LDIND_I: case CEE_LDIND_R4: case CEE_LDIND_R8: case CEE_LDIND_REF: if (!check_underflow (&ctx, 1)) break; if (stack_top (&ctx)->stype != TYPE_PTR && stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument to ldind at 0x%04x", ip_offset)); stack_top (&ctx)->stype = ldind_type [*ip - CEE_LDIND_I1]; ++ip; break; case CEE_STIND_REF: case CEE_STIND_I1: case CEE_STIND_I2: case CEE_STIND_I4: case CEE_STIND_I8: case CEE_STIND_R4: case CEE_STIND_R8: if (!check_underflow (&ctx, 2)) break; ctx.eval.size -= 2; if (stack_top (&ctx)->stype != TYPE_PTR && stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid pointer argument to stind at 0x%04x", ip_offset)); if (!stind_type (*ip, stack_get (&ctx, -1)->stype)) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Incompatible value argument to stind at 0x%04x", ip_offset)); ++ip; break; case CEE_NEG: case CEE_NOT: case CEE_CONV_I1: case CEE_CONV_I2: case CEE_CONV_I4: case CEE_CONV_I8: case CEE_CONV_R4: case CEE_CONV_R8: case CEE_CONV_U4: case CEE_CONV_U8: if (!check_underflow (&ctx, 1)) break; if (type_from_op (*ip, stack_top (&ctx)) == TYPE_INV) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid arguments to opcode 0x%02x at 0x%04x", *ip, ip_offset)); ++ip; break; case CEE_CPOBJ: token = read32 (ip + 1); if (!check_underflow (&ctx, 2)) break; ctx.eval.size -= 2; ip += 5; break; case CEE_LDOBJ: token = read32 (ip + 1); if (!check_underflow (&ctx, 1)) break; if (stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument to ldobj at 0x%04x", ip_offset)); klass = mono_class_get_full (image, token, generic_context); if (!klass) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot load class from token 0x%08x at 0x%04x", token, ip_offset)); if (!klass->valuetype) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Class is not a valuetype at 0x%04x", ip_offset)); stack_top (&ctx)->stype = TYPE_COMPLEX; stack_top (&ctx)->type = &klass->byval_arg; ip += 5; break; case CEE_LDSTR: /*TODO verify if token is a valid string literal*/ token = read32 (ip + 1); if (check_overflow (&ctx)) stack_push_val (&ctx, TYPE_COMPLEX, &mono_defaults.string_class->byval_arg); ip += 5; break; case CEE_NEWOBJ: token = read32 (ip + 1); /* * FIXME: we could just load the signature ... */ cmethod = mono_get_method_full (image, token, NULL, generic_context); if (!cmethod) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Constructor 0x%08x not found at 0x%04x", token, ip_offset)); csig = mono_method_signature (cmethod); if (!check_underflow (&ctx, csig->param_count)) break; ctx.eval.size -= csig->param_count; if (check_overflow (&ctx)) stack_push_val (&ctx, cmethod->klass->valuetype? TYPE_COMPLEX: TYPE_COMPLEX, &cmethod->klass->byval_arg); ip += 5; break; case CEE_CASTCLASS: case CEE_ISINST: token = read32 (ip + 1); if (!check_underflow (&ctx, 1)) break; ip += 5; break; case CEE_CONV_R_UN: if (!check_underflow (&ctx, 1)) break; ++ip; break; case CEE_UNUSED58: case CEE_UNUSED1: ++ip; /* warn, error ? */ break; case CEE_UNBOX: token = read32 (ip + 1); if (!check_underflow (&ctx, 1)) break; if (stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument %s to unbox at 0x%04x", type_names [stack_top (&ctx)->stype], ip_offset)); stack_top (&ctx)->stype = TYPE_COMPLEX; stack_top (&ctx)->type = NULL; ip += 5; break; case CEE_THROW: if (!check_underflow (&ctx, 1)) break; stack_pop (&ctx); ++ip; start = 1; break; case CEE_LDFLD: if (!check_underflow (&ctx, 1)) break; if (stack_top (&ctx)->stype != TYPE_COMPLEX && stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument %s to ldfld at 0x%04x", type_names [stack_top (&ctx)->stype], ip_offset)); token = read32 (ip + 1); field = mono_field_from_token (image, token, &klass, generic_context); if (!field) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot load field from token 0x%08x at 0x%04x", token, ip_offset)); type_to_eval_stack_type (field->type, stack_top (&ctx), FALSE); ip += 5; break; case CEE_LDFLDA: if (!check_underflow (&ctx, 1)) break; if (stack_top (&ctx)->stype != TYPE_COMPLEX && stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument to ldflda at 0x%04x", ip_offset)); token = read32 (ip + 1); field = mono_field_from_token (image, token, &klass, generic_context); if (!field) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot load field from token 0x%08x at 0x%04x", token, ip_offset)); type_to_eval_stack_type (field->type, stack_top (&ctx), TRUE); ip += 5; break; case CEE_STFLD: if (!check_underflow (&ctx, 2)) break; ctx.eval.size -= 2; if (stack_top (&ctx)->stype != TYPE_COMPLEX && stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument to stfld at 0x%04x", ip_offset)); token = read32 (ip + 1); field = mono_field_from_token (image, token, &klass, generic_context); if (!field) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot load field from token 0x%08x at 0x%04x", token, ip_offset)); /* can_store */ ip += 5; break; case CEE_LDSFLD: if (!check_overflow (&ctx)) break; token = read32 (ip + 1); field = mono_field_from_token (image, token, &klass, generic_context); if (!field) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot load field from token 0x%08x at 0x%04x", token, ip_offset)); type_to_eval_stack_type (field->type, stack_top (&ctx), FALSE); ++ctx.eval.size; ip += 5; break; case CEE_LDSFLDA: if (!check_overflow (&ctx)) break; token = read32 (ip + 1); field = mono_field_from_token (image, token, &klass, generic_context); if (!field) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot load field from token 0x%08x at 0x%04x", token, ip_offset)); type_to_eval_stack_type (field->type, stack_top (&ctx), TRUE); ++ctx.eval.size; ip += 5; break; case CEE_STSFLD: if (!check_underflow (&ctx, 1)) break; stack_pop (&ctx); token = read32 (ip + 1); field = mono_field_from_token (image, token, &klass, generic_context); if (!field) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot load field from token 0x%08x at 0x%04x", token, ip_offset)); /* can store */ ip += 5; break; case CEE_STOBJ: if (!check_underflow (&ctx, 2)) break; ctx.eval.size -= 2; token = read32 (ip + 1); ip += 5; break; case CEE_CONV_OVF_I1_UN: case CEE_CONV_OVF_I2_UN: case CEE_CONV_OVF_I4_UN: case CEE_CONV_OVF_I8_UN: case CEE_CONV_OVF_U1_UN: case CEE_CONV_OVF_U2_UN: case CEE_CONV_OVF_U4_UN: case CEE_CONV_OVF_U8_UN: case CEE_CONV_OVF_I_UN: case CEE_CONV_OVF_U_UN: if (!check_underflow (&ctx, 1)) break; if (type_from_op (*ip, stack_top (&ctx)) == TYPE_INV) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid arguments to opcode 0x%02x at 0x%04x", *ip, ip_offset)); ++ip; break; case CEE_BOX: if (!check_underflow (&ctx, 1)) break; token = read32 (ip + 1); if ( stack_top (&ctx)->stype == TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument %s to box at 0x%04x", type_names [stack_top (&ctx)->stype], ip_offset)); stack_top (&ctx)->stype = TYPE_COMPLEX; ip += 5; break; case CEE_NEWARR: if (!check_underflow (&ctx, 1)) break; token = read32 (ip + 1); stack_top (&ctx)->stype = TYPE_COMPLEX; ip += 5; break; case CEE_LDLEN: if (!check_underflow (&ctx, 1)) break; if (stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument to ldlen at 0x%04x", ip_offset)); stack_top (&ctx)->type = &mono_defaults.int_class->byval_arg; /* FIXME: use a native int type */ stack_top (&ctx)->stype = TYPE_PTR; ++ip; break; case CEE_LDELEMA: if (check_underflow (&ctx, 2)) break; --ctx.eval.size; if (stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid array argument to ldelema at 0x%04x", ip_offset)); if (stack_top (&ctx)->stype != TYPE_I4 && stack_top (&ctx)->stype != TYPE_PTR) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Array index needs to be Int32 or IntPtr at 0x%04x", ip_offset)); stack_top (&ctx)->stype = TYPE_COMPLEX; token = read32 (ip + 1); ip += 5; break; case CEE_LDELEM_I1: case CEE_LDELEM_U1: case CEE_LDELEM_I2: case CEE_LDELEM_U2: case CEE_LDELEM_I4: case CEE_LDELEM_U4: case CEE_LDELEM_I8: case CEE_LDELEM_I: case CEE_LDELEM_R4: case CEE_LDELEM_R8: case CEE_LDELEM_REF: if (!check_underflow (&ctx, 2)) break; --ctx.eval.size; if (stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid array argument to ldelem at 0x%04x", ip_offset)); if (stack_top (&ctx)->stype != TYPE_I4 && stack_top (&ctx)->stype != TYPE_PTR) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Array index needs to be Int32 or IntPtr at 0x%04x", ip_offset)); stack_top (&ctx)->stype = ldelem_type [*ip - CEE_LDELEM_I1]; ++ip; break; case CEE_STELEM_I: case CEE_STELEM_I1: case CEE_STELEM_I2: case CEE_STELEM_I4: case CEE_STELEM_I8: case CEE_STELEM_R4: case CEE_STELEM_R8: case CEE_STELEM_REF: if (!check_underflow (&ctx, 3)) break; ctx.eval.size -= 3; ++ip; break; case CEE_LDELEM_ANY: case CEE_STELEM_ANY: case CEE_UNBOX_ANY: case CEE_UNUSED5: case CEE_UNUSED6: case CEE_UNUSED7: case CEE_UNUSED8: case CEE_UNUSED9: case CEE_UNUSED10: case CEE_UNUSED11: case CEE_UNUSED12: case CEE_UNUSED13: case CEE_UNUSED14: case CEE_UNUSED15: case CEE_UNUSED16: case CEE_UNUSED17: ++ip; /* warn, error ? */ break; case CEE_CONV_OVF_I1: case CEE_CONV_OVF_U1: case CEE_CONV_OVF_I2: case CEE_CONV_OVF_U2: case CEE_CONV_OVF_I4: case CEE_CONV_OVF_U4: case CEE_CONV_OVF_I8: case CEE_CONV_OVF_U8: if (!check_underflow (&ctx, 1)) break; if (type_from_op (*ip, stack_top (&ctx)) == TYPE_INV) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid arguments to opcode 0x%02x at 0x%04x", *ip, ip_offset)); ++ip; break; case CEE_UNUSED50: case CEE_UNUSED18: case CEE_UNUSED19: case CEE_UNUSED20: case CEE_UNUSED21: case CEE_UNUSED22: case CEE_UNUSED23: ++ip; /* warn, error ? */ break; case CEE_REFANYVAL: if (!check_underflow (&ctx, 1)) break; ++ip; break; case CEE_CKFINITE: if (!check_underflow (&ctx, 1)) break; ++ip; break; case CEE_UNUSED24: case CEE_UNUSED25: ++ip; /* warn, error ? */ break; case CEE_MKREFANY: if (!check_underflow (&ctx, 1)) break; token = read32 (ip + 1); ip += 5; break; case CEE_UNUSED59: case CEE_UNUSED60: case CEE_UNUSED61: case CEE_UNUSED62: case CEE_UNUSED63: case CEE_UNUSED64: case CEE_UNUSED65: case CEE_UNUSED66: case CEE_UNUSED67: ++ip; /* warn, error ? */ break; case CEE_LDTOKEN: if (!check_overflow (&ctx)) break; token = read32 (ip + 1); ++ctx.eval.size; ip += 5; break; case CEE_CONV_U2: case CEE_CONV_U1: case CEE_CONV_I: case CEE_CONV_OVF_I: case CEE_CONV_OVF_U: if (!check_underflow (&ctx, 1)) break; if (type_from_op (*ip, stack_top (&ctx)) == TYPE_INV) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid arguments to opcode 0x%02x at 0x%04x", *ip, ip_offset)); ++ip; break; case CEE_ADD_OVF: case CEE_ADD_OVF_UN: case CEE_MUL_OVF: case CEE_MUL_OVF_UN: case CEE_SUB_OVF: case CEE_SUB_OVF_UN: if (!check_underflow (&ctx, 2)) break; stack_pop (&ctx); ++ip; break; case CEE_ENDFINALLY: ++ip; start = 1; break; case CEE_LEAVE: target = ip + (gint32)read32(ip + 1) + 5; if (target >= end || target < ctx.header->code) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset)); if (!is_correct_leave (ctx.header, ip_offset, target - ctx.header->code)) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Leave not allowed in finally block at 0x%04x", ip_offset)); ip += 5; start = 1; break; case CEE_LEAVE_S: target = ip + (signed char)ip [1] + 2; if (target >= end || target < ctx.header->code) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch target out of code at 0x%04x", ip_offset)); if (!is_correct_leave (ctx.header, ip_offset, target - ctx.header->code)) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Leave not allowed in finally block at 0x%04x", ip_offset)); ip += 2; start = 1; break; case CEE_STIND_I: if (!check_underflow (&ctx, 2)) break; ctx.eval.size -= 2; ++ip; break; case CEE_CONV_U: if (!check_underflow (&ctx, 1)) break; ++ip; break; case CEE_UNUSED26: case CEE_UNUSED27: case CEE_UNUSED28: case CEE_UNUSED29: case CEE_UNUSED30: case CEE_UNUSED31: case CEE_UNUSED32: case CEE_UNUSED33: case CEE_UNUSED34: case CEE_UNUSED35: case CEE_UNUSED36: case CEE_UNUSED37: case CEE_UNUSED38: case CEE_UNUSED39: case CEE_UNUSED40: case CEE_UNUSED41: case CEE_UNUSED42: case CEE_UNUSED43: case CEE_UNUSED44: case CEE_UNUSED45: case CEE_UNUSED46: case CEE_UNUSED47: case CEE_UNUSED48: ++ip; break; case CEE_PREFIX7: case CEE_PREFIX6: case CEE_PREFIX5: case CEE_PREFIX4: case CEE_PREFIX3: case CEE_PREFIX2: case CEE_PREFIXREF: ++ip; break; case CEE_PREFIX1: ++ip; switch (*ip) { case CEE_STLOC: store_local (&ctx, read16 (ip + 1)); ip += 3; break; case CEE_CEQ: do_cmp_op (&ctx, cmp_br_eq_op); ++ip; break; case CEE_CGT: case CEE_CGT_UN: case CEE_CLT: case CEE_CLT_UN: do_cmp_op (&ctx, cmp_br_op); ++ip; break; case CEE_STARG: store_arg (&ctx, read16 (ip + 1) ); ip += 3; break; case CEE_ARGLIST: check_overflow (&ctx); ++ip; case CEE_LDFTN: if (!check_overflow (&ctx)) break; token = read32 (ip + 1); ip += 5; stack_top (&ctx)->stype = TYPE_PTR; ctx.eval.size++; break; case CEE_LDVIRTFTN: if (!check_underflow (&ctx, 1)) break; token = read32 (ip + 1); ip += 5; if (stack_top (&ctx)->stype != TYPE_COMPLEX) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument to ldvirtftn at 0x%04x", ip_offset)); stack_top (&ctx)->stype = TYPE_PTR; break; case CEE_UNUSED56: ++ip; break; case CEE_LDARG: case CEE_LDARGA: push_arg (&ctx, read16 (ip + 1), *ip == CEE_LDARGA); ip += 3; break; case CEE_LDLOC: case CEE_LDLOCA: push_local (&ctx, read16 (ip + 1), *ip == CEE_LDLOCA); ip += 3; break; case CEE_LOCALLOC: if (ctx.eval.size != 1) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Stack must have only size item in localloc at 0x%04x", ip_offset)); if (stack_top (&ctx)->stype != TYPE_I4 && stack_top (&ctx)->stype != TYPE_PTR) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid argument to localloc at 0x%04x", ip_offset)); stack_top (&ctx)->stype = TYPE_COMPLEX; ++ip; break; case CEE_UNUSED57: ++ip; break; case CEE_ENDFILTER: if (ctx.eval.size != 1) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Stack must have only filter result in endfilter at 0x%04x", ip_offset)); ++ip; break; case CEE_UNALIGNED_: prefix |= PREFIX_UNALIGNED; ++ip; break; case CEE_VOLATILE_: prefix |= PREFIX_VOLATILE; ++ip; break; case CEE_TAIL_: prefix |= PREFIX_TAIL; ++ip; if (ip < end && (*ip != CEE_CALL && *ip != CEE_CALLI && *ip != CEE_CALLVIRT)) ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("tail prefix must be used only with call opcodes at 0x%04x", ip_offset)); break; case CEE_INITOBJ: if (!check_underflow (&ctx, 1)) break; token = read32 (ip + 1); ip += 5; stack_pop (&ctx); break; case CEE_CONSTRAINED_: token = read32 (ip + 1); ip += 5; break; case CEE_CPBLK: if (!check_underflow (&ctx, 3)) break; ip++; break; case CEE_INITBLK: if (!check_underflow (&ctx, 3)) break; ip++; break; case CEE_NO_: ip += 2; break; case CEE_RETHROW: ++ip; break; case CEE_UNUSED: ++ip; break; case CEE_SIZEOF: if (!check_overflow (&ctx)) break; token = read32 (ip + 1); ip += 5; stack_top (&ctx)->type = &mono_defaults.uint_class->byval_arg; stack_top (&ctx)->stype = TYPE_I4; ctx.eval.size++; break; case CEE_REFANYTYPE: if (!check_underflow (&ctx, 1)) break; ++ip; break; case CEE_UNUSED53: case CEE_UNUSED54: case CEE_UNUSED55: case CEE_UNUSED70: ++ip; break; } } } /* * if ip != end we overflowed: mark as error. */ if ((ip != end || !start) && ctx.verifiable && !ctx.list) { ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Run ahead of method code at 0x%04x", ip_offset)); } invalid_cil: if (ctx.code) { for (i = 0; i < ctx.header->code_size; ++i) { if (ctx.code [i].stack) g_free (ctx.code [i].stack); } } g_free (ctx.eval.stack); g_free (ctx.code); if (ctx.signature->hasthis) g_free (ctx.params); return ctx.list; } typedef struct { const char *name; guint64 offset; } FieldDesc; typedef struct { const char *name; const FieldDesc *fields; } ClassDesc; static const FieldDesc typebuilder_fields[] = { {"tname", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, name)}, {"nspace", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, nspace)}, {"parent", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, parent)}, {"interfaces", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, interfaces)}, {"methods", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, methods)}, {"properties", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, properties)}, {"fields", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, fields)}, {"attrs", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, attrs)}, {"table_idx", G_STRUCT_OFFSET (MonoReflectionTypeBuilder, table_idx)}, {NULL, 0} }; static const FieldDesc modulebuilder_fields[] = { {"types", G_STRUCT_OFFSET (MonoReflectionModuleBuilder, types)}, {"cattrs", G_STRUCT_OFFSET (MonoReflectionModuleBuilder, cattrs)}, {"guid", G_STRUCT_OFFSET (MonoReflectionModuleBuilder, guid)}, {"table_idx", G_STRUCT_OFFSET (MonoReflectionModuleBuilder, table_idx)}, {NULL, 0} }; static const FieldDesc assemblybuilder_fields[] = { {"entry_point", G_STRUCT_OFFSET (MonoReflectionAssemblyBuilder, entry_point)}, {"modules", G_STRUCT_OFFSET (MonoReflectionAssemblyBuilder, modules)}, {"name", G_STRUCT_OFFSET (MonoReflectionAssemblyBuilder, name)}, {"resources", G_STRUCT_OFFSET (MonoReflectionAssemblyBuilder, resources)}, {"version", G_STRUCT_OFFSET (MonoReflectionAssemblyBuilder, version)}, {"culture", G_STRUCT_OFFSET (MonoReflectionAssemblyBuilder, culture)}, {NULL, 0} }; static const FieldDesc ctorbuilder_fields[] = { {"ilgen", G_STRUCT_OFFSET (MonoReflectionCtorBuilder, ilgen)}, {"parameters", G_STRUCT_OFFSET (MonoReflectionCtorBuilder, parameters)}, {"attrs", G_STRUCT_OFFSET (MonoReflectionCtorBuilder, attrs)}, {"iattrs", G_STRUCT_OFFSET (MonoReflectionCtorBuilder, iattrs)}, {"table_idx", G_STRUCT_OFFSET (MonoReflectionCtorBuilder, table_idx)}, {"call_conv", G_STRUCT_OFFSET (MonoReflectionCtorBuilder, call_conv)}, {"type", G_STRUCT_OFFSET (MonoReflectionCtorBuilder, type)}, {NULL, 0} }; static const FieldDesc methodbuilder_fields[] = { {"mhandle", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, mhandle)}, {"rtype", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, rtype)}, {"parameters", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, parameters)}, {"attrs", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, attrs)}, {"iattrs", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, iattrs)}, {"name", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, name)}, {"table_idx", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, table_idx)}, {"code", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, code)}, {"ilgen", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, ilgen)}, {"type", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, type)}, {"pinfo", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, pinfo)}, {"pi_dll", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, dll)}, {"pi_entry", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, dllentry)}, {"charset", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, charset)}, {"extra_flags", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, extra_flags)}, {"native_cc", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, native_cc)}, {"call_conv", G_STRUCT_OFFSET (MonoReflectionMethodBuilder, call_conv)}, {NULL, 0} }; static const FieldDesc fieldbuilder_fields[] = { {"attrs", G_STRUCT_OFFSET (MonoReflectionFieldBuilder, attrs)}, {"type", G_STRUCT_OFFSET (MonoReflectionFieldBuilder, type)}, {"name", G_STRUCT_OFFSET (MonoReflectionFieldBuilder, name)}, {"def_value", G_STRUCT_OFFSET (MonoReflectionFieldBuilder, def_value)}, {"offset", G_STRUCT_OFFSET (MonoReflectionFieldBuilder, offset)}, {"table_idx", G_STRUCT_OFFSET (MonoReflectionFieldBuilder, table_idx)}, {NULL, 0} }; static const FieldDesc propertybuilder_fields[] = { {"attrs", G_STRUCT_OFFSET (MonoReflectionPropertyBuilder, attrs)}, {"name", G_STRUCT_OFFSET (MonoReflectionPropertyBuilder, name)}, {"type", G_STRUCT_OFFSET (MonoReflectionPropertyBuilder, type)}, {"parameters", G_STRUCT_OFFSET (MonoReflectionPropertyBuilder, parameters)}, {"def_value", G_STRUCT_OFFSET (MonoReflectionPropertyBuilder, def_value)}, {"set_method", G_STRUCT_OFFSET (MonoReflectionPropertyBuilder, set_method)}, {"get_method", G_STRUCT_OFFSET (MonoReflectionPropertyBuilder, get_method)}, {"table_idx", G_STRUCT_OFFSET (MonoReflectionPropertyBuilder, table_idx)}, {NULL, 0} }; static const FieldDesc ilgenerator_fields[] = { {"code", G_STRUCT_OFFSET (MonoReflectionILGen, code)}, {"code_len", G_STRUCT_OFFSET (MonoReflectionILGen, code_len)}, {"max_stack", G_STRUCT_OFFSET (MonoReflectionILGen, max_stack)}, {"cur_stack", G_STRUCT_OFFSET (MonoReflectionILGen, cur_stack)}, {"locals", G_STRUCT_OFFSET (MonoReflectionILGen, locals)}, {"ex_handlers", G_STRUCT_OFFSET (MonoReflectionILGen, ex_handlers)}, {NULL, 0} }; static const FieldDesc ilexinfo_fields[] = { {"handlers", G_STRUCT_OFFSET (MonoILExceptionInfo, handlers)}, {"start", G_STRUCT_OFFSET (MonoILExceptionInfo, start)}, {"len", G_STRUCT_OFFSET (MonoILExceptionInfo, len)}, {"end", G_STRUCT_OFFSET (MonoILExceptionInfo, label)}, {NULL, 0} }; static const FieldDesc ilexblock_fields[] = { {"extype", G_STRUCT_OFFSET (MonoILExceptionBlock, extype)}, {"type", G_STRUCT_OFFSET (MonoILExceptionBlock, type)}, {"start", G_STRUCT_OFFSET (MonoILExceptionBlock, start)}, {"len", G_STRUCT_OFFSET (MonoILExceptionBlock, len)}, {"filter_offset", G_STRUCT_OFFSET (MonoILExceptionBlock, filter_offset)}, {NULL, 0} }; static const ClassDesc emit_classes_to_check [] = { {"TypeBuilder", typebuilder_fields}, {"ModuleBuilder", modulebuilder_fields}, {"AssemblyBuilder", assemblybuilder_fields}, {"ConstructorBuilder", ctorbuilder_fields}, {"MethodBuilder", methodbuilder_fields}, {"FieldBuilder", fieldbuilder_fields}, {"PropertyBuilder", propertybuilder_fields}, {"ILGenerator", ilgenerator_fields}, {"ILExceptionBlock", ilexblock_fields}, {"ILExceptionInfo", ilexinfo_fields}, {NULL, NULL} }; static const FieldDesc monoevent_fields[] = { {"klass", G_STRUCT_OFFSET (MonoReflectionEvent, klass)}, {"handle", G_STRUCT_OFFSET (MonoReflectionEvent, event)}, {NULL, 0} }; static const FieldDesc monoproperty_fields[] = { {"klass", G_STRUCT_OFFSET (MonoReflectionProperty, klass)}, {"prop", G_STRUCT_OFFSET (MonoReflectionProperty, property)}, {NULL, 0} }; static const FieldDesc monofield_fields[] = { {"klass", G_STRUCT_OFFSET (MonoReflectionField, klass)}, {"fhandle", G_STRUCT_OFFSET (MonoReflectionField, field)}, {NULL, 0} }; static const FieldDesc monomethodinfo_fields[] = { {"parent", G_STRUCT_OFFSET (MonoMethodInfo, parent)}, {"ret", G_STRUCT_OFFSET (MonoMethodInfo, ret)}, {"attrs", G_STRUCT_OFFSET (MonoMethodInfo, attrs)}, {"iattrs", G_STRUCT_OFFSET (MonoMethodInfo, implattrs)}, {NULL, 0} }; static const FieldDesc monopropertyinfo_fields[] = { {"parent", G_STRUCT_OFFSET (MonoPropertyInfo, parent)}, {"name", G_STRUCT_OFFSET (MonoPropertyInfo, name)}, {"get_method", G_STRUCT_OFFSET (MonoPropertyInfo, get)}, {"set_method", G_STRUCT_OFFSET (MonoPropertyInfo, set)}, {"attrs", G_STRUCT_OFFSET (MonoPropertyInfo, attrs)}, {NULL, 0} }; static const FieldDesc monomethod_fields[] = { {"mhandle", G_STRUCT_OFFSET (MonoReflectionMethod, method)}, {NULL, 0} }; static const FieldDesc monocmethod_fields[] = { {"mhandle", G_STRUCT_OFFSET (MonoReflectionMethod, method)}, {NULL, 0} }; static const FieldDesc pinfo_fields[] = { {"ClassImpl", G_STRUCT_OFFSET (MonoReflectionParameter, ClassImpl)}, {"DefaultValueImpl", G_STRUCT_OFFSET (MonoReflectionParameter, DefaultValueImpl)}, {"MemberImpl", G_STRUCT_OFFSET (MonoReflectionParameter, MemberImpl)}, {"NameImpl", G_STRUCT_OFFSET (MonoReflectionParameter, NameImpl)}, {"PositionImpl", G_STRUCT_OFFSET (MonoReflectionParameter, PositionImpl)}, {"AttrsImpl", G_STRUCT_OFFSET (MonoReflectionParameter, AttrsImpl)}, {NULL, 0} }; static const ClassDesc reflection_classes_to_check [] = { {"MonoEvent", monoevent_fields}, {"MonoProperty", monoproperty_fields}, {"MonoField", monofield_fields}, {"MonoMethodInfo", monomethodinfo_fields}, {"MonoPropertyInfo", monopropertyinfo_fields}, {"MonoMethod", monomethod_fields}, {"MonoCMethod", monocmethod_fields}, {"ParameterInfo", pinfo_fields}, {NULL, NULL} }; static FieldDesc enuminfo_fields[] = { {"utype", G_STRUCT_OFFSET (MonoEnumInfo, utype)}, {"values", G_STRUCT_OFFSET (MonoEnumInfo, values)}, {"names", G_STRUCT_OFFSET (MonoEnumInfo, names)}, {NULL, 0} }; static FieldDesc delegate_fields[] = { {"target_type", G_STRUCT_OFFSET (MonoDelegate, target_type)}, {"m_target", G_STRUCT_OFFSET (MonoDelegate, target)}, {"method_name", G_STRUCT_OFFSET (MonoDelegate, method_name)}, {"method_ptr", G_STRUCT_OFFSET (MonoDelegate, method_ptr)}, {"delegate_trampoline", G_STRUCT_OFFSET (MonoDelegate, delegate_trampoline)}, {"method_info", G_STRUCT_OFFSET (MonoDelegate, method_info)}, {NULL, 0} }; static FieldDesc multicast_delegate_fields[] = { {"prev", G_STRUCT_OFFSET (MonoMulticastDelegate, prev)}, {NULL, 0} }; static FieldDesc async_result_fields[] = { {"async_state", G_STRUCT_OFFSET (MonoAsyncResult, async_state)}, {"handle", G_STRUCT_OFFSET (MonoAsyncResult, handle)}, {"async_delegate", G_STRUCT_OFFSET (MonoAsyncResult, async_delegate)}, {"data", G_STRUCT_OFFSET (MonoAsyncResult, data)}, {"sync_completed", G_STRUCT_OFFSET (MonoAsyncResult, sync_completed)}, {"completed", G_STRUCT_OFFSET (MonoAsyncResult, completed)}, {"endinvoke_called", G_STRUCT_OFFSET (MonoAsyncResult, endinvoke_called)}, {"async_callback", G_STRUCT_OFFSET (MonoAsyncResult, async_callback)}, {NULL, 0} }; static FieldDesc exception_fields[] = { {"trace_ips", G_STRUCT_OFFSET (MonoException, trace_ips)}, {"inner_exception", G_STRUCT_OFFSET (MonoException, inner_ex)}, {"message", G_STRUCT_OFFSET (MonoException, message)}, {"help_link", G_STRUCT_OFFSET (MonoException, help_link)}, {"class_name", G_STRUCT_OFFSET (MonoException, class_name)}, {"stack_trace", G_STRUCT_OFFSET (MonoException, stack_trace)}, {"remote_stack_trace", G_STRUCT_OFFSET (MonoException, remote_stack_trace)}, {"remote_stack_index", G_STRUCT_OFFSET (MonoException, remote_stack_index)}, {"hresult", G_STRUCT_OFFSET (MonoException, hresult)}, {"source", G_STRUCT_OFFSET (MonoException, source)}, {NULL, 0} }; static const ClassDesc system_classes_to_check [] = { {"Exception", exception_fields}, {"MonoEnumInfo", enuminfo_fields}, {"Delegate", delegate_fields}, {"MulticastDelegate", multicast_delegate_fields}, {NULL, NULL} }; static FieldDesc stack_frame_fields [] = { {"ilOffset", G_STRUCT_OFFSET (MonoStackFrame, il_offset)}, {"nativeOffset", G_STRUCT_OFFSET (MonoStackFrame, native_offset)}, {"methodBase", G_STRUCT_OFFSET (MonoStackFrame, method)}, {"fileName", G_STRUCT_OFFSET (MonoStackFrame, filename)}, {"lineNumber", G_STRUCT_OFFSET (MonoStackFrame, line)}, {"columnNumber", G_STRUCT_OFFSET (MonoStackFrame, column)}, {NULL, 0} }; static const ClassDesc system_diagnostics_classes_to_check [] = { {"StackFrame", stack_frame_fields}, {NULL, NULL} }; static FieldDesc mono_method_message_fields[] = { {"method", G_STRUCT_OFFSET (MonoMethodMessage, method)}, {"args", G_STRUCT_OFFSET (MonoMethodMessage, args)}, {"names", G_STRUCT_OFFSET (MonoMethodMessage, names)}, {"arg_types", G_STRUCT_OFFSET (MonoMethodMessage, arg_types)}, {"ctx", G_STRUCT_OFFSET (MonoMethodMessage, ctx)}, {"rval", G_STRUCT_OFFSET (MonoMethodMessage, rval)}, {"exc", G_STRUCT_OFFSET (MonoMethodMessage, exc)}, {NULL, 0} }; static const ClassDesc messaging_classes_to_check [] = { {"AsyncResult", async_result_fields}, {"MonoMethodMessage", mono_method_message_fields}, {NULL, NULL} }; static FieldDesc transparent_proxy_fields[] = { {"_rp", G_STRUCT_OFFSET (MonoTransparentProxy, rp)}, {"_class", G_STRUCT_OFFSET (MonoTransparentProxy, remote_class)}, {NULL, 0} }; static FieldDesc real_proxy_fields[] = { {"class_to_proxy", G_STRUCT_OFFSET (MonoRealProxy, class_to_proxy)}, {NULL, 0} }; static const ClassDesc proxy_classes_to_check [] = { {"TransparentProxy", transparent_proxy_fields}, {"RealProxy", real_proxy_fields}, {NULL, NULL} }; static FieldDesc wait_handle_fields[] = { {"os_handle", G_STRUCT_OFFSET (MonoWaitHandle, handle)}, {"disposed", G_STRUCT_OFFSET (MonoWaitHandle, disposed)}, {NULL, 0} }; static FieldDesc thread_fields[] = { {"system_thread_handle", G_STRUCT_OFFSET (MonoThread, handle)}, {"threadpool_thread", G_STRUCT_OFFSET (MonoThread, threadpool_thread)}, {"state", G_STRUCT_OFFSET (MonoThread, state)}, {"abort_exc", G_STRUCT_OFFSET (MonoThread, abort_exc)}, {"abort_state", G_STRUCT_OFFSET (MonoThread, abort_state)}, {"thread_id", G_STRUCT_OFFSET (MonoThread, tid)}, {NULL, 0} }; static const ClassDesc threading_classes_to_check [] = { {"Thread", thread_fields}, {"WaitHandle", wait_handle_fields}, {NULL, NULL} }; static const FieldDesc cinfo_fields[] = { {"datetime_format", G_STRUCT_OFFSET (MonoCultureInfo, datetime_format)}, {"number_format", G_STRUCT_OFFSET (MonoCultureInfo, number_format)}, {"textinfo", G_STRUCT_OFFSET (MonoCultureInfo, textinfo)}, {"name", G_STRUCT_OFFSET (MonoCultureInfo, name)}, {"displayname", G_STRUCT_OFFSET (MonoCultureInfo, displayname)}, {"englishname", G_STRUCT_OFFSET (MonoCultureInfo, englishname)}, {"nativename", G_STRUCT_OFFSET (MonoCultureInfo, nativename)}, {"iso3lang", G_STRUCT_OFFSET (MonoCultureInfo, iso3lang)}, {"iso2lang", G_STRUCT_OFFSET (MonoCultureInfo, iso2lang)}, {"icu_name", G_STRUCT_OFFSET (MonoCultureInfo, icu_name)}, {"win3lang", G_STRUCT_OFFSET (MonoCultureInfo, win3lang)}, {"compareinfo", G_STRUCT_OFFSET (MonoCultureInfo, compareinfo)}, {NULL, 0} }; static const FieldDesc dtfinfo_fields[] = { {"amDesignator", G_STRUCT_OFFSET (MonoDateTimeFormatInfo, AMDesignator)}, {"pmDesignator", G_STRUCT_OFFSET (MonoDateTimeFormatInfo, PMDesignator)}, {"dayNames", G_STRUCT_OFFSET (MonoDateTimeFormatInfo, DayNames)}, {"monthNames", G_STRUCT_OFFSET (MonoDateTimeFormatInfo, MonthNames)}, {NULL, 0} }; static const FieldDesc nfinfo_fields[] = { {"decimalFormats", G_STRUCT_OFFSET (MonoNumberFormatInfo, decimalFormats)}, {"currencySymbol", G_STRUCT_OFFSET (MonoNumberFormatInfo, currencySymbol)}, {"percentSymbol", G_STRUCT_OFFSET (MonoNumberFormatInfo, percentSymbol)}, {"positiveSign", G_STRUCT_OFFSET (MonoNumberFormatInfo, positiveSign)}, {NULL, 0} }; static const FieldDesc compinfo_fields[] = { {"lcid", G_STRUCT_OFFSET (MonoCompareInfo, lcid)}, {"ICU_collator", G_STRUCT_OFFSET (MonoCompareInfo, ICU_collator)}, {NULL, 0} }; static const FieldDesc sortkey_fields[] = { {"str", G_STRUCT_OFFSET (MonoSortKey, str)}, {"options", G_STRUCT_OFFSET (MonoSortKey, options)}, {"key", G_STRUCT_OFFSET (MonoSortKey, key)}, {"lcid", G_STRUCT_OFFSET (MonoSortKey, lcid)}, {NULL, 0} }; static const ClassDesc globalization_classes_to_check [] = { {"CultureInfo", cinfo_fields}, {"DateTimeFormatInfo", dtfinfo_fields}, {"NumberFormatInfo", nfinfo_fields}, {"CompareInfo", compinfo_fields}, {"SortKey", sortkey_fields}, {NULL, NULL} }; static const FieldDesc safe_handle_fields[] ={ {"handle", G_STRUCT_OFFSET (MonoSafeHandle, handle)}, {NULL, 0} }; static const ClassDesc interop_classes_to_check [] = { {"SafeHandle", safe_handle_fields}, {NULL, NULL} }; typedef struct { const char *name; const ClassDesc *types; } NameSpaceDesc; static const NameSpaceDesc namespaces_to_check[] = { {"System.Runtime.Remoting.Proxies", proxy_classes_to_check}, {"System.Runtime.Remoting.Messaging", messaging_classes_to_check}, {"System.Reflection.Emit", emit_classes_to_check}, {"System.Reflection", reflection_classes_to_check}, {"System.Threading", threading_classes_to_check}, {"System.Diagnostics", system_diagnostics_classes_to_check}, {"System", system_classes_to_check}, {"System.Globalization", globalization_classes_to_check}, {"System.Runtime.InteropServices", interop_classes_to_check}, {NULL, NULL} }; static char* check_corlib (MonoImage *corlib) { MonoClass *klass; MonoClassField *field; const FieldDesc *fdesc; const ClassDesc *cdesc; const NameSpaceDesc *ndesc; gint struct_offset; GString *result = NULL; for (ndesc = namespaces_to_check; ndesc->name; ++ndesc) { for (cdesc = ndesc->types; cdesc->name; ++cdesc) { klass = mono_class_from_name (corlib, ndesc->name, cdesc->name); if (!klass) { if (!result) result = g_string_new (""); g_string_append_printf (result, "Cannot find class %s\n", cdesc->name); continue; } mono_class_init (klass); /* * FIXME: we should also check the size of valuetypes, or * we're going to have trouble when we access them in arrays. */ if (klass->valuetype) struct_offset = sizeof (MonoObject); else struct_offset = 0; for (fdesc = cdesc->fields; fdesc->name; ++fdesc) { field = mono_class_get_field_from_name (klass, fdesc->name); if (!field || (field->offset != (fdesc->offset + struct_offset))) { if (!result) result = g_string_new (""); g_string_append_printf (result, "field `%s' mismatch in class %s (%ld + %ld != %ld)\n", fdesc->name, cdesc->name, (long) fdesc->offset, (long)struct_offset, (long) (field?field->offset:-1)); } } } } if (result) return g_string_free (result, FALSE); return NULL; } char* mono_verify_corlib () { return check_corlib (mono_defaults.corlib); }