[runtime] Check for ReferenceAssemblyAttribute when loading for execution.
authorAleksey Kliger <aleksey@xamarin.com>
Tue, 18 Oct 2016 18:45:42 +0000 (14:45 -0400)
committerAleksey Kliger <aleksey@xamarin.com>
Tue, 18 Oct 2016 18:48:25 +0000 (14:48 -0400)
An assembly with a System.Runtime.CompilerServices.ReferenceAssemblyAttribute
may be loaded in a reflection-only context, but not for execution.

When loading assembly refs, or when loading assemblies by name (as
opposed to file path), a reference assembly will be skipped over and
other assemblies from the GAC and the load path will be tried.

Fixes https://bugzilla.xamarin.com/show_bug.cgi?id=42584

mono/metadata/appdomain.c
mono/metadata/assembly.c
mono/metadata/class.c
mono/metadata/custom-attrs.c
mono/metadata/domain-internals.h
mono/metadata/domain.c

index b4c20c8e22bd57c6fad22d3e588661b55df19a35..e134b8f6120c0e7efc79b24ff23e9203a40bb750 100644 (file)
@@ -1958,37 +1958,37 @@ ves_icall_System_Reflection_Assembly_LoadFrom (MonoString *fname, MonoBoolean re
        MonoDomain *domain = mono_domain_get ();
        char *name, *filename;
        MonoImageOpenStatus status = MONO_IMAGE_OK;
-       MonoAssembly *ass;
+       MonoAssembly *ass = NULL;
+
+       name = NULL;
+       result = NULL;
+
+       mono_error_init (&error);
 
        if (fname == NULL) {
-               MonoException *exc = mono_get_exception_argument_null ("assemblyFile");
-               mono_set_pending_exception (exc);
-               return NULL;
+               mono_error_set_argument_null (&error, "assemblyFile", "");
+               goto leave;
        }
                
        name = filename = mono_string_to_utf8_checked (fname, &error);
-       if (mono_error_set_pending_exception (&error))
-               return NULL;
+       if (!is_ok (&error))
+               goto leave;
        
        ass = mono_assembly_open_full (filename, &status, refOnly);
        
        if (!ass) {
-               MonoException *exc;
-
                if (status == MONO_IMAGE_IMAGE_INVALID)
-                       exc = mono_get_exception_bad_image_format2 (NULL, fname);
+                       mono_error_set_bad_image_name (&error, name, "");
                else
-                       exc = mono_get_exception_file_not_found2 (NULL, fname);
-               g_free (name);
-               mono_set_pending_exception (exc);
-               return NULL;
+                       mono_error_set_exception_instance (&error, mono_get_exception_file_not_found2 (NULL, fname));
+               goto leave;
        }
 
-       g_free (name);
-
        result = mono_assembly_get_object_checked (domain, ass, &error);
-       if (!result)
-               mono_error_set_pending_exception (&error);
+
+leave:
+       mono_error_set_pending_exception (&error);
+       g_free (name);
        return result;
 }
 
@@ -2040,7 +2040,7 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef,
        MonoAssembly *ass;
        MonoAssemblyName aname;
        MonoReflectionAssembly *refass = NULL;
-       gchar *name;
+       gchar *name = NULL;
        gboolean parsed;
 
        g_assert (assRef);
@@ -2049,16 +2049,13 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef,
        if (mono_error_set_pending_exception (&error))
                return NULL;
        parsed = mono_assembly_name_parse (name, &aname);
-       g_free (name);
 
        if (!parsed) {
                /* This is a parse error... */
                if (!refOnly) {
                        refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly, &error);
-                       if (!mono_error_ok (&error)) {
-                               mono_error_set_pending_exception (&error);
-                               return NULL;
-                       }
+                       if (!is_ok (&error))
+                               goto leave;
                }
                return refass;
        }
@@ -2070,25 +2067,28 @@ ves_icall_System_AppDomain_LoadAssembly (MonoAppDomain *ad,  MonoString *assRef,
                /* MS.NET doesn't seem to call the assembly resolve handler for refonly assemblies */
                if (!refOnly) {
                        refass = mono_try_assembly_resolve (domain, assRef, NULL, refOnly, &error);
-                       if (!mono_error_ok (&error)) {
-                               mono_error_set_pending_exception (&error);
-                               return NULL;
-                       }
+                       if (!is_ok (&error))
+                               goto leave;
                }
                else
                        refass = NULL;
-               if (!refass) {
-                       return NULL;
-               }
+               if (!refass)
+                       goto leave;
+               ass = refass->assembly;
        }
 
