Merge branch 'master' of github.com:mono/mono
[mono.git] / mono / metadata / metadata-verify.c
index c5c418c3562625d762fbbc48c6fa4f479ccb674c..584250b9c6541eb85fbf3aba3d986a61b6153699 100644 (file)
@@ -23,6 +23,7 @@
 #include <mono/metadata/cil-coff.h>
 #include <mono/metadata/attrdefs.h>
 #include <mono/utils/strenc.h>
+#include <mono/utils/mono-error-internals.h>
 #include <string.h>
 //#include <signal.h>
 #include <ctype.h>
@@ -299,6 +300,16 @@ dword_align (const char *ptr)
 #endif
 }
 
+static void
+add_from_mono_error (VerifyContext *ctx, MonoError *error)
+{
+       if (mono_error_ok (error))
+               return;
+
+       ADD_ERROR (ctx, g_strdup (mono_error_get_message (error)));
+       mono_error_cleanup (error);
+}
+
 static guint32
 pe_signature_offset (VerifyContext *ctx)
 {
@@ -927,22 +938,31 @@ get_metadata_stream (VerifyContext *ctx, MonoStreamHeader *header)
 }
 
 static gboolean
-is_valid_string_full (VerifyContext *ctx, guint32 offset, gboolean allow_empty)
+is_valid_string_full_with_image (MonoImage *image, guint32 offset, gboolean allow_empty)
 {
-       OffsetAndSize strings = get_metadata_stream (ctx, &ctx->image->heap_strings);
+       guint32 heap_offset = (char*)image->heap_strings.data - image->raw_data;
+       guint32 heap_size = image->heap_strings.size;
+
        glong length;
-       const char *data = ctx->data + strings.offset;
+       const char *data = image->raw_data + heap_offset;
 
-       if (offset >= strings.size)
+       if (offset >= heap_size)
                return FALSE;
-       if (data + offset < data) //FIXME, use a generalized and smart unsigned add with overflow check and fix the whole thing  
+       if (CHECK_ADDP_OVERFLOW_UN (data, offset))
                return FALSE;
 
-       if (!mono_utf8_validate_and_len_with_bounds (data + offset, strings.size - offset, &length, NULL))
+       if (!mono_utf8_validate_and_len_with_bounds (data + offset, heap_size - offset, &length, NULL))
                return FALSE;
        return allow_empty || length > 0;
 }
 
+
+static gboolean
+is_valid_string_full (VerifyContext *ctx, guint32 offset, gboolean allow_empty)
+{
+       return is_valid_string_full_with_image (ctx->image, offset, allow_empty);
+}
+
 static gboolean
 is_valid_string (VerifyContext *ctx, guint32 offset)
 {
@@ -993,7 +1013,7 @@ make_coded_token (int kind, guint32 table, guint32 table_idx)
 }
 
 static gboolean
-is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token)
+is_valid_coded_index_with_image (MonoImage *image, int token_kind, guint32 coded_token)
 {
        guint32 bits = coded_index_desc [token_kind++];
        guint32 table_count = coded_index_desc [token_kind++];
@@ -1008,7 +1028,13 @@ is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token)
 
        if (table == INVALID_TABLE)
                return FALSE;
