New test.
[mono.git] / mono / metadata / verify.c
index 8392920f895eb98d800895997f42e7a834bdf75d..180c80daa7c5913bca2e0be80e033debe54bd917 100644 (file)
 #include <mono/metadata/tokentype.h>
 #include <mono/metadata/mono-basic-block.h>
 #include <mono/utils/mono-counters.h>
+#include <mono/utils/monobitset.h>
 #include <string.h>
 #include <signal.h>
 #include <ctype.h>
 
-
 static MiniVerifierMode verifier_mode = MONO_VERIFIER_MODE_OFF;
 static gboolean verify_all = FALSE;
 
@@ -231,6 +231,9 @@ mono_class_is_valid_generic_instantiation (VerifyContext *ctx, MonoClass *klass)
 
 static gboolean
 mono_method_is_valid_generic_instantiation (VerifyContext *ctx, MonoMethod *method);
+
+static MonoGenericParam*
+verifier_get_generic_param_from_type (VerifyContext *ctx, MonoType *type);
 //////////////////////////////////////////////////////////////////
 
 
@@ -505,6 +508,21 @@ mono_type_is_valid_type_in_context (MonoType *type, MonoGenericContext *context)
                        if (!mono_type_is_valid_type_in_context (inst->type_argv [i], context))
                                return FALSE;
                break;
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_VALUETYPE: {
+               MonoClass *klass = type->data.klass;
+               /*
+                * It's possible to encode generic'sh types in such a way that they disguise themselves as class or valuetype.
+                * Fixing the type decoding is really tricky since under some cases this behavior is needed, for example, to
+                * have a 'class' type pointing to a 'genericinst' class.
+                *
+                * For the runtime these non canonical (weird) encodings work fine, they worst they can cause is some
+                * reflection oddities which are harmless  - to security at least.
+                */
+               if (klass->byval_arg.type != type->type)
+                       return mono_type_is_valid_type_in_context (&klass->byval_arg, context);
+               break;
+       }
        }
        return TRUE;
 }
