[sgen] Never directly pin objects on the M&S major heap.
[mono.git] / mono / metadata / security-core-clr.c
index 50e0d84eafa1d029ce67705ff9556aa074c72788..0e165611a5363fea05698cf692d831e6b2d7e2b9 100644 (file)
@@ -5,7 +5,7 @@
  *     Mark Probst <mark.probst@gmail.com>
  *     Sebastien Pouliot  <sebastien@ximian.com>
  *
- * Copyright 2007-2009 Novell, Inc (http://www.novell.com)
+ * Copyright 2007-2010 Novell, Inc (http://www.novell.com)
  */
 
 #include <mono/metadata/class-internals.h>
@@ -15,6 +15,8 @@
 #include <mono/metadata/verify-internals.h>
 #include <mono/metadata/object.h>
 #include <mono/metadata/exception.h>
+#include <mono/metadata/debug-helpers.h>
+#include <mono/utils/mono-logger-internal.h>
 
 #include "security-core-clr.h"
 
@@ -46,6 +48,91 @@ security_safe_critical_attribute (void)
        return class;
 }
 
+/* sometime we get a NULL (not found) caller (e.g. get_reflection_caller) */
+static char*
+get_method_full_name (MonoMethod * method)
+{
+       return method ? mono_method_full_name (method, TRUE) : g_strdup ("'no caller found'");
+}
+
+/*
+ * set_type_load_exception_type
+ *
+ *     Set MONO_EXCEPTION_TYPE_LOAD on the specified 'class' and provide
+ *     a descriptive message for the exception. This message is also, 
+ *     optionally, being logged (export MONO_LOG_MASK="security") for
+ *     debugging purposes.
+ */
+static void
+set_type_load_exception_type (const char *format, MonoClass *class)
+{
+       char *type_name = mono_type_get_full_name (class);
+       char *parent_name = mono_type_get_full_name (class->parent);
+       char *message = g_strdup_printf (format, type_name, parent_name);
+
+       g_free (parent_name);
+       g_free (type_name);
+       
+       mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, message);
+       mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, message);
+       // note: do not free string given to mono_class_set_failure
+}
+
+/*
+ * set_type_load_exception_methods
+ *
+ *     Set MONO_EXCEPTION_TYPE_LOAD on the 'override' class and provide
+ *     a descriptive message for the exception. This message is also, 
+ *     optionally, being logged (export MONO_LOG_MASK="security") for
+ *     debugging purposes.
+ */
+static void
+set_type_load_exception_methods (const char *format, MonoMethod *override, MonoMethod *base)
+{
+       char *method_name = get_method_full_name (override);
+       char *base_name = get_method_full_name (base);
+       char *message = g_strdup_printf (format, method_name, base_name);
+
+       g_free (base_name);
+       g_free (method_name);
+
+       mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, message);
+       mono_class_set_failure (override->klass, MONO_EXCEPTION_TYPE_LOAD, message);
+       // note: do not free string given to mono_class_set_failure
+}
+
+/* MonoClass is not fully initialized (inited is not yet == 1) when we 
+ * check the inheritance rules so we need to look for the default ctor
+ * ourselve to avoid recursion (and aborting)
+ */
+static MonoMethod*
+get_default_ctor (MonoClass *klass)
+{
+       int i;
+
+       mono_class_setup_methods (klass);
+       if (!klass->methods)
+               return NULL;
+
+       for (i = 0; i < klass->method.count; ++i) {
+               MonoMethodSignature *sig;
+               MonoMethod *method = klass->methods [i];
+
+               if (!method)
+                       continue;
+
+               if ((method->flags & METHOD_ATTRIBUTE_SPECIAL_NAME) == 0)
+                       continue;
+               if ((method->name[0] != '.') || strcmp (".ctor", method->name))
+                       continue;
+               sig = mono_method_signature (method);
+               if (sig && (sig->param_count == 0))
+                       return method;
+       }
+
+       return NULL;
+}
+
 /*
  * mono_security_core_clr_check_inheritance:
  *
@@ -59,6 +146,13 @@ security_safe_critical_attribute (void)
  *     Critical        Critical
  *
  *     Reference: http://msdn.microsoft.com/en-us/magazine/cc765416.aspx#id0190030
+ *
+ *     Furthermore a class MUST have a default constructor if its base 
+ *     class has a non-transparent, public or protected, default constructor. 
+ *     The same inheritance rule applies to both default constructors.
+ *
+ *     Reference: message from a SecurityException in SL4RC
+ *     Reference: fxcop CA2132 rule
  */
 void
 mono_security_core_clr_check_inheritance (MonoClass *class)