-       return token <= ctx->image->tables [table].rows;
+       return token <= image->tables [table].rows;
+}
+
+static gboolean
+is_valid_coded_index (VerifyContext *ctx, int token_kind, guint32 coded_token)
+{
+       return is_valid_coded_index_with_image (ctx->image, token_kind, coded_token);
 }
 
 typedef struct {
@@ -2181,13 +2207,15 @@ is_valid_constant (VerifyContext *ctx, guint32 type, guint32 offset)
 #define SECTION_HEADER_INVALID_FLAGS 0x3E
 
 static gboolean
-is_valid_method_header (VerifyContext *ctx, guint32 rva)
+is_valid_method_header (VerifyContext *ctx, guint32 rva, guint32 *locals_token)
 {
        unsigned local_vars_tok, code_size, offset = mono_cli_rva_image_map (ctx->image, rva);
        unsigned header = 0;
        unsigned fat_header = 0, size = 0, max_stack;
        const char *ptr = NULL, *end;
 
+       *locals_token = 0;
+
        if (offset == INVALID_ADDRESS)
                FAIL (ctx, g_strdup ("MethodHeader: Invalid RVA"));
 
@@ -2230,6 +2258,7 @@ is_valid_method_header (VerifyContext *ctx, guint32 rva)
                        FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature table 0x%x", ((local_vars_tok >> 24) & 0xFF)));
                if ((local_vars_tok & 0xFFFFFF) > ctx->image->tables [MONO_TABLE_STANDALONESIG].rows)   
                        FAIL (ctx, g_strdup_printf ("MethodHeader: Invalid local vars signature points to invalid row 0x%x", local_vars_tok & 0xFFFFFF));
+               *locals_token = local_vars_tok & 0xFFFFFF;
        }
 
        if (fat_header & FAT_HEADER_INVALID_FLAGS)
@@ -2324,22 +2353,12 @@ static void
 verify_typeref_table (VerifyContext *ctx)
 {
        MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_TYPEREF];
-       guint32 data [MONO_TYPEREF_SIZE];
-       int i;
+       MonoError error;
+       guint32 i;
 
        for (i = 0; i < table->rows; ++i) {
-               mono_metadata_decode_row (table, i, data, MONO_TYPEREF_SIZE);
-               if (!is_valid_coded_index (ctx, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE]))
-                       ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d coded index 0x%08x", i, data [MONO_TYPEREF_SCOPE]));
-               
-               if (!get_coded_index_token (RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE]))
-                       ADD_ERROR (ctx, g_strdup_printf ("The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", i));
-
-               if (!data [MONO_TYPEREF_NAME] || !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAME]))
-                       ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d name token 0x%08x", i, data [MONO_TYPEREF_NAME]));
-
-               if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_non_empty_string (ctx, data [MONO_TYPEREF_NAMESPACE]))
-                       ADD_ERROR (ctx, g_strdup_printf ("Invalid typeref row %d namespace token 0x%08x", i, data [MONO_TYPEREF_NAMESPACE]));
+               mono_verifier_verify_typeref_row (ctx->image, i, &error);
+               add_from_mono_error (ctx, &error);
        }
 }
 
@@ -2672,7 +2691,7 @@ static void
 verify_method_table_full (VerifyContext *ctx)
 {
        MonoTableInfo *table = &ctx->image->tables [MONO_TABLE_METHOD];
-       guint32 data [MONO_METHOD_SIZE], rva;
+       guint32 data [MONO_METHOD_SIZE], rva, locals_token;
        int i;
 
        for (i = 0; i < table->rows; ++i) {
@@ -2682,7 +2701,7 @@ verify_method_table_full (VerifyContext *ctx)
                if (!data [MONO_METHOD_SIGNATURE] || !is_valid_method_signature (ctx, data [MONO_METHOD_SIGNATURE]))
                        ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d invalid signature token 0x%08x", i, data [MONO_METHOD_SIGNATURE]));
 
-               if (rva && !is_valid_method_header (ctx, rva))
+               if (rva && !is_valid_method_header (ctx, rva, &locals_token))
                        ADD_ERROR (ctx, g_strdup_printf ("Invalid method row %d RVA points to an invalid method header", i));
        }
 }
@@ -3684,6 +3703,7 @@ verify_tables_data_global_constraints (VerifyContext *ctx)
 static void
 verify_tables_data_global_constraints_full (VerifyContext *ctx)
 {
+       verify_typeref_table (ctx);
        verify_typeref_table_global_constraints (ctx);
 }
 
@@ -3714,8 +3734,9 @@ verify_tables_data (VerifyContext *ctx)
 
        verify_module_table (ctx);
        CHECK_ERROR ();