-       if (refass == NULL)
+       g_assert (ass);
+       if (refass == NULL) {
                refass = mono_assembly_get_object_checked (domain, ass, &error);
+               if (!is_ok (&error))
+                       goto leave;
+       }
 
-       if (refass == NULL)
-               mono_error_set_pending_exception (&error);
-       else
-               MONO_OBJECT_SETREF (refass, evidence, evidence);
+       MONO_OBJECT_SETREF (refass, evidence, evidence);
+
+leave:
+       g_free (name);
+       mono_error_set_pending_exception (&error);
        return refass;
 }
 
index 643c4930b1a29360e4c80fc4cae1df1cf51da0bf..b7dc085524205db8e3cf38758ea005bdab90f1b0 100644 (file)
@@ -214,6 +214,9 @@ mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *request
 static MonoBoolean
 mono_assembly_is_in_gac (const gchar *filanem);
 
+static MonoAssembly*
+prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly);
+
 static gchar*
 encode_public_tok (const guchar *token, gint32 len)
 {
@@ -1208,6 +1211,7 @@ mono_assembly_load_reference (MonoImage *image, int index)
                                   aname.major, aname.minor, aname.build, aname.revision,
                                   strlen ((char*)aname.public_key_token) == 0 ? "(none)" : (char*)aname.public_key_token, extra_msg);
                g_free (extra_msg);
+
        }
 
        mono_assemblies_lock ();
@@ -1826,7 +1830,7 @@ mono_assembly_load_friends (MonoAssembly* ass)
 
 
 /**
- * mono_assembly_get_reference_assembly_attribute:
+ * mono_assembly_has_reference_assembly_attribute:
  * @assembly: a MonoAssembly
  * @error: set on error.
  *
@@ -1834,7 +1838,7 @@ mono_assembly_load_friends (MonoAssembly* ass)
  * On error returns FALSE and sets @error.
  */
 gboolean
-mono_assembly_get_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error)
+mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error)
 {
        mono_error_init (error);
 
@@ -1843,14 +1847,8 @@ mono_assembly_get_reference_assembly_attribute (MonoAssembly *assembly, MonoErro
        if (!attrs)
                return FALSE;
        MonoClass *ref_asm_class = mono_class_try_get_reference_assembly_class ();
-       gboolean result = FALSE;
-       for (int i = 0; i < attrs->num_attrs; ++i) {
-               MonoCustomAttrEntry *attr = &attrs->attrs [i];
-               if (attr->ctor && attr->ctor->klass && attr->ctor->klass == ref_asm_class) {
-                       result = TRUE;
-                       break;
-               }
-       }
+       g_assert (ref_asm_class != NULL && ref_asm_class != mono_defaults.object_class && !strcmp(ref_asm_class->name, "ReferenceAssemblyAttribute") );
+       gboolean result = mono_custom_attrs_has_attr (attrs, ref_asm_class);
        mono_custom_attrs_free (attrs);
        return result;
 }
@@ -1990,6 +1988,36 @@ mono_assembly_load_from_full (MonoImage *image, const char*fname,
                return ass2;
        }
 
+       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Prepared to set up assembly '%s' (%s)", ass->aname.name, image->name);
+
+       /* We need to check for ReferenceAssmeblyAttribute before we
+        * mark the assembly as loaded and before we fire the load
+        * hook. Otherwise mono_domain_fire_assembly_load () in
+        * appdomain.c will cache a mapping from the assembly name to
+        * this image and we won't be able to look for a different
+        * candidate. */
+
+       if (!refonly && strcmp (ass->aname.name, "mscorlib") != 0) {
+               /* Don't check for reference assmebly attribute for
+                * corlib here because if corlib isn't loaded yet,
+                * it's too early to set up the
+                * ReferenceAssemblyAttribute class.  We check that
+                * we're not running with a reference corlib in
+                * mono_init_internal().
+                */
+               MonoError refasm_error;
+               if (mono_assembly_has_reference_assembly_attribute (ass, &refasm_error)) {
+                       mono_assemblies_unlock ();
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Image for assembly '%s' (%s) has ReferenceAssemblyAttribute, skipping", ass->aname.name, image->name);
+                       g_free (ass);
+                       g_free (base_dir);
+                       mono_image_close (image);
+                       *status = MONO_IMAGE_IMAGE_INVALID;
+                       return NULL;
+               }
+               mono_error_cleanup (&refasm_error);
+       }
+
        image->assembly = ass;
 
        loaded_assemblies = g_list_prepend (loaded_assemblies, ass);
@@ -3193,6 +3221,19 @@ return_corlib_and_facades:
        return corlib;
 }
 