@@ -72,8 +166,22 @@ mono_security_core_clr_check_inheritance (MonoClass *class)
        class_level = mono_security_core_clr_class_level (class);
        parent_level = mono_security_core_clr_class_level (parent);
 
-       if (class_level < parent_level)
-               mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL);
+       if (class_level < parent_level) {
+               set_type_load_exception_type (
+                       "Inheritance failure for type %s. Parent class %s is more restricted.",
+                       class);
+       } else {
+               MonoMethod *parent_ctor = get_default_ctor (parent);
+               if (parent_ctor && ((parent_ctor->flags & METHOD_ATTRIBUTE_PUBLIC) != 0)) {
+                       class_level = mono_security_core_clr_method_level (get_default_ctor (class), FALSE);
+                       parent_level = mono_security_core_clr_method_level (parent_ctor, FALSE);
+                       if (class_level < parent_level) {
+                               set_type_load_exception_type (
+                                       "Inheritance failure for type %s. Default constructor security mismatch with %s.",
+                                       class);
+                       }
+               }
+       }
 }
 
 /*
@@ -97,12 +205,18 @@ mono_security_core_clr_check_override (MonoClass *class, MonoMethod *override, M
        MonoSecurityCoreCLRLevel override_level = mono_security_core_clr_method_level (override, FALSE);
        /* if the base method is decorated with [SecurityCritical] then the overrided method MUST be too */
        if (base_level == MONO_SECURITY_CORE_CLR_CRITICAL) {
-               if (override_level != MONO_SECURITY_CORE_CLR_CRITICAL)
-                       mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL);
+               if (override_level != MONO_SECURITY_CORE_CLR_CRITICAL) {
+                       set_type_load_exception_methods (
+                               "Override failure for %s over %s. Override MUST be [SecurityCritical].",
+                               override, base);
+               }
        } else {
                /* base is [SecuritySafeCritical] or [SecurityTransparent], override MUST NOT be [SecurityCritical] */
-               if (override_level == MONO_SECURITY_CORE_CLR_CRITICAL)
-                       mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, NULL);
+               if (override_level == MONO_SECURITY_CORE_CLR_CRITICAL) {
+                       set_type_load_exception_methods (
+                               "Override failure for %s over %s. Override must NOT be [SecurityCritical].", 
+                               override, base);
+               }
        }
 }
 
@@ -188,7 +302,7 @@ get_caller_no_reflection_related (MonoMethod *m, gint32 no, gint32 ilo, gboolean
  * 
  *     Walk to the first managed method outside:
  *     - System.Reflection* namespaces
- *     - System.[MulticastDelegate]Delegate or Activator type
+ *     - System.[Multicast]Delegate or Activator type
  *     - platform code
  *     and return a pointer to its MonoMethod.
  *
@@ -199,11 +313,141 @@ get_reflection_caller (void)
 {
        MonoMethod *m = NULL;
        mono_stack_walk_no_il (get_caller_no_reflection_related, &m);
-       if (!m)
-               g_warning ("could not find a caller outside reflection");
+       if (G_UNLIKELY (!m)) {
+               mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, "No caller outside reflection was found");
+       }
        return m;
 }
 