+       /*Obfuscators love to place broken stuff in the typeref table
        verify_typeref_table (ctx);
-       CHECK_ERROR ();
+       CHECK_ERROR ();*/
        verify_typedef_table (ctx);
        CHECK_ERROR ();
        verify_field_table (ctx);
@@ -3782,11 +3803,11 @@ verify_tables_data (VerifyContext *ctx)
 }
 
 static void
-init_verify_context (VerifyContext *ctx, MonoImage *image, GSList **error_list)
+init_verify_context (VerifyContext *ctx, MonoImage *image, gboolean report_error)
 {
        memset (ctx, 0, sizeof (VerifyContext));
        ctx->image = image;
-       ctx->report_error = error_list != NULL;
+       ctx->report_error = report_error;
        ctx->report_warning = FALSE; //export this setting in the API
        ctx->valid = 1;
        ctx->size = image->raw_data_len;
@@ -3804,6 +3825,18 @@ cleanup_context (VerifyContext *ctx, GSList **error_list)
        return ctx->valid;      
 }
 
+static gboolean
+cleanup_context_checked (VerifyContext *ctx, MonoError *error)
+{
+       g_free (ctx->sections);
+       if (ctx->errors) {
+               MonoVerifyInfo *info = ctx->errors->data;
+               mono_error_set_bad_image (error, ctx->image, "%s", info->message);
+               mono_free_verify_list (ctx->errors);
+       }
+       return ctx->valid;
+}
+
 gboolean
 mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list)
 {
@@ -3812,7 +3845,7 @@ mono_verifier_verify_pe_data (MonoImage *image, GSList **error_list)
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_PE;
 
        verify_msdos_header (&ctx);
@@ -3842,7 +3875,7 @@ mono_verifier_verify_cli_data (MonoImage *image, GSList **error_list)
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_CLI;
 
        verify_cli_header (&ctx);
@@ -3875,7 +3908,7 @@ mono_verifier_verify_table_data (MonoImage *image, GSList **error_list)
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        verify_tables_data (&ctx);
@@ -3895,7 +3928,7 @@ mono_verifier_verify_full_table_data (MonoImage *image, GSList **error_list)
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        verify_typedef_table_full (&ctx);
@@ -3934,7 +3967,7 @@ mono_verifier_verify_field_signature (MonoImage *image, guint32 offset, GSList *
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        is_valid_field_signature (&ctx, offset);
@@ -3945,30 +3978,39 @@ gboolean
 mono_verifier_verify_method_header (MonoImage *image, guint32 offset, GSList **error_list)
 {
        VerifyContext ctx;
+       guint32 locals_token;
 
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
-       is_valid_method_header (&ctx, offset);
+       is_valid_method_header (&ctx, offset, &locals_token);
+       if (locals_token) {
+               guint32 sig_offset = mono_metadata_decode_row_col (&image->tables [MONO_TABLE_STANDALONESIG], locals_token - 1, MONO_STAND_ALONE_SIGNATURE);
+               is_valid_standalonesig_blob (&ctx, sig_offset);
+       }
+
        return cleanup_context (&ctx, error_list);
 }
 
 gboolean
-mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, GSList **error_list)
+mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, MonoError *error)
 {
        VerifyContext ctx;
 
+       mono_error_init (error);
+
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, TRUE);
        ctx.stage = STAGE_TABLES;
 
        is_valid_method_signature (&ctx, offset);
-       return cleanup_context (&ctx, error_list);
+       /*XXX This returns a bad image exception, it might be the case that the right exception is method load.*/
+       return cleanup_context_checked (&ctx, error);
 }
 
 gboolean
