2010-04-09 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / metadata / metadata-verify.c
index 61a00c69ee7c613fe6ded94b1c2f5974ae2c5e18..0ffb428a92d5b64c8396455bea201d06e2f82540 100644 (file)
@@ -217,7 +217,7 @@ typedef struct {
 
 typedef struct {
        const char *data;
-       guint32 size;
+       guint32 size, token;
        GSList *errors;
        int valid;
        MonoImage *image;
@@ -250,6 +250,13 @@ typedef struct {
                } \
        } while (0)
 
+#define ADD_ERROR_NO_RETURN(__ctx, __msg)      \
+       do {    \
+               if ((__ctx)->report_error) \
+                       ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
+               (__ctx)->valid = 0; \
+       } while (0)
+
 #define ADD_ERROR(__ctx, __msg)        \
        do {    \
                if ((__ctx)->report_error) \
@@ -742,7 +749,7 @@ verify_metadata_header (VerifyContext *ctx)
 {
        int i;
        DataDirectory it = get_data_dir (ctx, CLI_HEADER_IDX);
-       guint32 offset;
+       guint32 offset, section_count;
        const char *ptr;
 
        offset = it.translated_offset;
@@ -772,13 +779,14 @@ verify_metadata_header (VerifyContext *ctx)
 
        ptr = ctx->data + offset; //move to streams header 
 
-       if (read16 (ptr + 2) < 3)
+       section_count = read16 (ptr + 2);
+       if (section_count < 3)
                ADD_ERROR (ctx, g_strdup_printf ("Metadata root section must have at least 3 streams (#~, #GUID and #Blob"));
 
        ptr += 4;
        offset += 4;
 
-       for (i = 0; i < 5; ++i) {
+       for (i = 0; i < section_count; ++i) {
                guint32 stream_off, stream_size;
                int string_size, stream_idx;
 
@@ -814,8 +822,12 @@ verify_metadata_header (VerifyContext *ctx)
                        stream_idx = GUID_STREAM;
                else if (!strncmp ("#~", ptr, 3))
                        stream_idx = TILDE_STREAM;
-               else
-                       ADD_ERROR (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr));
+               else {
+                       ADD_WARNING (ctx, g_strdup_printf ("Metadata stream header %d invalid name %s", i, ptr));
+                       offset = pad4 (offset);
+                       ptr = ctx->data + offset;
+                       continue;
+               }
 
                if (ctx->metadata_streams [stream_idx].offset != 0)
                        ADD_ERROR (ctx, g_strdup_printf ("Duplicated metadata stream header %s", ptr));
@@ -869,7 +881,7 @@ verify_tables_schema (VerifyContext *ctx)
                  Unused: 0x1E 0x1F 0x2D-0x3F
                  We don't care about the MS extensions.*/
                if (i == 0x3 || i == 0x5 || i == 0x7 || i == 0x13 || i == 0x16)
-                       ADD_ERROR (ctx, g_strdup_printf ("The metadata verifies doesn't support MS specific table %x", i));
+                       ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support MS specific table %x", i));
                if (i == 0x1E || i == 0x1F || i >= 0x2D)
                        ADD_ERROR (ctx, g_strdup_printf ("Invalid table %x", i));
                ++count;
@@ -1255,6 +1267,12 @@ parse_generic_inst (VerifyContext *ctx, const char **_ptr, const char *end)
        if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token))
                FAIL (ctx, g_strdup_printf ("GenericInst: invalid TypeDefOrRef token %x", token));
 
+       if (ctx->token) {
+               if (mono_metadata_token_index (ctx->token) == get_coded_index_token (TYPEDEF_OR_REF_DESC, token) &&
+                       mono_metadata_token_table (ctx->token) == get_coded_index_table (TYPEDEF_OR_REF_DESC, token))
+                       FAIL (ctx, g_strdup_printf ("Type: Recurside generic instance specification (%x). A type signature can't reference itself", ctx->token));
+       }
+
        if (!safe_read_cint (count, ptr, end))
                FAIL (ctx, g_strdup ("GenericInst: Not enough room for argument count"));
 
@@ -1307,6 +1325,11 @@ parse_type (VerifyContext *ctx, const char **_ptr, const char *end)
        
                if (!is_valid_coded_index (ctx, TYPEDEF_OR_REF_DESC, token))
                        FAIL (ctx, g_strdup_printf ("Type: invalid TypeDefOrRef token %x", token));
+               if (ctx->token) {
+                       if (mono_metadata_token_index (ctx->token) == get_coded_index_token (TYPEDEF_OR_REF_DESC, token) &&
+                               mono_metadata_token_table (ctx->token) == get_coded_index_table (TYPEDEF_OR_REF_DESC, token))
+                               FAIL (ctx, g_strdup_printf ("Type: Recurside type specification (%x). A type signature can't reference itself", ctx->token));
+               }
                break;
 
        case MONO_TYPE_VAR:
@@ -1672,6 +1695,11 @@ is_valid_standalonesig_blob (VerifyContext *ctx, guint32 offset)
        --ptr;
        if (signature == 0x07)
                return parse_locals_signature (ctx, &ptr, end);
+
+       /*F# and managed C++ produce standalonesig for fields even thou the spec doesn't mention it.*/
+       if (signature == 0x06)
+               return parse_field (ctx, &ptr, end);
+
        return parse_method_signature (ctx, &ptr, end, TRUE, TRUE);
 }
 
@@ -1721,7 +1749,7 @@ is_valid_typespec_blob (VerifyContext *ctx, guint32 offset)
 }
 
 static gboolean