+typedef struct {
+       int depth;
+       MonoMethod *caller;
+} ElevatedTrustCookie;
+
+/*
+ * get_caller_of_elevated_trust_code
+ *
+ *     Stack walk to find who is calling code requiring Elevated Trust.
+ *     If a critical method is found then the caller is platform code
+ *     and has elevated trust, otherwise (transparent) a check needs to
+ *     be done (on the managed side) to determine if the application is
+ *     running with elevated permissions.
+ */
+static gboolean
+get_caller_of_elevated_trust_code (MonoMethod *m, gint32 no, gint32 ilo, gboolean managed, gpointer data)
+{
+       ElevatedTrustCookie *cookie = data;
+
+       /* skip unmanaged frames and wrappers */
+       if (!managed || (m->wrapper_type != MONO_WRAPPER_NONE))
+               return FALSE;
+
+       /* end stack walk if we find ourselves outside platform code (we won't find critical code anymore) */
+       if (!mono_security_core_clr_is_platform_image (m->klass->image)) {
+               cookie->caller = m;
+               return TRUE;
+       }
+
+       switch (cookie->depth) {
+       /* while depth == 0 look for SecurityManager::[Check|Ensure]ElevatedPermissions */
+       case 0:
+               if (strcmp (m->klass->name_space, "System.Security"))
+                       return FALSE;
+               if (strcmp (m->klass->name, "SecurityManager"))
+                       return FALSE;
+               if ((strcmp (m->name, "EnsureElevatedPermissions")) && strcmp (m->name, "CheckElevatedPermissions"))
+                       return FALSE;
+               cookie->depth = 1;
+               break;
+       /* while depth == 1 look for the caller to SecurityManager::[Check|Ensure]ElevatedPermissions */
+       case 1:
+               /* this frame is [SecuritySafeCritical] because it calls [SecurityCritical] [Check|Ensure]ElevatedPermissions */
+               /* the next frame will contain the caller(s) we want to check */
+               cookie->depth = 2;
+               break;
+       /* while depth >= 2 look for [safe]critical caller, end stack walk if we find it  */
+       default:
+               cookie->depth++;
+               /* if the caller is transparent then we continue the stack walk */
+               if (mono_security_core_clr_method_level (m, TRUE) == MONO_SECURITY_CORE_CLR_TRANSPARENT)
+                       break;
+
+               /* Security[Safe]Critical code is always allowed to call elevated-trust code */
+               cookie->caller = m;
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+/*
+ * mono_security_core_clr_require_elevated_permissions:
+ *
+ *     Return TRUE if the caller of the current method (the code who 
+ *     called SecurityManager.get_RequiresElevatedPermissions) needs
+ *     elevated trust to perform an action.
+ *
+ *     A stack walk is done to find the callers. If one of the callers
+ *     is either [SecurityCritical] or [SecuritySafeCritical] then the
+ *     action is needed for platform code (i.e. no restriction). 
+ *     Otherwise (transparent) the requested action needs elevated trust
+ */
+gboolean
+mono_security_core_clr_require_elevated_permissions (void)
+{
+       ElevatedTrustCookie cookie;
+       cookie.depth = 0;
+       cookie.caller = NULL;
+       mono_stack_walk_no_il (get_caller_of_elevated_trust_code, &cookie);
+
+       /* return TRUE if the stack walk did not reach far enough or did not find callers */
+       if (!cookie.caller || cookie.depth < 3)
+               return TRUE;
+
+       /* return TRUE if the caller is transparent, i.e. if elevated trust is required to continue executing the method */
+       return (mono_security_core_clr_method_level (cookie.caller, TRUE) == MONO_SECURITY_CORE_CLR_TRANSPARENT);
+}
+
+
+static MonoSecurityCoreCLROptions security_core_clr_options = MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT;
+
+/**
+ * mono_security_core_clr_set_options:
+ * @options: the new options for the coreclr system to use
+ *
+ * By default, the CoreCLRs security model forbids execution trough reflection of methods not visible from the calling code.
+ * Even if the method being called is not in a platform assembly. For non moonlight CoreCLR users this restriction does not
+ * make a lot of sense, since the author could have just changed the non platform assembly to allow the method to be called.
+ * This function allows specific relaxations from the default behaviour to be set.
+ *
+ * Use MONO_SECURITY_CORE_CLR_OPTIONS_DEFAULT for the default coreclr coreclr behaviour as used in Moonlight.
+ *
+ * Use MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION to allow transparent code to execute methods and access 
+ * fields that are not in platformcode, even if those methods and fields are private or otherwise not visible to the calling code.
+ *
+ * Use MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE to allow delegates to be created that point at methods that are not in
+ * platformcode even if those methods and fields are private or otherwise not visible to the calling code.
+ *
+ */
+
+void 
+mono_security_core_clr_set_options (MonoSecurityCoreCLROptions options) {
+       security_core_clr_options = options;
+}
+
+/**
+ * mono_security_core_clr_get_options:
+ *
+ * Retrieves the current options used by the coreclr system.
+ */
+
+MonoSecurityCoreCLROptions
+mono_security_core_clr_get_options ()
+{
+       return security_core_clr_options;
+}
+
+
 /*
  * check_field_access:
  *
@@ -214,7 +458,17 @@ check_field_access (MonoMethod *caller, MonoClassField *field)
 {
        /* if get_reflection_caller returns NULL then we assume the caller has NO privilege */
        if (caller) {
-               MonoClass *klass = (mono_field_get_flags (field) & FIELD_ATTRIBUTE_STATIC) ? NULL : mono_field_get_parent (field);
+               MonoError error;
+               MonoClass *klass;
+
+               /* this check can occur before the field's type is resolved (and that can fail) */
+               mono_field_get_type_checked (field, &error);
+               if (!mono_error_ok (&error)) {
+                       mono_error_cleanup (&error);
+                       return FALSE;
+               }
+
+               klass = (mono_field_get_flags (field) & FIELD_ATTRIBUTE_STATIC) ? NULL : mono_field_get_parent (field);
                return mono_method_can_access_field_full (caller, field, klass);
        }
        return FALSE;
@@ -236,6 +490,81 @@ check_method_access (MonoMethod *caller, MonoMethod *callee)
        return FALSE;
 }
 
+/*
+ * get_argument_exception
+ *
+ *     Helper function to create an MonoException (ArgumentException in
+ *     managed-land) and provide a descriptive message for it. This 
+ *     message is also, optionally, being logged (export 
+ *     MONO_LOG_MASK="security") for debugging purposes.
+ */
+static MonoException*
+get_argument_exception (const char *format, MonoMethod *caller, MonoMethod *callee)
+{
+       MonoException *ex;
+       char *caller_name = get_method_full_name (caller);
+       char *callee_name = get_method_full_name (callee);
+       char *message = g_strdup_printf (format, caller_name, callee_name);
+       g_free (callee_name);
+       g_free (caller_name);
+
+       mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, message);
+       ex = mono_get_exception_argument ("method", message);
+       g_free (message);
+
+       return ex;
+}
+
+/*
+ * get_field_access_exception
+ *
+ *     Helper function to create an MonoException (FieldAccessException
+ *     in managed-land) and provide a descriptive message for it. This
+ *     message is also, optionally, being logged (export 
+ *     MONO_LOG_MASK="security") for debugging purposes.
+ */
+static MonoException*
+get_field_access_exception (const char *format, MonoMethod *caller, MonoClassField *field)
+{
+       MonoException *ex;
+       char *caller_name = get_method_full_name (caller);
+       char *field_name = mono_field_full_name (field);
+       char *message = g_strdup_printf (format, caller_name, field_name);
+       g_free (field_name);
+       g_free (caller_name);
+
+       mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, message);
+       ex = mono_get_exception_field_access_msg (message);
+       g_free (message);
+
+       return ex;
+}
+
+/*
+ * get_method_access_exception
+ *
+ *     Helper function to create an MonoException (MethodAccessException
+ *     in managed-land) and provide a descriptive message for it. This
+ *     message is also, optionally, being logged (export 
+ *     MONO_LOG_MASK="security") for debugging purposes.
+ */
+static MonoException*
+get_method_access_exception (const char *format, MonoMethod *caller, MonoMethod *callee)
+{
+       MonoException *ex;
+       char *caller_name = get_method_full_name (caller);
+       char *callee_name = get_method_full_name (callee);
+       char *message = g_strdup_printf (format, caller_name, callee_name);
+       g_free (callee_name);
+       g_free (caller_name);
+
+       mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_SECURITY, message);
+       ex = mono_get_exception_method_access_msg (message);
+       g_free (message);
+
+       return ex;
+}
+
 /*
  * mono_security_core_clr_ensure_reflection_access_field:
  *
@@ -253,13 +582,24 @@ mono_security_core_clr_ensure_reflection_access_field (MonoClassField *field)
        if (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT)
                return;
 
+       if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION) {
+               if (!mono_security_core_clr_is_platform_image (mono_field_get_parent(field)->image))
+                       return;
+       }
+
        /* Transparent code cannot [get|set]value on Critical fields */