@@ -538,23 +556,41 @@ is_valid_generic_instantiation (MonoGenericContainer *gc, MonoGenericContext *co
                MonoGenericParamInfo *param_info = mono_generic_container_get_param_info (gc, i);
                MonoClass *paramClass;
                MonoClass **constraints;
+               MonoType *param_type = ginst->type_argv [i];
 
-               if (!param_info->constraints && !(param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK))
+               /*it's not our job to validate type variables*/
+               if (mono_type_is_generic_argument (param_type))
                        continue;
-               if (mono_type_is_generic_argument (ginst->type_argv [i]))
-                       continue; //it's not our job to validate type variables
 
-               paramClass = mono_class_from_mono_type (ginst->type_argv [i]);
+               paramClass = mono_class_from_mono_type (param_type);
 
                if (paramClass->exception_type != MONO_EXCEPTION_NONE)
                        return FALSE;
 
+               /* A GTD can't be a generic argument.
+                *
+                * Due to how types are encoded we must check for the case of a genericinst MonoType and GTD MonoClass.
+                * This happens in cases such as: class Foo<T>  { void X() { new Bar<T> (); } }
+                *
+                * Open instantiations can have GTDs as this happens when one type is instantiated with others params
+                * and the former has an expansion into the later. For example:
+                * class B<K> {}
+                * class A<T>: B<K> {}
+                * The type A <K> has a parent B<K>, that is inflated into the GTD B<>.
+                * Since A<K> is open, thus not instantiatable, this is valid.
+                */
+               if (paramClass->generic_container && param_type->type != MONO_TYPE_GENERICINST && !ginst->is_open)
+                       return FALSE;
+
                /*it's not safe to call mono_class_init from here*/
                if (paramClass->generic_class && !paramClass->inited) {
                        if (!mono_class_is_valid_generic_instantiation (NULL, paramClass))
                                return FALSE;
                }
 
+               if (!param_info->constraints && !(param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK))
+                       continue;
+
                if ((param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) && (!paramClass->valuetype || mono_class_is_nullable (paramClass)))
                        return FALSE;
 
@@ -592,7 +628,7 @@ is_valid_generic_instantiation (MonoGenericContainer *gc, MonoGenericContext *co
  * This means that @candidate constraints are a super set of @target constaints
  */
 static gboolean
-mono_generic_param_is_constraint_compatible (VerifyContext *ctx, MonoGenericParam *target, MonoGenericParam *candidate, MonoGenericContext *context)
+mono_generic_param_is_constraint_compatible (VerifyContext *ctx, MonoGenericParam *target, MonoGenericParam *candidate, MonoClass *candidate_param_class, MonoGenericContext *context)
 {
        MonoGenericParamInfo *tinfo = mono_generic_param_info (target);
        MonoGenericParamInfo *cinfo = mono_generic_param_info (candidate);
@@ -604,8 +640,6 @@ mono_generic_param_is_constraint_compatible (VerifyContext *ctx, MonoGenericPara
 
        if (tinfo->constraints) {
                MonoClass **target_class, **candidate_class;
-               if (!cinfo->constraints)
-                       return FALSE;
                for (target_class = tinfo->constraints; *target_class; ++target_class) {
                        MonoClass *tc;
                        MonoType *inflated = verifier_inflate_type (ctx, &(*target_class)->byval_arg, context);
@@ -614,6 +648,16 @@ mono_generic_param_is_constraint_compatible (VerifyContext *ctx, MonoGenericPara
                        tc = mono_class_from_mono_type (inflated);
                        mono_metadata_free_type (inflated);
 
+                       /*
+                        * A constraint from @target might inflate into @candidate itself and in that case we don't need
+                        * check it's constraints since it satisfy the constraint by itself.
+                        */
+                       if (mono_metadata_type_equal (&tc->byval_arg, &candidate_param_class->byval_arg))
+                               continue;
+
+                       if (!cinfo->constraints)
+                               return FALSE;
+
                        for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) {
                                MonoClass *cc;
                                inflated = verifier_inflate_type (ctx, &(*candidate_class)->byval_arg, ctx->generic_context);
@@ -624,6 +668,23 @@ mono_generic_param_is_constraint_compatible (VerifyContext *ctx, MonoGenericPara
 
                                if (mono_class_is_assignable_from (tc, cc))
                                        break;
+
+                               /*
+                                * This happens when we have the following:
+                                *
+                                * Bar<K> where K : IFace
+                                * Foo<T, U> where T : U where U : IFace
+                                *      ...
+                                *      Bar<T> <- T here satisfy K constraint transitively through to U's constraint
+                                *
+                                */
+                               if (mono_type_is_generic_argument (&cc->byval_arg)) {
+                                       MonoGenericParam *other_candidate = verifier_get_generic_param_from_type (ctx, &cc->byval_arg);
+
+                                       if (mono_generic_param_is_constraint_compatible (ctx, target, other_candidate, cc, context)) {
+                                               break;
+                                       }
+                               }
                        }
                        if (!*candidate_class)
                                return FALSE;
@@ -653,7 +714,7 @@ verifier_get_generic_param_from_type (VerifyContext *ctx, MonoType *type)
                gc = mono_method_get_generic_container (gmd);
        }
        if (!gc)
-               return FALSE;
+               return NULL;
        return mono_generic_container_get_param (gc, num);
 }
 
@@ -690,6 +751,7 @@ generic_arguments_respect_constraints (VerifyContext *ctx, MonoGenericContainer
                MonoType *type = ginst->type_argv [i];
                MonoGenericParam *target = mono_generic_container_get_param (gc, i);
                MonoGenericParam *candidate;
+               MonoClass *candidate_class;
 
                if (!mono_type_is_generic_argument (type))
                        continue;
@@ -698,8 +760,9 @@ generic_arguments_respect_constraints (VerifyContext *ctx, MonoGenericContainer
                        return FALSE;
 
                candidate = verifier_get_generic_param_from_type (ctx, type);
+               candidate_class = mono_class_from_mono_type (type);
 
-               if (!mono_generic_param_is_constraint_compatible (ctx, target, candidate, context))
+               if (!mono_generic_param_is_constraint_compatible (ctx, target, candidate, candidate_class, context))
                        return FALSE;
        }
        return TRUE;
@@ -777,6 +840,7 @@ mono_type_is_valid_in_context (VerifyContext *ctx, MonoType *type)
                        ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic instantiation of type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD);
                else
                        ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Could not load type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD);
+               mono_loader_clear_error ();
                return FALSE;
        }
 
@@ -834,14 +898,22 @@ verifier_load_field (VerifyContext *ctx, int token, MonoClass **out_klass, const
        }
 
        field = mono_field_from_token (ctx->image, token, &klass, ctx->generic_context);
-       if (!field || !field->parent || !klass) {
+       if (!field || !field->parent || !klass || mono_loader_get_last_error ()) {
                ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Cannot load field from token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
+               mono_loader_clear_error ();
                return NULL;
        }
 
        if (!mono_type_is_valid_in_context (ctx, &klass->byval_arg))
                return NULL;
 
+       if (mono_field_get_flags (field) & FIELD_ATTRIBUTE_LITERAL) {
+               char *type_name = mono_type_get_full_name (field->parent);
+               ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Cannot reference literal field %s::%s at 0x%04x", type_name, field->name, ctx->ip_offset));
+               g_free (type_name);
+               return NULL;
+       }
+
        *out_klass = klass;
        return field;
 }
@@ -857,8 +929,9 @@ verifier_load_method (VerifyContext *ctx, int token, const char *opcode) {
 
        method = mono_get_method_full (ctx->image, token, NULL, ctx->generic_context);
 
-       if (!method) {
+       if (!method || mono_loader_get_last_error ()) {
                ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Cannot load method from token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
+               mono_loader_clear_error ();
                return NULL;
        }
        
@@ -879,8 +952,9 @@ verifier_load_type (VerifyContext *ctx, int token, const char *opcode) {
 
        type = mono_type_get_full (ctx->image, token, ctx->generic_context);
 
-       if (!type) {
+       if (!type || mono_loader_get_last_error ()) {
                ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Cannot load type from token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
+               mono_loader_clear_error ();
                return NULL;
        }
 
@@ -2156,6 +2230,30 @@ get_generic_param (VerifyContext *ctx, MonoType *param)
        return ctx->generic_context->method_inst->type_argv [param_num]->data.generic_param;
        
 }
+
+static gboolean
+recursive_boxed_constraint_type_check (VerifyContext *ctx, MonoType *type, MonoClass *constraint_class, int recursion_level)
+{
+       MonoType *constraint_type = &constraint_class->byval_arg;
+       if (recursion_level <= 0)
+               return FALSE;
+
+       if (verify_type_compatibility_full (ctx, type, mono_type_get_type_byval (constraint_type), FALSE))
+               return TRUE;
+
+       if (mono_type_is_generic_argument (constraint_type)) {
+               MonoGenericParam *param = get_generic_param (ctx, constraint_type);
+               MonoClass **class;
+               if (!param)
+                       return FALSE;
+               for (class = mono_generic_param_info (param)->constraints; class && *class; ++class) {
+                       if (recursive_boxed_constraint_type_check (ctx, type, *class, recursion_level - 1))
+                               return TRUE;
+               }
+       }
+       return FALSE;
+}
+
 /*
  * is_compatible_boxed_valuetype:
  * 
@@ -2178,8 +2276,12 @@ is_compatible_boxed_valuetype (VerifyContext *ctx, MonoType *type, MonoType *can
        if (mono_type_is_generic_argument (candidate)) {
                MonoGenericParam *param = get_generic_param (ctx, candidate);
                MonoClass **class;
+               if (!param)
+                       return FALSE;
+
                for (class = mono_generic_param_info (param)->constraints; class && *class; ++class) {
-                       if (verify_type_compatibility_full (ctx, type, mono_type_get_type_byval (& (*class)->byval_arg), FALSE))
+                       /*256 should be enough since there can't be more than 255 generic arguments.*/
+                       if (recursive_boxed_constraint_type_check (ctx, type, *class, 256))
                                return TRUE;
                }
        }
@@ -2351,6 +2453,12 @@ mono_delegate_signature_equal (MonoMethodSignature *delegate_sig, MonoMethodSign
        return TRUE;
 }
 
+gboolean
+mono_verifier_is_signature_compatible (MonoMethodSignature *target, MonoMethodSignature *candidate)
+{
+       return mono_delegate_signature_equal (target, candidate, FALSE);
+}
+
 /* 
  * verify_ldftn_delegate:
  * 
@@ -2923,8 +3031,16 @@ do_invoke_method (VerifyContext *ctx, int method_token, gboolean virtual)
                        if (method->klass->valuetype && (stack_slot_is_boxed_value (value) || !stack_slot_is_managed_pointer (value)))
                                CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use a boxed or literal valuetype to call a valuetype method at 0x%04x", ctx->ip_offset));
                }
-               if (!verify_stack_type_compatibility (ctx, type, &copy))
-                       CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible this argument on stack with method signature at 0x%04x", ctx->ip_offset));
+               if (!verify_stack_type_compatibility (ctx, type, &copy)) {
+                       char *expected = mono_type_full_name (type);
+                       char *effective = stack_slot_full_name (&copy);
+                       char *method_name = mono_method_full_name (method, TRUE);
+                       CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible this argument on stack with method signature expected '%s' but got '%s' for a call to '%s' at 0x%04x",
+                                       expected, effective, method_name, ctx->ip_offset));
+                       g_free (method_name);
+                       g_free (effective);
+                       g_free (expected);
+               }
 
                if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_method_full (ctx->method, method, mono_class_from_mono_type (value->type))) {
                        char *name = mono_method_full_name (method, TRUE);
@@ -3014,8 +3130,14 @@ do_store_static_field (VerifyContext *ctx, int token) {
        if (!IS_SKIP_VISIBILITY (ctx) && !mono_method_can_access_field_full (ctx->method, field, NULL))
                CODE_NOT_VERIFIABLE2 (ctx, g_strdup_printf ("Type at stack is not accessible at 0x%04x", ctx->ip_offset), MONO_EXCEPTION_FIELD_ACCESS);
 
-       if (!verify_stack_type_compatibility (ctx, field->type, value))
-               CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible type %s in static field store at 0x%04x", stack_slot_get_name (value), ctx->ip_offset));       
+       if (!verify_stack_type_compatibility (ctx, field->type, value)) {
+               char *stack_name = stack_slot_full_name (value);
+               char *field_name = mono_type_full_name (field->type);
+               CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Incompatible type in static field store expected '%s' but found '%s' at 0x%04x",
+                               field_name, stack_name, ctx->ip_offset));
+               g_free (field_name);
+               g_free (stack_name);
+       }
 }
 
 static gboolean
@@ -3265,6 +3387,25 @@ do_load_token (VerifyContext *ctx, int token)
        MonoClass *handle_class;
        if (!check_overflow (ctx))
                return;
+
+       switch (token & 0xff000000) {
+       case MONO_TOKEN_TYPE_DEF:
+       case MONO_TOKEN_TYPE_REF:
+       case MONO_TOKEN_TYPE_SPEC:
+       case MONO_TOKEN_FIELD_DEF:
+       case MONO_TOKEN_METHOD_DEF:
+       case MONO_TOKEN_METHOD_SPEC:
+       case MONO_TOKEN_MEMBER_REF:
+               if (!token_bounds_check (ctx->image, token)) {
+                       ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Table index out of range 0x%x for token %x for ldtoken at 0x%04x", mono_metadata_token_index (token), token, ctx->ip_offset));
+                       return;
+               }
+               break;
+       default:
+               ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid table 0x%x for token 0x%x for ldtoken at 0x%04x", mono_metadata_token_table (token), token, ctx->ip_offset));
+               return;
+       }
+
        handle = mono_ldtoken (ctx->image, token, &handle_class, ctx->generic_context);
        if (!handle) {
                ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid token 0x%x for ldtoken at 0x%04x", token, ctx->ip_offset));
@@ -3338,8 +3479,13 @@ do_stobj (VerifyContext *ctx, int token)
        if (stack_slot_is_boxed_value (src) && !MONO_TYPE_IS_REFERENCE (src->type) && !MONO_TYPE_IS_REFERENCE (type))
                CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Cannot use stobj with a boxed source value that is not a reference type at 0x%04x", ctx->ip_offset));
 
-       if (!verify_stack_type_compatibility (ctx, type, src))
-               CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Token and source types of stobj don't match at 0x%04x", ctx->ip_offset));
+       if (!verify_stack_type_compatibility (ctx, type, src)) {
+               char *type_name = mono_type_full_name (type);
+               char *src_name = stack_slot_full_name (src);
+               CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Token '%s' and source '%s' of stobj don't match ' at 0x%04x", type_name, src_name, ctx->ip_offset));
+               g_free (type_name);
+               g_free (src_name);
+       }
 
        if (!verify_type_compatibility (ctx, mono_type_get_type_byval (dest->type), type))
                CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Destination and token types of stobj don't match at 0x%04x", ctx->ip_offset));
@@ -3445,6 +3591,11 @@ do_newobj (VerifyContext *ctx, int token)
                return;
        }
 
+       if (!sig->hasthis) {
+               ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Invalid constructor signature missing hasthis at 0x%04x", ctx->ip_offset));
+               return;
+       }
+
        if (!check_underflow (ctx, sig->param_count))
                return;
 
@@ -3491,7 +3642,7 @@ do_cast (VerifyContext *ctx, int token, const char *opcode) {
        if (!check_underflow (ctx, 1))
                return;
 
-       if (!(type = verifier_load_type (ctx, token, opcode)))
+       if (!(type = get_boxable_mono_type (ctx, token, opcode)))
                return;
 
        if (type->byref) {
@@ -3890,6 +4041,7 @@ do_leave (VerifyContext *ctx, int delta)
        if (!is_correct_leave (ctx->header, ctx->ip_offset, target))
                CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Leave not allowed in finally block at 0x%04x", ctx->ip_offset));
        ctx->eval.size = 0;
+       ctx->target = target;
 }
 
 /* 
@@ -4582,10 +4734,11 @@ mono_method_verify (MonoMethod *method, int level)
        g_assert (bb);
 
        while (ip < end && ctx.valid) {
+               int op_size;
                ip_offset = ip - code_start;
                {
                        const unsigned char *ip_copy = ip;
-                       int size, op;
+                       int op;
 
                        if (ip_offset > bb->end) {
                                ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch or EH block at [0x%04x] targets middle instruction at 0x%04x", bb->end, ip_offset));
@@ -4595,29 +4748,22 @@ mono_method_verify (MonoMethod *method, int level)
                        if (ip_offset == bb->end)
                                bb = bb->next;
        
-                       size = mono_opcode_value_and_size (&ip_copy, end, &op);
-                       if (size == -1) {
+                       op_size = mono_opcode_value_and_size (&ip_copy, end, &op);
+                       if (op_size == -1) {
                                ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Invalid instruction %x at 0x%04x", *ip, ip_offset));
                                goto cleanup;
                        }
 
-                       if (ADD_IS_GREATER_OR_OVF (ip_offset, size, bb->end)) {
+                       if (ADD_IS_GREATER_OR_OVF (ip_offset, op_size, bb->end)) {
                                ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch or EH block targets middle of instruction at 0x%04x", ip_offset));
                                goto cleanup;
                        }
 
                        /*Last Instruction*/
-                       if (ip_offset + size == bb->end && mono_opcode_is_prefix (op)) {
+                       if (ip_offset + op_size == bb->end && mono_opcode_is_prefix (op)) {
                                ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Branch or EH block targets between prefix '%s' and instruction at 0x%04x", mono_opcode_name (op), ip_offset));
                                goto cleanup;
                        }
-
-                       if (bb->dead) {
-                               /*FIXME remove this once we move all bad branch checking code to use BB only*/
-                               ctx.code [ip_offset].flags |= IL_CODE_FLAG_SEEN;
-                               ip += size;
-                               continue;
-                       }
                }
 
                ctx.ip_offset = ip_offset = ip - code_start;
@@ -4659,6 +4805,14 @@ mono_method_verify (MonoMethod *method, int level)
                        }
                }
 
+               /*This must be done after fallthru detection otherwise it won't happen.*/
+               if (bb->dead) {
+                       /*FIXME remove this once we move all bad branch checking code to use BB only*/
+                       ctx.code [ip_offset].flags |= IL_CODE_FLAG_SEEN;
+                       ip += op_size;
+                       continue;
+               }
+
                if (!ctx.valid)
                        break;
 
@@ -5290,6 +5444,7 @@ mono_method_verify (MonoMethod *method, int level)
                        do_leave (&ctx, read32 (ip + 1) + 5);
                        ip += 5;
                        start = 1;
+                       need_merge = 1;
                        break;
 
                case CEE_LEAVE_S:
@@ -5297,6 +5452,7 @@ mono_method_verify (MonoMethod *method, int level)
                        do_leave (&ctx, (signed char)ip [1] + 2);
                        ip += 2;
                        start = 1;
+                       need_merge = 1;
                        break;
 
                case CEE_PREFIX1:
@@ -5330,7 +5486,8 @@ mono_method_verify (MonoMethod *method, int level)
 
 
                        case CEE_ARGLIST:
-                               check_overflow (&ctx);
+                               if (!check_overflow (&ctx))
+                                       break;
                                if (ctx.signature->call_convention != MONO_CALL_VARARG)
                                        ADD_VERIFY_ERROR (&ctx, g_strdup_printf ("Cannot use arglist on method without VARGARG calling convention at 0x%04x", ctx.ip_offset));
                                set_stack_value (&ctx, stack_push (&ctx), &mono_defaults.argumenthandle_class->byval_arg, FALSE);
@@ -5768,11 +5925,41 @@ verify_valuetype_layout (MonoClass *class)
        return res;
 }
 
+static gboolean
+recursive_mark_constraint_args (MonoBitSet *used_args, MonoGenericContainer *gc, MonoType *type)
+{
+       int idx;
+       MonoClass **constraints;
+       MonoGenericParamInfo *param_info;
+
+       g_assert (mono_type_is_generic_argument (type));
+
+       idx = mono_type_get_generic_param_num (type);
+       if (mono_bitset_test_fast (used_args, idx))
+               return FALSE;
+
+       mono_bitset_set_fast (used_args, idx);
+       param_info = mono_generic_container_get_param_info (gc, idx);
+
+       if (!param_info->constraints)
+               return TRUE;
+
+       for (constraints = param_info->constraints; *constraints; ++constraints) {
+               MonoClass *ctr = *constraints;
+               MonoType *constraint_type = &ctr->byval_arg;
+
+               if (mono_type_is_generic_argument (constraint_type) && !recursive_mark_constraint_args (used_args, gc, constraint_type))
+                       return FALSE;
+       }
+       return TRUE;
+}
+
 static gboolean
 verify_generic_parameters (MonoClass *class)
 {
        int i;
        MonoGenericContainer *gc = class->generic_container;
+       MonoBitSet *used_args = mono_bitset_new (gc->type_argc, 0);
 
        for (i = 0; i < gc->type_argc; ++i) {
                MonoGenericParamInfo *param_info = mono_generic_container_get_param_info (gc, i);
@@ -5781,14 +5968,28 @@ verify_generic_parameters (MonoClass *class)
                if (!param_info->constraints)
                        continue;
 
+               mono_bitset_clear_all (used_args);
+               mono_bitset_set_fast (used_args, i);
+
                for (constraints = param_info->constraints; *constraints; ++constraints) {
                        MonoClass *ctr = *constraints;
+                       MonoType *constraint_type = &ctr->byval_arg;
 
-                       if (!mono_type_is_valid_type_in_context (&ctr->byval_arg, &gc->context))
-                               return FALSE;
+                       if (!mono_type_is_valid_type_in_context (constraint_type, &gc->context))
+                               goto fail;
+
+                       if (mono_type_is_generic_argument (constraint_type) && !recursive_mark_constraint_args (used_args, gc, constraint_type))
+                               goto fail;
+                       if (ctr->generic_class && !mono_class_is_valid_generic_instantiation (NULL, ctr))
+                               goto fail;
                }
        }
+       mono_bitset_free (used_args);
        return TRUE;
+
+fail:
+       mono_bitset_free (used_args);
+       return FALSE;
 }
 
 /*
@@ -5809,8 +6010,12 @@ mono_verifier_verify_class (MonoClass *class)
                !MONO_CLASS_IS_INTERFACE (class) &&
                (!class->image->dynamic && class->type_token != 0x2000001)) /*<Module> is the first type in the assembly*/
                return FALSE;
-       if (class->parent && MONO_CLASS_IS_INTERFACE (class->parent))
-               return FALSE;
+       if (class->parent) {
+               if (MONO_CLASS_IS_INTERFACE (class->parent))
+                       return FALSE;
+               if (!class->generic_class && class->parent->generic_container)
+                       return FALSE;
+       }
        if (class->generic_container && (class->flags & TYPE_ATTRIBUTE_LAYOUT_MASK) == TYPE_ATTRIBUTE_EXPLICIT_LAYOUT)
                return FALSE;
        if (class->generic_container && !verify_generic_parameters (class))