-is_valid_methodspec_blog (VerifyContext *ctx, guint32 offset)
+is_valid_methodspec_blob (VerifyContext *ctx, guint32 offset)
 {
        int size = 0;
        const char *ptr = NULL, *end;
@@ -2083,8 +2111,10 @@ verify_typedef_table_full (VerifyContext *ctx)
                mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE);
 
                if (i == 0) {
-                       if (data [MONO_TYPEDEF_EXTENDS] != 0)
+                       /*XXX it's ok if <module> extends object, or anything at all, actually. */
+                       /*if (data [MONO_TYPEDEF_EXTENDS] != 0)
                                ADD_ERROR (ctx, g_strdup_printf ("Invalid typedef row 0 for the special <module> type must have a null extend field"));
+                       */
                        continue;
                }
 
@@ -2190,8 +2220,8 @@ verify_field_table_full (VerifyContext *ctx)
        }
 }
 
-/*bits 6,8,9,10,11,13,14,15*/
-#define INVALID_METHOD_IMPLFLAG_BITS ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15))
+/*bits 8,9,10,11,13,14,15*/
+#define INVALID_METHOD_IMPLFLAG_BITS ((1 << 8) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 13) | (1 << 14) | (1 << 15))
 static void
 verify_method_table (VerifyContext *ctx)
 {
@@ -2240,6 +2270,8 @@ verify_method_table (VerifyContext *ctx)
                if (flags & METHOD_ATTRIBUTE_ABSTRACT) {
                        if (flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
                                ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and PinvokeImpl", i));
+                       if (flags & METHOD_ATTRIBUTE_FINAL)
+                               ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract and Final", i));
                        if (!(flags & METHOD_ATTRIBUTE_VIRTUAL))
                                ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d is Abstract but not Virtual", i));
                }
@@ -2887,10 +2919,11 @@ verify_typespec_table_full (VerifyContext *ctx)
 
        for (i = 0; i < table->rows; ++i) {
                mono_metadata_decode_row (table, i, data, MONO_TYPESPEC_SIZE);
-
+               ctx->token = (i + 1) | MONO_TOKEN_TYPE_SPEC;
                if (!is_valid_typespec_blob (ctx, data [MONO_TYPESPEC_SIGNATURE]))
                        ADD_ERROR (ctx, g_strdup_printf ("Invalid TypeSpec row %d Signature field %08x", i, data [MONO_TYPESPEC_SIGNATURE]));
        }
+       ctx->token = 0;
 }
 
 #define INVALID_IMPLMAP_FLAGS_BITS ~((1 << 0) | (1 << 1) | (1 << 2) | (1 << 6) | (1 << 8) | (1 << 9) | (1 << 10))
@@ -3191,7 +3224,7 @@ verify_method_spec_table_full (VerifyContext *ctx)
        for (i = 0; i < table->rows; ++i) {
                mono_metadata_decode_row (table, i, data, MONO_METHODSPEC_SIZE);
 
-               if (!is_valid_methodspec_blog (ctx, data [MONO_METHODSPEC_SIGNATURE]))
+               if (!is_valid_methodspec_blob (ctx, data [MONO_METHODSPEC_SIGNATURE]))
                        ADD_ERROR (ctx, g_strdup_printf ("MethodSpec table row %d has invalid Instantiation token %08x", i, data [MONO_METHODSPEC_SIGNATURE]));
        }
 }
@@ -3217,6 +3250,104 @@ verify_generic_param_constraint_table (VerifyContext *ctx)
        }
 }
 