+static MonoAssembly*
+prevent_reference_assembly_from_running (MonoAssembly* candidate, gboolean refonly)
+{
+       MonoError refasm_error;
+       mono_error_init (&refasm_error);
+       if (candidate && !refonly && mono_assembly_has_reference_assembly_attribute (candidate, &refasm_error)) {
+               candidate = NULL;
+       }
+       mono_error_cleanup (&refasm_error);
+       return candidate;
+}
+
+
 MonoAssembly*
 mono_assembly_load_full_nosearch (MonoAssemblyName *aname, 
                                                                  const char       *basedir, 
@@ -3274,9 +3315,11 @@ mono_assembly_load_full_internal (MonoAssemblyName *aname, MonoAssembly *request
 {
        MonoAssembly *result = mono_assembly_load_full_nosearch (aname, basedir, status, refonly);
 
-       if (!result)
+       if (!result) {
                /* Try a postload search hook */
                result = mono_assembly_invoke_search_hook_internal (aname, requesting, refonly, TRUE);
+               result = prevent_reference_assembly_from_running (result, refonly);
+       }
        return result;
 }
 
index a147e41a075ba753e735cd5c33489aaa441ab7d2..c45974a0d5d232de4fb4d60e115b5f1c747c88ea 100644 (file)
@@ -5549,6 +5549,7 @@ mono_class_setup_parent (MonoClass *klass, MonoClass *parent)
                        /* set the parent to something useful and safe, but mark the type as broken */
                        parent = mono_defaults.object_class;
                        mono_class_set_type_load_failure (klass, "");
+                       g_assert (parent);
                }
 
                klass->parent = parent;
index 91970fecf43328339720a34514dd0334f426d984..b30f3d8ef594b24682e5917cd318776d3784a96a 100644 (file)
@@ -1438,7 +1438,7 @@ mono_custom_attrs_has_attr (MonoCustomAttrInfo *ainfo, MonoClass *attr_klass)
                if (centry->ctor == NULL)
                        continue;
                MonoClass *klass = centry->ctor->klass;
-               if (mono_class_has_parent (klass, attr_klass) || (MONO_CLASS_IS_INTERFACE (attr_klass) && mono_class_is_assignable_from (attr_klass, klass)))
+               if (klass == attr_klass || mono_class_has_parent (klass, attr_klass) || (MONO_CLASS_IS_INTERFACE (attr_klass) && mono_class_is_assignable_from (attr_klass, klass)))
                        return TRUE;
        }
        return FALSE;
@@ -1468,7 +1468,7 @@ mono_custom_attrs_get_attr_checked (MonoCustomAttrInfo *ainfo, MonoClass *attr_k
                if (centry->ctor == NULL)
                        continue;
                MonoClass *klass = centry->ctor->klass;
-               if (mono_class_is_assignable_from (attr_klass, klass))
+               if (attr_klass == klass || mono_class_is_assignable_from (attr_klass, klass))
                        break;
        }
        if (centry == NULL)
index b845fc2708e7186d19458c90c47ce86045dcdb94..5448aa4ca8f50d65095781497ed21baf794face5 100644 (file)
@@ -697,7 +697,7 @@ void
 mono_context_init_checked (MonoDomain *domain, MonoError *error);
 
 gboolean
-mono_assembly_get_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error);
+mono_assembly_has_reference_assembly_attribute (MonoAssembly *assembly, MonoError *error);
 
 
 #endif /* __MONO_METADATA_DOMAIN_INTERNALS_H__ */
index dfb3c29eefc5af88d07cdc37c168c66c6f39a58c..39dd1ca416571b02f73e94eb4afea4fc677457ed 100644 (file)
@@ -816,6 +816,16 @@ mono_init_internal (const char *filename, const char *exe_filename, const char *
 
        mono_profiler_appdomain_name (domain, domain->friendly_name);
 
+       /* Have to do this quite late so that we at least have System.Object */
+       MonoError custom_attr_error;
+       if (mono_assembly_has_reference_assembly_attribute (ass, &custom_attr_error)) {
+               char *corlib_file = g_build_filename (mono_assembly_getrootdir (), "mono", current_runtime->framework_version, "mscorlib.dll", NULL);
+               g_print ("Could not load file or assembly %s. Reference assemblies should not be loaded for execution.  They can only be loaded in the Reflection-only loader context.", corlib_file);
+               g_free (corlib_file);
+               exit (1);
+       }
+       mono_error_assert_ok (&custom_attr_error);
+
        return domain;
 }