@@ -3979,7 +4021,7 @@ mono_verifier_verify_memberref_signature (MonoImage *image, guint32 offset, GSLi
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        is_valid_method_or_field_signature (&ctx, offset);
@@ -3994,7 +4036,7 @@ mono_verifier_verify_standalone_signature (MonoImage *image, guint32 offset, GSL
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        is_valid_standalonesig_blob (&ctx, offset);
@@ -4009,7 +4051,7 @@ mono_verifier_verify_typespec_signature (MonoImage *image, guint32 offset, guint
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
        ctx.token = token;
 
@@ -4025,7 +4067,7 @@ mono_verifier_verify_methodspec_signature (MonoImage *image, guint32 offset, GSL
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        is_valid_methodspec_blob (&ctx, offset);
@@ -4061,7 +4103,7 @@ mono_verifier_verify_string_signature (MonoImage *image, guint32 offset, GSList
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        verify_user_string (&ctx, offset);
@@ -4077,7 +4119,7 @@ mono_verifier_verify_cattr_blob (MonoImage *image, guint32 offset, GSList **erro
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        is_valid_cattr_blob (&ctx, offset);
@@ -4093,7 +4135,7 @@ mono_verifier_verify_cattr_content (MonoImage *image, MonoMethod *ctor, const gu
        if (!mono_verifier_is_enabled_for_image (image))
                return TRUE;
 
-       init_verify_context (&ctx, image, error_list);
+       init_verify_context (&ctx, image, error_list != NULL);
        ctx.stage = STAGE_TABLES;
 
        is_valid_cattr_content (&ctx, ctor, (const char*)data, size);
@@ -4129,6 +4171,46 @@ mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMetho
        return TRUE;
 }
 
+gboolean
+mono_verifier_verify_typeref_row (MonoImage *image, guint32 row, MonoError *error)
+{
+       MonoTableInfo *table = &image->tables [MONO_TABLE_TYPEREF];
+       guint32 data [MONO_TYPEREF_SIZE];
+
+       mono_error_init (error);
+
+       if (!mono_verifier_is_enabled_for_image (image))
+               return TRUE;
+
+       if (row >= table->rows) {
+               mono_error_set_bad_image (error, image, "Invalid typeref row %d - table has %d rows", row, table->rows);
+               return FALSE;
+       }
+
+       mono_metadata_decode_row (table, row, data, MONO_TYPEREF_SIZE);
+       if (!is_valid_coded_index_with_image (image, RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) {
+               mono_error_set_bad_image (error, image, "Invalid typeref row %d coded index 0x%08x", row, data [MONO_TYPEREF_SCOPE]);
+               return FALSE;
+       }
+
+       if (!get_coded_index_token (RES_SCOPE_DESC, data [MONO_TYPEREF_SCOPE])) {
+               mono_error_set_bad_image (error, image, "The metadata verifier doesn't support null ResolutionScope tokens for typeref row %d", row);
+               return FALSE;
+       }
+
+       if (!data [MONO_TYPEREF_NAME] || !is_valid_string_full_with_image (image, data [MONO_TYPEREF_NAME], FALSE)) {
+               mono_error_set_bad_image (error, image, "Invalid typeref row %d name token 0x%08x", row, data [MONO_TYPEREF_NAME]);
+               return FALSE;
+       }
+
+       if (data [MONO_TYPEREF_NAMESPACE] && !is_valid_string_full_with_image (image, data [MONO_TYPEREF_NAMESPACE], FALSE)) {
+               mono_error_set_bad_image (error, image, "Invalid typeref row %d namespace token 0x%08x", row, data [MONO_TYPEREF_NAMESPACE]);
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
 #else
 gboolean
 mono_verifier_verify_table_data (MonoImage *image, GSList **error_list)
@@ -4167,8 +4249,9 @@ mono_verifier_verify_method_header (MonoImage *image, guint32 offset, GSList **e
 }
 
 gboolean
-mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, GSList **error_list)
+mono_verifier_verify_method_signature (MonoImage *image, guint32 offset, MonoError *error)
 {
+       mono_error_init (error);
        return TRUE;
 }
 
@@ -4221,4 +4304,10 @@ mono_verifier_is_sig_compatible (MonoImage *image, MonoMethod *method, MonoMetho
 }
 
 
+gboolean
+mono_verifier_verify_typeref_row (MonoImage *image, guint32 row, MonoError *error)
+{
+       mono_error_init (error);
+}
+
 #endif /* DISABLE_VERIFIER */