-       if (mono_security_core_clr_class_level (mono_field_get_parent (field)) == MONO_SECURITY_CORE_CLR_CRITICAL)
-               mono_raise_exception (mono_get_exception_field_access ());
+       if (mono_security_core_clr_class_level (mono_field_get_parent (field)) == MONO_SECURITY_CORE_CLR_CRITICAL) {
+               mono_raise_exception (get_field_access_exception (
+                       "Transparent method %s cannot get or set Critical field %s.", 
+                       caller, field));
+       }
 
        /* also it cannot access a fields that is not visible from it's (caller) point of view */
-       if (!check_field_access (caller, field))
-               mono_raise_exception (mono_get_exception_field_access ());
+       if (!check_field_access (caller, field)) {
+               mono_raise_exception (get_field_access_exception (
+                       "Transparent method %s cannot get or set private/internal field %s.", 
+                       caller, field));
+       }
 }
 
 /*
@@ -279,13 +619,24 @@ mono_security_core_clr_ensure_reflection_access_method (MonoMethod *method)
        if (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT)
                return;
 
+       if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION) {
+               if (!mono_security_core_clr_is_platform_image (method->klass->image))
+                       return;
+       }
+
        /* Transparent code cannot invoke, even using reflection, Critical code */
-       if (mono_security_core_clr_method_level (method, TRUE) == MONO_SECURITY_CORE_CLR_CRITICAL)
-               mono_raise_exception (mono_get_exception_method_access ());
+       if (mono_security_core_clr_method_level (method, TRUE) == MONO_SECURITY_CORE_CLR_CRITICAL) {
+               mono_raise_exception (get_method_access_exception (
+                       "Transparent method %s cannot invoke Critical method %s.", 
+                       caller, method));
+       }
 
        /* also it cannot invoke a method that is not visible from it's (caller) point of view */
