X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fverify.c;h=180c80daa7c5913bca2e0be80e033debe54bd917;hb=e152071d2a0061d7aa6d7f40b220b78f43132aa2;hp=bfae7b11cd8119e6f7f8921002f35ee6138d9831;hpb=a4d731119594f3fc75ecb47e3b68a984596de6ea;p=mono.git diff --git a/mono/metadata/verify.c b/mono/metadata/verify.c index bfae7b11cd8..180c80daa7c 100644 --- a/mono/metadata/verify.c +++ b/mono/metadata/verify.c @@ -30,7 +30,6 @@ #include #include - static MiniVerifierMode verifier_mode = MONO_VERIFIER_MODE_OFF; static gboolean verify_all = FALSE; @@ -232,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); ////////////////////////////////////////////////////////////////// @@ -506,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; } @@ -539,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 { void X() { new Bar (); } } + * + * 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 {} + * class A: B {} + * The type A has a parent B, that is inflated into the GTD B<>. + * Since A 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; @@ -593,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); @@ -605,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); @@ -615,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); @@ -625,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 where K : IFace + * Foo where T : U where U : IFace + * ... + * Bar <- 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; @@ -654,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); } @@ -691,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; @@ -699,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; @@ -778,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; } @@ -835,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; } @@ -858,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; } @@ -880,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; } @@ -2380,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: * @@ -2952,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, ©)) - 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, ©)) { + char *expected = mono_type_full_name (type); + char *effective = stack_slot_full_name (©); + 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); @@ -3043,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 @@ -3294,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)); @@ -3367,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)); @@ -3474,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; @@ -3520,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) { @@ -3919,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; } /* @@ -4611,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)); @@ -4624,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; @@ -4688,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; @@ -5319,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: @@ -5326,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: @@ -5359,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); @@ -5852,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); @@ -5880,8 +6010,12 @@ mono_verifier_verify_class (MonoClass *class) !MONO_CLASS_IS_INTERFACE (class) && (!class->image->dynamic && class->type_token != 0x2000001)) /* 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))