+
+typedef struct {
+       const char *name;
+       const char *name_space;
+       guint32 resolution_scope;
+} TypeDefUniqueId;
+
+static guint
+typedef_hash (gconstpointer _key)
+{
+       const TypeDefUniqueId *key = _key;
+       return g_str_hash (key->name) ^ g_str_hash (key->name_space) ^ key->resolution_scope; /*XXX better salt the int key*/
+}
+
+static gboolean
+typedef_equals (gconstpointer _a, gconstpointer _b)
+{
+       const TypeDefUniqueId *a = _a;
+       const TypeDefUniqueId *b = _b;
+       return !strcmp (a->name, b->name) && !strcmp (a->name_space, b->name_space) && a->resolution_scope == b->resolution_scope;
+}
+
+static void
+verify_typedef_table_global_constraints (VerifyContext *ctx)
+{
+       int i;
+       guint32 data [MONO_TYPEDEF_SIZE];
+       guint32 nested_data [MONO_NESTED_CLASS_SIZE];
+       MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEDEF];
+       MonoTableInfo *nested_table = &ctx->image->tables [MONO_TABLE_NESTEDCLASS];
+       GHashTable *unique_types = g_hash_table_new_full (&typedef_hash, &typedef_equals, g_free, NULL);
+
+       for (i = 0; i < table->rows; ++i) {
+               guint visibility;
+               TypeDefUniqueId *type = g_new (TypeDefUniqueId, 1);
+               mono_metadata_decode_row (table, i, data, MONO_TYPEDEF_SIZE);
+
+               type->name = mono_metadata_string_heap (ctx->image, data [MONO_TYPEDEF_NAME]);
+               type->name_space = mono_metadata_string_heap (ctx->image, data [MONO_TYPEDEF_NAMESPACE]);
+               type->resolution_scope = 0;
+
+               visibility = data [MONO_TYPEDEF_FLAGS] & TYPE_ATTRIBUTE_VISIBILITY_MASK;
+               if (visibility >= TYPE_ATTRIBUTE_NESTED_PUBLIC && visibility <= TYPE_ATTRIBUTE_NESTED_FAM_OR_ASSEM) {
+                       int res = search_sorted_table (ctx, MONO_TABLE_NESTEDCLASS, MONO_NESTED_CLASS_NESTED, i + 1);
+                       g_assert (res >= 0);
+
+                       mono_metadata_decode_row (nested_table, res, nested_data, MONO_NESTED_CLASS_SIZE);
+                       type->resolution_scope = nested_data [MONO_NESTED_CLASS_ENCLOSING];
+               }
+
+               if (g_hash_table_lookup (unique_types, type)) {
+                       ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("TypeDef table row %d has duplicate for tuple (%s,%s,%x)", i, type->name, type->name_space, type->resolution_scope));
+                       g_hash_table_destroy (unique_types);
+                       g_free (type);
+                       return;
+               }
+               g_hash_table_insert (unique_types, type, GUINT_TO_POINTER (1));
+       }
+
+       g_hash_table_destroy (unique_types);
+}
+
+static void
+verify_typeref_table_global_constraints (VerifyContext *ctx)
+{
+       int i;
+       guint32 data [MONO_TYPEREF_SIZE];
+       MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF];
+       GHashTable *unique_types = g_hash_table_new_full (&typedef_hash, &typedef_equals, g_free, NULL);
+
+       for (i = 0; i < table->rows; ++i) {
+               TypeDefUniqueId *type = g_new (TypeDefUniqueId, 1);
+               mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE);
+
+               type->resolution_scope = data [MONO_TYPEREF_SCOPE];
+               type->name = mono_metadata_string_heap (ctx->image, data [MONO_TYPEREF_NAME]);
+               type->name_space = mono_metadata_string_heap (ctx->image, data [MONO_TYPEREF_NAMESPACE]);
+
+               if (g_hash_table_lookup (unique_types, type)) {
+                       ADD_ERROR_NO_RETURN (ctx, g_strdup_printf ("TypeRef table row %d has duplicate for tuple (%s,%s,%x)", i, type->name, type->name_space, type->resolution_scope));
+                       g_hash_table_destroy (unique_types);
+                       g_free (type);
+                       return;
+               }
+               g_hash_table_insert (unique_types, type, GUINT_TO_POINTER (1));
+       }
+
+       g_hash_table_destroy (unique_types);
+}
+
+static void
+verify_tables_data_global_constraints (VerifyContext *ctx)
+{
+       verify_typeref_table_global_constraints (ctx);
+       CHECK_ERROR ();
+       verify_typedef_table_global_constraints (ctx);
+}
+       
 static void
 verify_tables_data (VerifyContext *ctx)
 {
@@ -3307,6 +3438,8 @@ verify_tables_data (VerifyContext *ctx)
        verify_method_spec_table (ctx);
        CHECK_ERROR ();
        verify_generic_param_constraint_table (ctx);
+       CHECK_ERROR ();
+       verify_tables_data_global_constraints (ctx);
 }
 
 static void