-       if (!check_method_access (caller, method))
-               mono_raise_exception (mono_get_exception_method_access ());
+       if (!check_method_access (caller, method)) {
+               mono_raise_exception (get_method_access_exception (
+                       "Transparent method %s cannot invoke private/internal method %s.", 
+                       caller, method));
+       }
 }
 
 /*
@@ -351,12 +702,22 @@ mono_security_core_clr_ensure_delegate_creation (MonoMethod *method, gboolean th
                if (!throwOnBindFailure)
                        return FALSE;
 
-               mono_raise_exception (mono_get_exception_argument ("method", "Transparent code cannot call Critical code"));
+               mono_raise_exception (get_argument_exception (
+                       "Transparent method %s cannot create a delegate on Critical method %s.", 
+                       caller, method));
        }
-       
+
+       if (mono_security_core_clr_get_options () & MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE) {
+               if (!mono_security_core_clr_is_platform_image (method->klass->image))
+                       return TRUE;
+       }
+
        /* also it cannot create the delegate on a method that is not visible from it's (caller) point of view */
-       if (!check_method_access (caller, method))
-               mono_raise_exception (mono_get_exception_method_access ());
+       if (!check_method_access (caller, method)) {
+               mono_raise_exception (get_method_access_exception (
+                       "Transparent method %s cannot create a delegate on private/internal method %s.", 
+                       caller, method));
+       }
 
        return TRUE;
 }
