New test.
[mono.git] / mono / metadata / verify.c
index e344637b2cde3e759d37fc603f045623496a4634..180c80daa7c5913bca2e0be80e033debe54bd917 100644 (file)
@@ -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;
 
@@ -632,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;
@@ -661,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);
 }
 
@@ -854,6 +907,13 @@ verifier_load_field (VerifyContext *ctx, int token, MonoClass **out_klass, const
        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;
 }
@@ -2393,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:
  * 
@@ -3525,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;
 
@@ -3571,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) {
@@ -4663,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));
@@ -4676,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;
@@ -4740,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;
 
@@ -5907,6 +5980,8 @@ verify_generic_parameters (MonoClass *class)
 
                        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);
@@ -5935,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))