/mono/metadata/verify.c
C | 6374 lines | 4891 code | 987 blank | 496 comment | 1403 complexity | f77ac059fe004a60662c03edb9e66b82 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
Large files files are truncated, but you can click here to view the full file
- /*
- * verify.c:
- *
- * Author:
- * Mono Project (http://www.mono-project.com)
- *
- * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
- * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
- * Copyright 2011 Rodrigo Kumpera
- */
- #include <config.h>
- #include <mono/metadata/object-internals.h>
- #include <mono/metadata/verify.h>
- #include <mono/metadata/verify-internals.h>
- #include <mono/metadata/opcodes.h>
- #include <mono/metadata/tabledefs.h>
- #include <mono/metadata/reflection.h>
- #include <mono/metadata/debug-helpers.h>
- #include <mono/metadata/mono-endian.h>
- #include <mono/metadata/metadata.h>
- #include <mono/metadata/metadata-internals.h>
- #include <mono/metadata/class-internals.h>
- #include <mono/metadata/security-manager.h>
- #include <mono/metadata/security-core-clr.h>
- #include <mono/metadata/tokentype.h>
- #include <mono/metadata/mono-basic-block.h>
- #include <mono/metadata/attrdefs.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;
- /*
- * Set the desired level of checks for the verfier.
- *
- */
- void
- mono_verifier_set_mode (MiniVerifierMode mode)
- {
- verifier_mode = mode;
- }
- void
- mono_verifier_enable_verify_all ()
- {
- verify_all = TRUE;
- }
- #ifndef DISABLE_VERIFIER
- /*
- * Pull the list of opcodes
- */
- #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
- a = i,
- enum {
- #include "mono/cil/opcode.def"
- LAST = 0xff
- };
- #undef OPDEF
- #ifdef MONO_VERIFIER_DEBUG
- #define VERIFIER_DEBUG(code) do { code } while (0)
- #else
- #define VERIFIER_DEBUG(code)
- #endif
- //////////////////////////////////////////////////////////////////
- #define IS_STRICT_MODE(ctx) (((ctx)->level & MONO_VERIFY_NON_STRICT) == 0)
- #define IS_FAIL_FAST_MODE(ctx) (((ctx)->level & MONO_VERIFY_FAIL_FAST) == MONO_VERIFY_FAIL_FAST)
- #define IS_SKIP_VISIBILITY(ctx) (((ctx)->level & MONO_VERIFY_SKIP_VISIBILITY) == MONO_VERIFY_SKIP_VISIBILITY)
- #define IS_REPORT_ALL_ERRORS(ctx) (((ctx)->level & MONO_VERIFY_REPORT_ALL_ERRORS) == MONO_VERIFY_REPORT_ALL_ERRORS)
- #define CLEAR_PREFIX(ctx, prefix) do { (ctx)->prefix_set &= ~(prefix); } while (0)
- #define ADD_VERIFY_INFO(__ctx, __msg, __status, __exception) \
- do { \
- MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \
- vinfo->info.status = __status; \
- vinfo->info.message = ( __msg ); \
- vinfo->exception_type = (__exception); \
- (__ctx)->list = g_slist_prepend ((__ctx)->list, vinfo); \
- } while (0)
- //TODO support MONO_VERIFY_REPORT_ALL_ERRORS
- #define ADD_VERIFY_ERROR(__ctx, __msg) \
- do { \
- ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, MONO_EXCEPTION_INVALID_PROGRAM); \
- (__ctx)->valid = 0; \
- } while (0)
- #define CODE_NOT_VERIFIABLE(__ctx, __msg) \
- do { \
- if ((__ctx)->verifiable || IS_REPORT_ALL_ERRORS (__ctx)) { \
- ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_NOT_VERIFIABLE, MONO_EXCEPTION_UNVERIFIABLE_IL); \
- (__ctx)->verifiable = 0; \
- if (IS_FAIL_FAST_MODE (__ctx)) \
- (__ctx)->valid = 0; \
- } \
- } while (0)
- #define ADD_VERIFY_ERROR2(__ctx, __msg, __exception) \
- do { \
- ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_ERROR, __exception); \
- (__ctx)->valid = 0; \
- } while (0)
- #define CODE_NOT_VERIFIABLE2(__ctx, __msg, __exception) \
- do { \
- if ((__ctx)->verifiable || IS_REPORT_ALL_ERRORS (__ctx)) { \
- ADD_VERIFY_INFO(__ctx, __msg, MONO_VERIFY_NOT_VERIFIABLE, __exception); \
- (__ctx)->verifiable = 0; \
- if (IS_FAIL_FAST_MODE (__ctx)) \
- (__ctx)->valid = 0; \
- } \
- } while (0)
- #define CHECK_ADD4_OVERFLOW_UN(a, b) ((guint32)(0xFFFFFFFFU) - (guint32)(b) < (guint32)(a))
- #define CHECK_ADD8_OVERFLOW_UN(a, b) ((guint64)(0xFFFFFFFFFFFFFFFFUL) - (guint64)(b) < (guint64)(a))
- #if SIZEOF_VOID_P == 4
- #define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD4_OVERFLOW_UN(a, b)
- #else
- #define CHECK_ADDP_OVERFLOW_UN(a,b) CHECK_ADD8_OVERFLOW_UN(a, b)
- #endif
- #define ADDP_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADDP_OVERFLOW_UN (a, b))
- #define ADD_IS_GREATER_OR_OVF(a, b, c) (((a) + (b) > (c)) || CHECK_ADD4_OVERFLOW_UN (a, b))
- /*Flags to be used with ILCodeDesc::flags */
- enum {
- /*Instruction has not been processed.*/
- IL_CODE_FLAG_NOT_PROCESSED = 0,
- /*Instruction was decoded by mono_method_verify loop.*/
- IL_CODE_FLAG_SEEN = 1,
- /*Instruction was target of a branch or is at a protected block boundary.*/
- IL_CODE_FLAG_WAS_TARGET = 2,
- /*Used by stack_init to avoid double initialize each entry.*/
- IL_CODE_FLAG_STACK_INITED = 4,
- /*Used by merge_stacks to decide if it should just copy the eval stack.*/
- IL_CODE_STACK_MERGED = 8,
- /*This instruction is part of the delegate construction sequence, it cannot be target of a branch.*/
- IL_CODE_DELEGATE_SEQUENCE = 0x10,
- /*This is a delegate created from a ldftn to a non final virtual method*/
- IL_CODE_LDFTN_DELEGATE_NONFINAL_VIRTUAL = 0x20,
- /*This is a call to a non final virtual method*/
- IL_CODE_CALL_NONFINAL_VIRTUAL = 0x40,
- };
- typedef enum {
- RESULT_VALID,
- RESULT_UNVERIFIABLE,
- RESULT_INVALID
- } verify_result_t;
- typedef struct {
- MonoType *type;
- int stype;
- MonoMethod *method;
- } ILStackDesc;
- typedef struct {
- ILStackDesc *stack;
- guint16 size, max_size;
- guint16 flags;
- } ILCodeDesc;
- typedef struct {
- int max_args;
- int max_stack;
- int verifiable;
- int valid;
- int level;
- int code_size;
- ILCodeDesc *code;
- ILCodeDesc eval;
- MonoType **params;
- GSList *list;
- /*Allocated fnptr MonoType that should be freed by us.*/
- GSList *funptrs;
- /*Type dup'ed exception types from catch blocks.*/
- GSList *exception_types;
- int num_locals;
- MonoType **locals;
- /*TODO get rid of target here, need_merge in mono_method_verify and hoist the merging code in the branching code*/
- int target;
- guint32 ip_offset;
- MonoMethodSignature *signature;
- MonoMethodHeader *header;
- MonoGenericContext *generic_context;
- MonoImage *image;
- MonoMethod *method;
- /*This flag helps solving a corner case of delegate verification in that you cannot have a "starg 0"
- *on a method that creates a delegate for a non-final virtual method using ldftn*/
- gboolean has_this_store;
- /*This flag is used to control if the contructor of the parent class has been called.
- *If the this pointer is pushed on the eval stack and it's a reference type constructor and
- * super_ctor_called is false, the uninitialized flag is set on the pushed value.
- *
- * Poping an uninitialized this ptr from the eval stack is an unverifiable operation unless
- * the safe variant is used. Only a few opcodes can use it : dup, pop, ldfld, stfld and call to a constructor.
- */
- gboolean super_ctor_called;
- guint32 prefix_set;
- gboolean has_flags;
- MonoType *constrained_type;
- } VerifyContext;
- static void
- merge_stacks (VerifyContext *ctx, ILCodeDesc *from, ILCodeDesc *to, gboolean start, gboolean external);
- static int
- get_stack_type (MonoType *type);
- static gboolean
- mono_delegate_signature_equal (MonoMethodSignature *delegate_sig, MonoMethodSignature *method_sig, gboolean is_static_ldftn);
- static gboolean
- 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);
- static gboolean
- verifier_class_is_assignable_from (MonoClass *target, MonoClass *candidate);
- //////////////////////////////////////////////////////////////////
- enum {
- TYPE_INV = 0, /* leave at 0. */
- TYPE_I4 = 1,
- TYPE_I8 = 2,
- TYPE_NATIVE_INT = 3,
- TYPE_R8 = 4,
- /* Used by operator tables to resolve pointer types (managed & unmanaged) and by unmanaged pointer types*/
- TYPE_PTR = 5,
- /* value types and classes */
- TYPE_COMPLEX = 6,
- /* Number of types, used to define the size of the tables*/
- TYPE_MAX = 6,
- /* Used by tables to signal that a result is not verifiable*/
- NON_VERIFIABLE_RESULT = 0x80,
- /*Mask used to extract just the type, excluding flags */
- TYPE_MASK = 0x0F,
- /* The stack type is a managed pointer, unmask the value to res */
- POINTER_MASK = 0x100,
-
- /*Stack type with the pointer mask*/
- RAW_TYPE_MASK = 0x10F,
- /* Controlled Mutability Manager Pointer */
- CMMP_MASK = 0x200,
- /* The stack type is a null literal*/
- NULL_LITERAL_MASK = 0x400,
-
- /**Used by ldarg.0 and family to let delegate verification happens.*/
- THIS_POINTER_MASK = 0x800,
- /**Signals that this is a boxed value type*/
- BOXED_MASK = 0x1000,
- /*This is an unitialized this ref*/
- UNINIT_THIS_MASK = 0x2000,
- };
- static const char* const
- type_names [TYPE_MAX + 1] = {
- "Invalid",
- "Int32",
- "Int64",
- "Native Int",
- "Float64",
- "Native Pointer",
- "Complex"
- };
- enum {
- PREFIX_UNALIGNED = 1,
- PREFIX_VOLATILE = 2,
- PREFIX_TAIL = 4,
- PREFIX_CONSTRAINED = 8,
- PREFIX_READONLY = 16
- };
- //////////////////////////////////////////////////////////////////
- #ifdef ENABLE_VERIFIER_STATS
- #define MEM_ALLOC(amt) do { allocated_memory += (amt); working_set += (amt); } while (0)
- #define MEM_FREE(amt) do { working_set -= (amt); } while (0)
- static int allocated_memory;
- static int working_set;
- static int max_allocated_memory;
- static int max_working_set;
- static int total_allocated_memory;
- static void
- finish_collect_stats (void)
- {
- max_allocated_memory = MAX (max_allocated_memory, allocated_memory);
- max_working_set = MAX (max_working_set, working_set);
- total_allocated_memory += allocated_memory;
- allocated_memory = working_set = 0;
- }
- static void
- init_verifier_stats (void)
- {
- static gboolean inited;
- if (!inited) {
- inited = TRUE;
- mono_counters_register ("Maximum memory allocated during verification", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &max_allocated_memory);
- mono_counters_register ("Maximum memory used during verification", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &max_working_set);
- mono_counters_register ("Total memory allocated for verification", MONO_COUNTER_METADATA | MONO_COUNTER_INT, &total_allocated_memory);
- }
- }
- #else
- #define MEM_ALLOC(amt) do {} while (0)
- #define MEM_FREE(amt) do { } while (0)
- #define finish_collect_stats()
- #define init_verifier_stats()
- #endif
- //////////////////////////////////////////////////////////////////
- /*Token validation macros and functions */
- #define IS_MEMBER_REF(token) (mono_metadata_token_table (token) == MONO_TABLE_MEMBERREF)
- #define IS_METHOD_DEF(token) (mono_metadata_token_table (token) == MONO_TABLE_METHOD)
- #define IS_METHOD_SPEC(token) (mono_metadata_token_table (token) == MONO_TABLE_METHODSPEC)
- #define IS_FIELD_DEF(token) (mono_metadata_token_table (token) == MONO_TABLE_FIELD)
- #define IS_TYPE_REF(token) (mono_metadata_token_table (token) == MONO_TABLE_TYPEREF)
- #define IS_TYPE_DEF(token) (mono_metadata_token_table (token) == MONO_TABLE_TYPEDEF)
- #define IS_TYPE_SPEC(token) (mono_metadata_token_table (token) == MONO_TABLE_TYPESPEC)
- #define IS_METHOD_DEF_OR_REF_OR_SPEC(token) (IS_METHOD_DEF (token) || IS_MEMBER_REF (token) || IS_METHOD_SPEC (token))
- #define IS_TYPE_DEF_OR_REF_OR_SPEC(token) (IS_TYPE_DEF (token) || IS_TYPE_REF (token) || IS_TYPE_SPEC (token))
- #define IS_FIELD_DEF_OR_REF(token) (IS_FIELD_DEF (token) || IS_MEMBER_REF (token))
- /*
- * Verify if @token refers to a valid row on int's table.
- */
- static gboolean
- token_bounds_check (MonoImage *image, guint32 token)
- {
- if (image->dynamic)
- return mono_reflection_is_valid_dynamic_token ((MonoDynamicImage*)image, token);
- return image->tables [mono_metadata_token_table (token)].rows >= mono_metadata_token_index (token) && mono_metadata_token_index (token) > 0;
- }
- static MonoType *
- mono_type_create_fnptr_from_mono_method (VerifyContext *ctx, MonoMethod *method)
- {
- MonoType *res = g_new0 (MonoType, 1);
- MEM_ALLOC (sizeof (MonoType));
- //FIXME use mono_method_get_signature_full
- res->data.method = mono_method_signature (method);
- res->type = MONO_TYPE_FNPTR;
- ctx->funptrs = g_slist_prepend (ctx->funptrs, res);
- return res;
- }
- /*
- * mono_type_is_enum_type:
- *
- * Returns TRUE if @type is an enum type.
- */
- static gboolean
- mono_type_is_enum_type (MonoType *type)
- {
- if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype)
- return TRUE;
- if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype)
- return TRUE;
- return FALSE;
- }
- /*
- * mono_type_is_value_type:
- *
- * Returns TRUE if @type is named after @namespace.@name.
- *
- */
- static gboolean
- mono_type_is_value_type (MonoType *type, const char *namespace, const char *name)
- {
- return type->type == MONO_TYPE_VALUETYPE &&
- !strcmp (namespace, type->data.klass->name_space) &&
- !strcmp (name, type->data.klass->name);
- }
- /*
- * Returns TURE if @type is VAR or MVAR
- */
- static gboolean
- mono_type_is_generic_argument (MonoType *type)
- {
- return type->type == MONO_TYPE_VAR || type->type == MONO_TYPE_MVAR;
- }
- /*
- * mono_type_get_underlying_type_any:
- *
- * This functions is just like mono_type_get_underlying_type but it doesn't care if the type is byref.
- *
- * Returns the underlying type of @type regardless if it is byref or not.
- */
- static MonoType*
- mono_type_get_underlying_type_any (MonoType *type)
- {
- if (type->type == MONO_TYPE_VALUETYPE && type->data.klass->enumtype)
- return mono_class_enum_basetype (type->data.klass);
- if (type->type == MONO_TYPE_GENERICINST && type->data.generic_class->container_class->enumtype)
- return mono_class_enum_basetype (type->data.generic_class->container_class);
- return type;
- }
- static G_GNUC_UNUSED const char*
- mono_type_get_stack_name (MonoType *type)
- {
- return type_names [get_stack_type (type) & TYPE_MASK];
- }
- #define CTOR_REQUIRED_FLAGS (METHOD_ATTRIBUTE_SPECIAL_NAME | METHOD_ATTRIBUTE_RT_SPECIAL_NAME)
- #define CTOR_INVALID_FLAGS (METHOD_ATTRIBUTE_STATIC)
- static gboolean
- mono_method_is_constructor (MonoMethod *method)
- {
- return ((method->flags & CTOR_REQUIRED_FLAGS) == CTOR_REQUIRED_FLAGS &&
- !(method->flags & CTOR_INVALID_FLAGS) &&
- !strcmp (".ctor", method->name));
- }
- static gboolean
- mono_class_has_default_constructor (MonoClass *klass)
- {
- MonoMethod *method;
- int i;
- mono_class_setup_methods (klass);
- if (klass->exception_type)
- return FALSE;
- for (i = 0; i < klass->method.count; ++i) {
- method = klass->methods [i];
- if (mono_method_is_constructor (method) &&
- mono_method_signature (method) &&
- mono_method_signature (method)->param_count == 0 &&
- (method->flags & METHOD_ATTRIBUTE_MEMBER_ACCESS_MASK) == METHOD_ATTRIBUTE_PUBLIC)
- return TRUE;
- }
- return FALSE;
- }
- /*
- * Verify if @type is valid for the given @ctx verification context.
- * this function checks for VAR and MVAR types that are invalid under the current verifier,
- */
- static gboolean
- mono_type_is_valid_type_in_context_full (MonoType *type, MonoGenericContext *context, gboolean check_gtd)
- {
- int i;
- MonoGenericInst *inst;
- switch (type->type) {
- case MONO_TYPE_VAR:
- case MONO_TYPE_MVAR:
- if (!context)
- return FALSE;
- inst = type->type == MONO_TYPE_VAR ? context->class_inst : context->method_inst;
- if (!inst || mono_type_get_generic_param_num (type) >= inst->type_argc)
- return FALSE;
- break;
- case MONO_TYPE_SZARRAY:
- return mono_type_is_valid_type_in_context_full (&type->data.klass->byval_arg, context, check_gtd);
- case MONO_TYPE_ARRAY:
- return mono_type_is_valid_type_in_context_full (&type->data.array->eklass->byval_arg, context, check_gtd);
- case MONO_TYPE_PTR:
- return mono_type_is_valid_type_in_context_full (type->data.type, context, check_gtd);
- case MONO_TYPE_GENERICINST:
- inst = type->data.generic_class->context.class_inst;
- if (!inst->is_open)
- break;
- for (i = 0; i < inst->type_argc; ++i)
- if (!mono_type_is_valid_type_in_context_full (inst->type_argv [i], context, check_gtd))
- 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, the 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_full (&klass->byval_arg, context, check_gtd);
- if (check_gtd && klass->generic_container)
- return FALSE;
- break;
- }
- }
- return TRUE;
- }
- static gboolean
- mono_type_is_valid_type_in_context (MonoType *type, MonoGenericContext *context)
- {
- return mono_type_is_valid_type_in_context_full (type, context, FALSE);
- }
- /*This function returns NULL if the type is not instantiatable*/
- static MonoType*
- verifier_inflate_type (VerifyContext *ctx, MonoType *type, MonoGenericContext *context)
- {
- MonoError error;
- MonoType *result;
- result = mono_class_inflate_generic_type_checked (type, context, &error);
- if (!mono_error_ok (&error)) {
- mono_error_cleanup (&error);
- return NULL;
- }
- return result;
- }
- /*A side note here. We don't need to check if arguments are broken since this
- is only need to be done by the runtime before realizing the type.
- */
- static gboolean
- is_valid_generic_instantiation (MonoGenericContainer *gc, MonoGenericContext *context, MonoGenericInst *ginst)
- {
- MonoError error;
- int i;
- if (ginst->type_argc != gc->type_argc)
- return FALSE;
- for (i = 0; i < gc->type_argc; ++i) {
- MonoGenericParamInfo *param_info = mono_generic_container_get_param_info (gc, i);
- MonoClass *paramClass;
- MonoClass **constraints;
- MonoType *param_type = ginst->type_argv [i];
- /*it's not our job to validate type variables*/
- if (mono_type_is_generic_argument (param_type))
- continue;
- paramClass = mono_class_from_mono_type (param_type);
- /* 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;
- if ((param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) && paramClass->valuetype)
- return FALSE;
- if ((param_info->flags & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) && !paramClass->valuetype && !mono_class_has_default_constructor (paramClass))
- return FALSE;
- if (!param_info->constraints)
- continue;
- for (constraints = param_info->constraints; *constraints; ++constraints) {
- MonoClass *ctr = *constraints;
- MonoType *inflated;
- inflated = mono_class_inflate_generic_type_checked (&ctr->byval_arg, context, &error);
- if (!mono_error_ok (&error)) {
- mono_error_cleanup (&error);
- return FALSE;
- }
- ctr = mono_class_from_mono_type (inflated);
- mono_metadata_free_type (inflated);
- /*FIXME maybe we need the same this as verifier_class_is_assignable_from*/
- if (!mono_class_is_assignable_from_slow (ctr, paramClass))
- return FALSE;
- }
- }
- return TRUE;
- }
- /*
- * Return true if @candidate is constraint compatible with @target.
- *
- * 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, MonoClass *candidate_param_class, MonoGenericContext *context)
- {
- MonoGenericParamInfo *tinfo = mono_generic_param_info (target);
- MonoGenericParamInfo *cinfo = mono_generic_param_info (candidate);
- MonoClass **candidate_class;
- gboolean class_constraint_satisfied = FALSE;
- gboolean valuetype_constraint_satisfied = FALSE;
- int tmask = tinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK;
- int cmask = cinfo->flags & GENERIC_PARAMETER_ATTRIBUTE_SPECIAL_CONSTRAINTS_MASK;
- if (cinfo->constraints) {
- for (candidate_class = cinfo->constraints; *candidate_class; ++candidate_class) {
- MonoClass *cc;
- MonoType *inflated = verifier_inflate_type (ctx, &(*candidate_class)->byval_arg, ctx->generic_context);
- if (!inflated)
- return FALSE;
- cc = mono_class_from_mono_type (inflated);
- mono_metadata_free_type (inflated);
- if (mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc))
- class_constraint_satisfied = TRUE;
- else if (!mono_type_is_reference (&cc->byval_arg) && !MONO_CLASS_IS_INTERFACE (cc))
- valuetype_constraint_satisfied = TRUE;
- }
- }
- class_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) != 0;
- valuetype_constraint_satisfied |= (cmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) != 0;
- if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_REFERENCE_TYPE_CONSTRAINT) && !class_constraint_satisfied)
- return FALSE;
- if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_VALUE_TYPE_CONSTRAINT) && !valuetype_constraint_satisfied)
- return FALSE;
- if ((tmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) && !((cmask & GENERIC_PARAMETER_ATTRIBUTE_CONSTRUCTOR_CONSTRAINT) ||
- valuetype_constraint_satisfied)) {
- return FALSE;
- }
- if (tinfo->constraints) {
- MonoClass **target_class;
- for (target_class = tinfo->constraints; *target_class; ++target_class) {
- MonoClass *tc;
- MonoType *inflated = verifier_inflate_type (ctx, &(*target_class)->byval_arg, context);
- if (!inflated)
- return FALSE;
- 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);
- if (!inflated)
- return FALSE;
- cc = mono_class_from_mono_type (inflated);
- mono_metadata_free_type (inflated);
- if (verifier_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;
- }
- }
- return TRUE;
- }
- static MonoGenericParam*
- verifier_get_generic_param_from_type (VerifyContext *ctx, MonoType *type)
- {
- MonoGenericContainer *gc;
- MonoMethod *method = ctx->method;
- int num;
- num = mono_type_get_generic_param_num (type);
- if (type->type == MONO_TYPE_VAR) {
- MonoClass *gtd = method->klass;
- if (gtd->generic_class)
- gtd = gtd->generic_class->container_class;
- gc = gtd->generic_container;
- } else { //MVAR
- MonoMethod *gmd = method;
- if (method->is_inflated)
- gmd = ((MonoMethodInflated*)method)->declaring;
- gc = mono_method_get_generic_container (gmd);
- }
- if (!gc)
- return NULL;
- return mono_generic_container_get_param (gc, num);
- }
- /*
- * Verify if @type is valid for the given @ctx verification context.
- * this function checks for VAR and MVAR types that are invalid under the current verifier,
- * This means that it either
- */
- static gboolean
- is_valid_type_in_context (VerifyContext *ctx, MonoType *type)
- {
- return mono_type_is_valid_type_in_context (type, ctx->generic_context);
- }
- static gboolean
- is_valid_generic_instantiation_in_context (VerifyContext *ctx, MonoGenericInst *ginst, gboolean check_gtd)
- {
- int i;
- for (i = 0; i < ginst->type_argc; ++i) {
- MonoType *type = ginst->type_argv [i];
-
- if (!mono_type_is_valid_type_in_context_full (type, ctx->generic_context, TRUE))
- return FALSE;
- }
- return TRUE;
- }
- static gboolean
- generic_arguments_respect_constraints (VerifyContext *ctx, MonoGenericContainer *gc, MonoGenericContext *context, MonoGenericInst *ginst)
- {
- int i;
- for (i = 0; i < ginst->type_argc; ++i) {
- 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;
- if (!is_valid_type_in_context (ctx, type))
- 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, candidate_class, context))
- return FALSE;
- }
- return TRUE;
- }
- static gboolean
- mono_method_repect_method_constraints (VerifyContext *ctx, MonoMethod *method)
- {
- MonoMethodInflated *gmethod = (MonoMethodInflated *)method;
- MonoGenericInst *ginst = gmethod->context.method_inst;
- MonoGenericContainer *gc = mono_method_get_generic_container (gmethod->declaring);
- return !gc || generic_arguments_respect_constraints (ctx, gc, &gmethod->context, ginst);
- }
- static gboolean
- mono_class_repect_method_constraints (VerifyContext *ctx, MonoClass *klass)
- {
- MonoGenericClass *gklass = klass->generic_class;
- MonoGenericInst *ginst = gklass->context.class_inst;
- MonoGenericContainer *gc = gklass->container_class->generic_container;
- return !gc || generic_arguments_respect_constraints (ctx, gc, &gklass->context, ginst);
- }
- static gboolean
- mono_method_is_valid_generic_instantiation (VerifyContext *ctx, MonoMethod *method)
- {
- MonoMethodInflated *gmethod = (MonoMethodInflated *)method;
- MonoGenericInst *ginst = gmethod->context.method_inst;
- MonoGenericContainer *gc = mono_method_get_generic_container (gmethod->declaring);
- if (!gc) /*non-generic inflated method - it's part of a generic type */
- return TRUE;
- if (ctx && !is_valid_generic_instantiation_in_context (ctx, ginst, TRUE))
- return FALSE;
- return is_valid_generic_instantiation (gc, &gmethod->context, ginst);
- }
- static gboolean
- mono_class_is_valid_generic_instantiation (VerifyContext *ctx, MonoClass *klass)
- {
- MonoGenericClass *gklass = klass->generic_class;
- MonoGenericInst *ginst = gklass->context.class_inst;
- MonoGenericContainer *gc = gklass->container_class->generic_container;
- if (ctx && !is_valid_generic_instantiation_in_context (ctx, ginst, TRUE))
- return FALSE;
- return is_valid_generic_instantiation (gc, &gklass->context, ginst);
- }
- static gboolean
- mono_type_is_valid_in_context (VerifyContext *ctx, MonoType *type)
- {
- MonoClass *klass;
- if (type == NULL) {
- ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid null type at 0x%04x", ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
- return FALSE;
- }
- if (!is_valid_type_in_context (ctx, type)) {
- char *str = mono_type_full_name (type);
- ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic type (%s%s) (argument out of range or %s is not generic) at 0x%04x",
- type->type == MONO_TYPE_VAR ? "!" : "!!",
- str,
- type->type == MONO_TYPE_VAR ? "class" : "method",
- ctx->ip_offset),
- MONO_EXCEPTION_BAD_IMAGE);
- g_free (str);
- return FALSE;
- }
- klass = mono_class_from_mono_type (type);
- mono_class_init (klass);
- if (mono_loader_get_last_error () || klass->exception_type != MONO_EXCEPTION_NONE) {
- if (klass->generic_class && !mono_class_is_valid_generic_instantiation (NULL, klass))
- 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;
- }
- if (klass->generic_class && klass->generic_class->container_class->exception_type != MONO_EXCEPTION_NONE) {
- 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);
- return FALSE;
- }
- if (!klass->generic_class)
- return TRUE;
- if (!mono_class_is_valid_generic_instantiation (ctx, klass)) {
- ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic type instantiation of type %s.%s at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD);
- return FALSE;
- }
- if (!mono_class_repect_method_constraints (ctx, klass)) {
- ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic type instantiation of type %s.%s (generic args don't respect target's constraints) at 0x%04x", klass->name_space, klass->name, ctx->ip_offset), MONO_EXCEPTION_TYPE_LOAD);
- return FALSE;
- }
- return TRUE;
- }
- static verify_result_t
- mono_method_is_valid_in_context (VerifyContext *ctx, MonoMethod *method)
- {
- if (!mono_type_is_valid_in_context (ctx, &method->klass->byval_arg))
- return RESULT_INVALID;
- if (!method->is_inflated)
- return RESULT_VALID;
- if (!mono_method_is_valid_generic_instantiation (ctx, method)) {
- ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid generic method instantiation of method %s.%s::%s at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset), MONO_EXCEPTION_UNVERIFIABLE_IL);
- return RESULT_INVALID;
- }
- if (!mono_method_repect_method_constraints (ctx, method)) {
- CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Invalid generic method instantiation of method %s.%s::%s (generic args don't respect target's constraints) at 0x%04x", method->klass->name_space, method->klass->name, method->name, ctx->ip_offset));
- return RESULT_UNVERIFIABLE;
- }
- return RESULT_VALID;
- }
-
- static MonoClassField*
- verifier_load_field (VerifyContext *ctx, int token, MonoClass **out_klass, const char *opcode) {
- MonoClassField *field;
- MonoClass *klass = NULL;
- if (ctx->method->wrapper_type != MONO_WRAPPER_NONE) {
- field = mono_method_get_wrapper_data (ctx->method, (guint32)token);
- klass = field ? field->parent : NULL;
- } else {
- if (!IS_FIELD_DEF_OR_REF (token) || !token_bounds_check (ctx->image, token)) {
- ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid field token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
- return NULL;
- }
- field = mono_field_from_token (ctx->image, token, &klass, ctx->generic_context);
- }
- 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;
- }
- static MonoMethod*
- verifier_load_method (VerifyContext *ctx, int token, const char *opcode) {
- MonoMethod* method;
- if (ctx->method->wrapper_type != MONO_WRAPPER_NONE) {
- method = mono_method_get_wrapper_data (ctx->method, (guint32)token);
- } else {
- if (!IS_METHOD_DEF_OR_REF_OR_SPEC (token) || !token_bounds_check (ctx->image, token)) {
- ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid method token 0x%08x for %s at 0x%04x", token, opcode, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
- return NULL;
- }
- method = mono_get_method_full (ctx->image, token, NULL, ctx->generic_context);
- }
- 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;
- }
-
- if (mono_method_is_valid_in_context (ctx, method) == RESULT_INVALID)
- return NULL;
- return method;
- }
- static MonoType*
- verifier_load_type (VerifyContext *ctx, int token, const char *opcode) {
- MonoType* type;
-
- if (ctx->method->wrapper_type != MONO_WRAPPER_NONE) {
- MonoClass *class = mono_method_get_wrapper_data (ctx->method, (guint32)token);
- type = class ? &class->byval_arg : NULL;
- } else {
- if (!IS_TYPE_DEF_OR_REF_OR_SPEC (token) || !token_bounds_check (ctx->image, token)) {
- ADD_VERIFY_ERROR2 (ctx, g_strdup_printf ("Invalid type token 0x%08x at 0x%04x", token, ctx->ip_offset), MONO_EXCEPTION_BAD_IMAGE);
- return NULL;
- }
- type = mono_type_get_full (ctx->image, token, ctx->generic_context);
- }
- 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;
- }
- if (!mono_type_is_valid_in_context (ctx, type))
- return NULL;
- return type;
- }
- /* stack_slot_get_type:
- *
- * Returns the stack type of @value. This value includes POINTER_MASK.
- *
- * Use this function to checks that account for a managed pointer.
- */
- static gint32
- stack_slot_get_type (ILStackDesc *value)
- {
- return value->stype & RAW_TYPE_MASK;
- }
- /* stack_slot_get_underlying_type:
- *
- * Returns the stack type of @value. This value does not include POINTER_MASK.
- *
- * Use this function is cases where the fact that the value could be a managed pointer is
- * irrelevant. For example, field load doesn't care about this fact of type on stack.
- */
- static gint32
- stack_slot_get_underlying_type (ILStackDesc *value)
- {
- return value->stype & TYPE_MASK;
- }
- /* stack_slot_is_managed_pointer:
- *
- * Returns TRUE is @value is a managed pointer.
- */
- static gboolean
- stack_slot_is_managed_pointer (ILStackDesc *value)
- {
- return (value->stype & POINTER_MASK) == POINTER_MASK;
- }
- /* stack_slot_is_managed_mutability_pointer:
- *
- * Returns TRUE is @value is a managed mutability pointer.
- */
- static G_GNUC_UNUSED gboolean
- stack_slot_is_managed_mutability_pointer (ILStackDesc *value)
- {
- return (value->stype & CMMP_MASK) == CMMP_MASK;
- }
- /* stack_slot_is_null_literal:
- *
- * Returns TRUE is @value is the null literal.
- */
- static gboolean
- stack_slot_is_null_literal (ILStackDesc *value)
- {
- return (value->stype & NULL_LITERAL_MASK) == NULL_LITERAL_MASK;
- }
- /* stack_slot_is_this_pointer:
- *
- * Returns TRUE is @value is the this literal
- */
- static gboolean
- stack_slot_is_this_pointer (ILStackDesc *value)
- {
- return (value->stype & THIS_POINTER_MASK) == THIS_POINTER_MASK;
- }
- /* stack_slot_is_boxed_value:
- *
- * Returns TRUE is @value is a boxed value
- */
- static gboolean
- stack_slot_is_boxed_value (ILStackDesc *value)
- {
- return (value->stype & BOXED_MASK) == BOXED_MASK;
- }
- static const char *
- stack_slot_get_name (ILStackDesc *value)
- {
- return type_names [value->stype & TYPE_MASK];
- }
- #define APPEND_WITH_PREDICATE(PRED,NAME) do {\
- if (PRED (value)) { \
- if (!first) \
- g_string_append (str, ", "); \
- g_string_append (str, NAME); \
- first = FALSE; \
- } } while (0)
- static char*
- stack_slot_stack_type_full_name (ILStackDesc *value)
- {
- GString *str = g_string_new ("");
- char *result;
- gboolean has_pred = FALSE, first = TRUE;
- if ((value->stype & TYPE_MASK) != value->stype) {
- g_string_append(str, "[");
- APPEND_WITH_PREDICATE (stack_slot_is_this_pointer, "this");
- APPEND_WITH_PREDICATE (stack_slot_is_boxed_value, "boxed");
- APPEND_WITH_PREDICATE (stack_slot_is_null_literal, "null");
- APPEND_WITH_PREDICATE (stack_slot_is_managed_mutability_pointer, "cmmp");
- APPEND_WITH_PREDICATE (stack_slot_is_managed_pointer, "mp");
- has_pred = TRUE;
- }
- if (mono_type_is_generic_argument (value->type) && !stack_slot_is_boxed_value (value)) {
- if (!has_pred)
- g_string_append(str, "[");
- if (!first)
- g_string_append (str, ", ");
- g_string_append (str, "unboxed");
- has_pred = TRUE;
- }
- if (has_pred)
- g_string_append(str, "] ");
- g_string_append (str, stack_slot_get_name (value));
- result = str->str;
- g_string_free (str, FALSE);
- return result;
- }
- static char*
- stack_slot_full_name (ILStackDesc *value)
- {
- char *type_name = mono_type_full_name (value->type);
- char *stack_name = stack_slot_stack_type_full_name (value);
- char *res = g_strdup_printf ("%s (%s)", type_name, stack_name);
- g_free (type_name);
- g_free (stack_name);
- return res;
- }
- //////////////////////////////////////////////////////////////////
- void
- mono_free_verify_list (GSList *list)
- {
- MonoVerifyInfoExtended *info;
- GSList *tmp;
- for (tmp = list; tmp; tmp = tmp->next) {
- info = tmp->data;
- g_free (info->info.message);
- g_free (info);
- }
- g_slist_free (list);
- }
- #define ADD_ERROR(list,msg) \
- do { \
- MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \
- vinfo->info.status = MONO_VERIFY_ERROR; \
- vinfo->info.message = (msg); \
- (list) = g_slist_prepend ((list), vinfo); \
- } while (0)
- #define ADD_WARN(list,code,msg) \
- do { \
- MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \
- vinfo->info.status = (code); \
- vinfo->info.message = (msg); \
- (list) = g_slist_prepend ((list), vinfo); \
- } while (0)
- #define ADD_INVALID(list,msg) \
- do { \
- MonoVerifyInfoExtended *vinfo = g_new (MonoVerifyInfoExtended, 1); \
- vinfo->status = MONO_VERIFY_ERROR; \
- vinfo->message = (msg); \
- (list) = g_slist_prepend ((list), vinfo); \
- /*G_BREAKPOINT ();*/ \
- goto invalid_cil; \
- } while (0)
- #define CHECK_STACK_UNDERFLOW(num) \
- do { \
- if (cur_stack < (num)) \
- ADD_INVALID (list, g_strdup_printf ("Stack underflow at 0x%04x (%d items instead of %d)", ip_offset, cur_stack, (num))); \
- } while (0)
- #define CHECK_STACK_OVERFLOW() \
- do { \
- if (cur_stack >= max_stack) \
- ADD_INVALID (list, g_strdup_printf ("Maxstack exceeded at 0x%04x", ip_offset)); \
- } while (0)
- static int
- in_any_block (MonoMethodHeader *header, guint offset)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < header->num_clauses; ++i) {
- clause = &header->clauses [i];
- if (MONO_OFFSET_IN_CLAUSE (clause, offset))
- return 1;
- if (MONO_OFFSET_IN_HANDLER (clause, offset))
- return 1;
- if (MONO_OFFSET_IN_FILTER (clause, offset))
- return 1;
- }
- return 0;
- }
- /*
- * in_any_exception_block:
- *
- * Returns TRUE is @offset is part of any exception clause (filter, handler, catch, finally or fault).
- */
- static gboolean
- in_any_exception_block (MonoMethodHeader *header, guint offset)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < header->num_clauses; ++i) {
- clause = &header->clauses [i];
- if (MONO_OFFSET_IN_HANDLER (clause, offset))
- return TRUE;
- if (MONO_OFFSET_IN_FILTER (clause, offset))
- return TRUE;
- }
- return FALSE;
- }
- /*
- * is_valid_branch_instruction:
- *
- * Verify if it's valid to perform a branch from @offset to @target.
- * This should be used with br and brtrue/false.
- * It returns 0 if valid, 1 for unverifiable and 2 for invalid.
- * The major difference from other similiar functions is that branching into a
- * finally/fault block is invalid instead of just unverifiable.
- */
- static int
- is_valid_branch_instruction (MonoMethodHeader *header, guint offset, guint target)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < header->num_clauses; ++i) {
- clause = &header->clauses [i];
- /*branching into a finally block is invalid*/
- if ((clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY || clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) &&
- !MONO_OFFSET_IN_HANDLER (clause, offset) &&
- MONO_OFFSET_IN_HANDLER (clause, target))
- return 2;
- if (clause->try_offset != target && (MONO_OFFSET_IN_CLAUSE (clause, offset) ^ MONO_OFFSET_IN_CLAUSE (clause, target)))
- return 1;
- if (MONO_OFFSET_IN_HANDLER (clause, offset) ^ MONO_OFFSET_IN_HANDLER (clause, target))
- return 1;
- if (MONO_OFFSET_IN_FILTER (clause, offset) ^ MONO_OFFSET_IN_FILTER (clause, target))
- return 1;
- }
- return 0;
- }
- /*
- * is_valid_cmp_branch_instruction:
- *
- * Verify if it's valid to perform a branch from @offset to @target.
- * This should be used with binary comparison branching instruction, like beq, bge and similars.
- * It returns 0 if valid, 1 for unverifiable and 2 for invalid.
- *
- * The major differences from other similar functions are that most errors lead to invalid
- * code and only branching out of finally, filter or fault clauses is unverifiable.
- */
- static int
- is_valid_cmp_branch_instruction (MonoMethodHeader *header, guint offset, guint target)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < header->num_clauses; ++i) {
- clause = &header->clauses [i];
- /*branching out of a handler or finally*/
- if (clause->flags != MONO_EXCEPTION_CLAUSE_NONE &&
- MONO_OFFSET_IN_HANDLER (clause, offset) &&
- !MONO_OFFSET_IN_HANDLER (clause, target))
- return 1;
- if (clause->try_offset != target && (MONO_OFFSET_IN_CLAUSE (clause, offset) ^ MONO_OFFSET_IN_CLAUSE (clause, target)))
- return 2;
- if (MONO_OFFSET_IN_HANDLER (clause, offset) ^ MONO_OFFSET_IN_HANDLER (clause, target))
- return 2;
- if (MONO_OFFSET_IN_FILTER (clause, offset) ^ MONO_OFFSET_IN_FILTER (clause, target))
- return 2;
- }
- return 0;
- }
- /*
- * A leave can't escape a finally block
- */
- static int
- is_correct_leave (MonoMethodHeader *header, guint offset, guint target)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < header->num_clauses; ++i) {
- clause = &header->clauses [i];
- if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY && MONO_OFFSET_IN_HANDLER (clause, offset) && !MONO_OFFSET_IN_HANDLER (clause, target))
- return 0;
- if (MONO_OFFSET_IN_FILTER (clause, offset))
- return 0;
- }
- return 1;
- }
- /*
- * A rethrow can't happen outside of a catch handler.
- */
- static int
- is_correct_rethrow (MonoMethodHeader *header, guint offset)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < header->num_clauses; ++i) {
- clause = &header->clauses [i];
- if (MONO_OFFSET_IN_HANDLER (clause, offset))
- return 1;
- if (MONO_OFFSET_IN_FILTER (clause, offset))
- return 1;
- }
- return 0;
- }
- /*
- * An endfinally can't happen outside of a finally/fault handler.
- */
- static int
- is_correct_endfinally (MonoMethodHeader *header, guint offset)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < header->num_clauses; ++i) {
- clause = &header->clauses [i];
- if (MONO_OFFSET_IN_HANDLER (clause, offset) && (clause->flags == MONO_EXCEPTION_CLAUSE_FAULT || clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY))
- return 1;
- }
- return 0;
- }
- /*
- * An endfilter can only happens inside a filter clause.
- * In non-strict mode filter is allowed inside the handler clause too
- */
- static MonoExceptionClause *
- is_correct_endfilter (VerifyContext *ctx, guint offset)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < ctx->header->num_clauses; ++i) {
- clause = &ctx->header->clauses [i];
- if (clause->flags != MONO_EXCEPTION_CLAUSE_FILTER)
- continue;
- if (MONO_OFFSET_IN_FILTER (clause, offset))
- return clause;
- if (!IS_STRICT_MODE (ctx) && MONO_OFFSET_IN_HANDLER (clause, offset))
- return clause;
- }
- return NULL;
- }
- /*
- * Non-strict endfilter can happens inside a try block or any handler block
- */
- static int
- is_unverifiable_endfilter (VerifyContext *ctx, guint offset)
- {
- int i;
- MonoExceptionClause *clause;
- for (i = 0; i < ctx->header->num_clauses; ++i) {
- clause = &ctx->header->clauses [i];
- if (MONO_OFFSET_IN_CLAUSE (clause, offset))
- return 1;
- }
- return 0;
- }
- static gboolean
- is_valid_bool_arg (ILStackDesc *arg)
- {
- if (stack_slot_is_managed_pointer (arg) || stack_slot_is_boxed_value (arg) || stack_slot_is_null_literal (arg))
- return TRUE;
- switch (stack_slot_get_underlying_type (arg)) {
- case TYPE_I4:
- case TYPE_I8:
- case TYPE_NATIVE_INT:
- case TYPE_PTR:
- return TRUE;
- case TYPE_COMPLEX:
- g_assert (arg->type);
- switch (arg->type->type) {
- case MONO_TYPE_CLASS:
- case MONO_TYPE_STRING:
- case MONO_TYPE_OBJECT:
- case MONO_TYPE_SZARRAY:
- case MONO_TYPE_ARRAY:
- case MONO_TYPE_FNPTR:
- case MONO_TYPE_PTR:
- return TRUE;
- case MONO_TYPE_GENERICINST:
- /*We need to check if the container class
- * of the generic type is a valuetype, iow:
- * is it a "class Foo<T>" or a "struct Foo<T>"?
- */
- return !arg->type->data.generic_class->container_class->valuetype;
- }
- default:
- return FALSE;
- }
- }
- /*Type manipulation helper*/
- /*Returns the byref version of the supplied MonoType*/
- static MonoType*
- mono_type_get_type_byref (MonoType *type)
- {
- if (type->byref)
- return type;
- return &mono_class_from_mono_type (type)->this_arg;
- }
- /*Returns the byval version of the supplied MonoType*/
- static MonoType*
- mono_type_get_type_byval (MonoType *type)
- {
- if (!type->byref)
- return type;
- return &mono_class_from_mono_type (type)->byval_arg;
- }
- static MonoType*
- mono_type_from_stack_slot (ILStackDesc *slot)
- {
- if (stack_slot_is_managed_pointer (slot))
- return mono_type_get_type_byref (slot->type);
- return slot->type;
- }
- /*Stack manipulation code*/
- static void
- ensure_stack_size (ILCodeDesc *stack, int required)
- {
- int new_size = 8;
- ILStackDesc *tmp;
- if (required < stack->max_size)
- return;
- /* We don't have to worry about the exponential growth since stack_copy prune unused space */
- new_size = MAX (8, MAX (required, stack->max_size * 2));
- g_assert (new_size >= stack->size);
- g_assert (new_size >= required);
- tmp = g_new0 (ILStackDesc, new_size);
- MEM_ALLOC (sizeof (ILStackDesc) * new_size);
- if (stack->stack) {
- if (stack->size)
- memcpy (tmp, stack->stack, stack->size * sizeof (ILStackDesc));
- g_free (stack->stack);
- MEM_FREE (sizeof (ILStackDesc) * stack->max_size);
- }
- stack->stack = tmp;
- stack->max_size = new_size;
- }
- static void
- stack_init (VerifyContext *ctx, ILCodeDesc *state)
- {
- if (state->flags & IL_CODE_FLAG_STACK_INITED)
- return;
- state->size = state->max_size = 0;
- state->flags |= IL_CODE_FLAG_STACK_INITED;
- }
- static void
- stack_copy (ILCodeDesc *to, ILCodeDesc *from)
- {
- ensure_stack_size (to, from->size);
- to->size = from->size;
- /*stack copy happens at merge points, which have small stacks*/
- if (from->size)
- memcpy (to->stack, from->stack, sizeof (ILStackDesc) * from->size);
- }
- static void
- copy_stack_value (ILStackDesc *to, ILStackDesc *from)
- {
- to->stype = from->stype;
- to->type = from->type;
- to->method = from->method;
- }
- static int
- check_underflow (VerifyContext *ctx, int size)
- {
- if (ctx->eval.size < size) {
- ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Stack underflow, required %d, but have %d at 0x%04x", size, ctx->eval.size, ctx->ip_offset));
- return 0;
- }
- return 1;
- }
- static int
- check_overflow (VerifyContext *ctx)
- {
- if (ctx->eval.size >= ctx->max_stack) {
- ADD_VERIFY_ERROR (ctx, g_strdup_printf ("Method doesn't have stack-depth %d at 0x%04x", ctx->eval.size + 1, ctx->ip_offset));
- return 0;
- }
- return 1;
- }
- /*This reject out PTR, FNPTR and TYPEDBYREF*/
- static gboolean
- check_unmanaged_pointer (VerifyContext *ctx, ILStackDesc *value)
- {
- if (stack_slot_get_type (value) == TYPE_PTR) {
- CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Unmanaged pointer is not a verifiable type at 0x%04x", ctx->ip_offset));
- return 0;
- }
- return 1;
- }
- /*TODO verify if MONO_TYPE_TYPEDBYREF is not allowed here as well.*/
- static gboolean
- check_unverifiable_type (VerifyContext *ctx, MonoType *type)
- {
- if (type->type == MONO_TYPE_PTR || type->type == MONO_TYPE_FNPTR) {
- CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Unmanaged pointer is not a verifiable type at 0x%04x", ctx->ip_offset));
- return 0;
- }
- return 1;
- }
- static ILStackDesc *
- stack_push (VerifyContext *ctx)
- {
- g_assert (ctx->eval.size < ctx->max_stack);
- g_assert (ctx->eval.size <= ctx->eval.max_size);
- ensure_stack_size (&ctx->eval, ctx->eval.size + 1);
- return & ctx->eval.stack [ctx->eval.size++];
- }
- static ILStackDesc *
- stack_push_val (VerifyContext *ctx, int stype, MonoType *type)
- {
- ILStackDesc *top = stack_push (ctx);
- top->stype = stype;
- top->type = type;
- return top;
- }
- static ILStackDesc *
- stack_pop (VerifyContext *ctx)
- {
- ILStackDesc *ret;
- g_assert (ctx->eval.size > 0);
- ret = ctx->eval.stack + --ctx->eval.size;
- if ((ret->stype & UNINIT_THIS_MASK) == UNINIT_THIS_MASK)
- CODE_NOT_VERIFIABLE (ctx, g_strdup_printf ("Found use of uninitialized 'this ptr' ref at 0x%04x", ctx->ip_offset));
- return ret;
- }
- /* This function allows to safely pop an unititialized this ptr from
- * the eval stack without marking the method as unverifiable.
- */
- static ILStackDesc *
- stack_pop_safe (VerifyContext *ctx)
- {
- g_assert (ctx->eval.size > 0);
- return ctx->eval.stack + --ctx->eval.size;
- }
- /*Positive number distance from stack top. [0] is stack top, [1] is the one below*/
- static ILStackDesc*
- stack_peek (VerifyContext *ctx, int distance)
- {
- g_assert (ctx->eval.size - distance > 0);
- return ctx->eval.stack + (ctx->eval.size - 1 - distance);
- }
- static ILStackDesc *
- stack_push_stack_val (VerifyContext *ctx, ILStackDesc *value)
- {
- ILStackDesc *top = stack_push (ctx);
- copy_stack_value (top, value);
- return top;
- }
- /* Returns the MonoType associated with the token, or NULL if it is invalid.
- *
- * A boxable type can be either a refereā¦
Large files files are truncated, but you can click here to view the full file