@@ -380,8 +741,11 @@ mono_security_core_clr_ensure_dynamic_method_resolved_object (gpointer ref, Mono
                if (mono_security_core_clr_is_platform_image (klass->image)) {
                        MonoMethod *caller = get_reflection_caller ();
                        /* XXX Critical code probably can do this / need some test cases (safer off otherwise) XXX */
-                       if (!check_field_access (caller, field))
-                               return mono_get_exception_field_access ();
+                       if (!check_field_access (caller, field)) {
+                               return get_field_access_exception (
+                                       "Dynamic method %s cannot create access private/internal field %s.", 
+                                       caller, field);
+                       }
                }
        } else if (handle_class == mono_defaults.methodhandle_class) {
                MonoMethod *method = (MonoMethod*) ref;
@@ -389,8 +753,11 @@ mono_security_core_clr_ensure_dynamic_method_resolved_object (gpointer ref, Mono
                if (mono_security_core_clr_is_platform_image (method->klass->image)) {
                        MonoMethod *caller = get_reflection_caller ();
                        /* XXX Critical code probably can do this / need some test cases (safer off otherwise) XXX */
-                       if (!check_method_access (caller, method))
-                               return mono_get_exception_method_access ();
+                       if (!check_method_access (caller, method)) {
+                               return get_method_access_exception (
+                                       "Dynamic method %s cannot create access private/internal method %s.", 
+                                       caller, method);
+                       }
                }
        }
        return NULL;
@@ -423,6 +790,54 @@ mono_security_core_clr_can_access_internals (MonoImage *accessing, MonoImage* ac
        return (strcmp (accessed->assembly->basedir, accessing->assembly->basedir) == 0);
 }
 
+/*
+ * mono_security_core_clr_is_field_access_allowed
+ *
+ *     Return a MonoException (FieldccessException in managed-land) if
+ *     the access from "caller" to "field" is not valid under CoreCLR -
+ *     i.e. a [SecurityTransparent] method calling a [SecurityCritical]
+ *     field.
+ */
+MonoException*
+mono_security_core_clr_is_field_access_allowed (MonoMethod *caller, MonoClassField *field)
+{
+       /* there's no restriction to access Transparent or SafeCritical fields, so we only check calls to Critical methods */
+       if (mono_security_core_clr_class_level (mono_field_get_parent (field)) != MONO_SECURITY_CORE_CLR_CRITICAL)
+               return NULL;
+
+       /* caller is Critical! only SafeCritical and Critical callers can access the field, so we throw if caller is Transparent */
+       if (!caller || (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT))
+               return NULL;
+
+       return get_field_access_exception (
+               "Transparent method %s cannot call use Critical field %s.", 
+               caller, field);
+}
+
+/*
+ * mono_security_core_clr_is_call_allowed
+ *
+ *     Return a MonoException (MethodAccessException in managed-land) if
+ *     the call from "caller" to "callee" is not valid under CoreCLR -
+ *     i.e. a [SecurityTransparent] method calling a [SecurityCritical]
+ *     method.
+ */
+MonoException*
+mono_security_core_clr_is_call_allowed (MonoMethod *caller, MonoMethod *callee)
+{
+       /* there's no restriction to call Transparent or SafeCritical code, so we only check calls to Critical methods */
+       if (mono_security_core_clr_method_level (callee, TRUE) != MONO_SECURITY_CORE_CLR_CRITICAL)
+               return NULL;
+
+       /* callee is Critical! only SafeCritical and Critical callers can call it, so we throw if the caller is Transparent */
+       if (!caller || (mono_security_core_clr_method_level (caller, TRUE) != MONO_SECURITY_CORE_CLR_TRANSPARENT))
+               return NULL;
+
+       return get_method_access_exception (
+               "Transparent method %s cannot call Critical method %s.", 
+               caller, callee);
+}
+
 /*
  * mono_security_core_clr_level_from_cinfo:
  *