@@ -3528,7 +3661,7 @@ mono_verifier_verify_standalone_signature (MonoImage *image, guint32 offset, GSL
 }
 
 gboolean
-mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, GSList **error_list)
+mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, guint32 token, GSList **error_list)
 {
        VerifyContext ctx;
 
@@ -3537,6 +3670,7 @@ mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, GSLis
 
        init_verify_context (&ctx, image, error_list);
        ctx.stage = STAGE_TABLES;
+       ctx.token = token;
 
        is_valid_typespec_blob (&ctx, offset);
        return cleanup_context (&ctx, error_list);
@@ -3553,10 +3687,75 @@ mono_verifier_verify_methodspec_signature (MonoImage *image, guint32 offset, GSL
        init_verify_context (&ctx, image, error_list);
        ctx.stage = STAGE_TABLES;
 
-       is_valid_methodspec_blog (&ctx, offset);
+       is_valid_methodspec_blob (&ctx, offset);
+       return cleanup_context (&ctx, error_list);
+}
+
+static void
+verify_user_string (VerifyContext *ctx, guint32 offset)
+{
+       OffsetAndSize heap_us = get_metadata_stream (ctx, &ctx->image->heap_us);
+       guint32 entry_size, bytes;
+
+       if (heap_us.size < offset)
+               ADD_ERROR (ctx, g_strdup ("User string offset beyond heap_us size"));
+
+       if (!decode_value (ctx->data + offset + heap_us.offset, heap_us.size - heap_us.offset, &entry_size, &bytes))
+               ADD_ERROR (ctx, g_strdup ("Could not decode user string blob size"));
+
+       if (CHECK_ADD4_OVERFLOW_UN (entry_size, bytes))
+               ADD_ERROR (ctx, g_strdup ("User string size overflow"));
+
+       entry_size += bytes;
+
+       if (ADD_IS_GREATER_OR_OVF (offset, entry_size, heap_us.size))
+               ADD_ERROR (ctx, g_strdup ("User string oveflow heap_us"));
+}
+
+gboolean
+mono_verifier_verify_string_signature (MonoImage *image, guint32 offset, GSList **error_list)
+{
+       VerifyContext ctx;
+
+       if (!mono_verifier_is_enabled_for_image (image))
+               return TRUE;
+
+       init_verify_context (&ctx, image, error_list);
+       ctx.stage = STAGE_TABLES;
+
+       verify_user_string (&ctx, offset);
+
        return cleanup_context (&ctx, error_list);
 }
 
+gboolean
+mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMethodSignature *signature)
+{
+       MonoMethodSignature *original_sig;
+       if (!mono_verifier_is_enabled_for_image (image))
+               return TRUE;
+
+       original_sig = mono_method_signature (method);
+       if (original_sig->call_convention == MONO_CALL_VARARG) {
+               if (original_sig->hasthis != signature->hasthis)
+                       return FALSE;
+               if (original_sig->call_convention != signature->call_convention)
+                       return FALSE;
+               if (original_sig->explicit_this != signature->explicit_this)
+                       return FALSE;
+               if (original_sig->call_convention != signature->call_convention)
+                       return FALSE;
+               if (original_sig->pinvoke != signature->pinvoke)
+                       return FALSE;
+               if (original_sig->sentinelpos != signature->sentinelpos)
+                       return FALSE;
+       } else if (!mono_metadata_signature_equal (signature, original_sig)) {
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
 #else
 gboolean
 mono_verifier_verify_table_data (MonoImage *image, GSList **error_list)
@@ -3613,7 +3812,7 @@ mono_verifier_verify_standalone_signature (MonoImage *image, guint32 offset, GSL
 }
 
 gboolean
-mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, GSList **error_list)
+mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, guint32 token, GSList **error_list)
 {
        return TRUE;
 }
@@ -3624,4 +3823,17 @@ mono_verifier_verify_methodspec_signature (MonoImage *image, guint32 offset, GSL
        return TRUE;
 }
 
+gboolean
+mono_verifier_verify_string_signature (MonoImage *image, guint32 offset, GSList **error_list)
+{
+       return TRUE;
+}
+
+gboolean
+mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMethodSignature *signature)
+{
+       return TRUE;
+}
+
+
 #endif /* DISABLE_VERIFIER */