[mono] Update ignored assemblies list to include System.ValueTuple
[mono.git] / mono / metadata / image.c
index 50eaded2e37279442560273998901d1e3cbb82ea..504b64294e0c053a002d038563ab131cbfc487e9 100644 (file)
@@ -27,7 +27,7 @@
 #include "loader.h"
 #include "marshal.h"
 #include "coree.h"
-#include <mono/io-layer/io-layer.h>
+#include <mono/utils/checked-build.h>
 #include <mono/utils/mono-logger-internals.h>
 #include <mono/utils/mono-path.h>
 #include <mono/utils/mono-mmap.h>
@@ -45,6 +45,7 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#include <mono/metadata/w32error.h>
 
 #define INVALID_ADDRESS 0xffffffff
 
@@ -66,18 +67,43 @@ enum {
 };
 static GHashTable *loaded_images_hashes [4] = {NULL, NULL, NULL, NULL};
 
-static GHashTable *get_loaded_images_hash (gboolean refonly)
+static GHashTable *
+get_loaded_images_hash (gboolean refonly)
 {
        int idx = refonly ? IMAGES_HASH_PATH_REFONLY : IMAGES_HASH_PATH;
        return loaded_images_hashes [idx];
 }
 
-static GHashTable *get_loaded_images_by_name_hash (gboolean refonly)
+static GHashTable *
+get_loaded_images_by_name_hash (gboolean refonly)
 {
        int idx = refonly ? IMAGES_HASH_NAME_REFONLY : IMAGES_HASH_NAME;
        return loaded_images_hashes [idx];
 }
 
+// Change the assembly set in `image` to the assembly set in `assemblyImage`. Halt if overwriting is attempted.
+// Can be used on modules loaded through either the "file" or "module" mechanism
+static gboolean
+assign_assembly_parent_for_netmodule (MonoImage *image, MonoImage *assemblyImage, MonoError *error)
+{
+       // Assembly to assign
+       MonoAssembly *assembly = assemblyImage->assembly;
+
+       while (1) {
+               // Assembly currently assigned
+               MonoAssembly *assemblyOld = image->assembly;
+               if (assemblyOld) {
+                       if (assemblyOld == assembly)
+                               return TRUE;
+                       mono_error_set_bad_image (error, assemblyImage, "Attempted to load module %s which has already been loaded by assembly %s. This is not supported in Mono.", image->name, assemblyOld->image->name);
+                       return FALSE;
+               }
+               gpointer result = InterlockedExchangePointer((gpointer *)&image->assembly, assembly);
+               if (result == assembly)
+                       return TRUE;
+       }
+}
+
 static gboolean debug_assembly_unload = FALSE;
 
 #define mono_images_lock() if (mutex_inited) mono_os_mutex_lock (&images_mutex)
@@ -494,7 +520,7 @@ load_metadata_ptrs (MonoImage *image, MonoCLIImageInfo *iinfo)
                        image->heap_tables.size = read32 (ptr + 4);
                        ptr += 8 + 3;
                        image->uncompressed_metadata = TRUE;
-                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly '%s' has the non-standard metadata heap #-.\nRecompile it correctly (without the /incremental switch or in Release mode).\n", image->name);
+                       mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Assembly '%s' has the non-standard metadata heap #-.\nRecompile it correctly (without the /incremental switch or in Release mode).", image->name);
                } else if (strncmp (ptr + 8, "#Pdb", 5) == 0) {
                        image->heap_pdb.data = image->raw_metadata + read32 (ptr);
                        image->heap_pdb.size = read32 (ptr + 4);
@@ -632,13 +658,13 @@ load_modules (MonoImage *image)
 }
 
 /**
- * mono_image_load_module:
+ * mono_image_load_module_checked:
  *
  *   Load the module with the one-based index IDX from IMAGE and return it. Return NULL if
- * it cannot be loaded.
+ * it cannot be loaded. NULL without MonoError being set will be interpreted as "not found".
  */
 MonoImage*
-mono_image_load_module (MonoImage *image, int idx)
+mono_image_load_module_checked (MonoImage *image, int idx, MonoError *error)
 {
        MonoTableInfo *t;
        MonoTableInfo *file_table;
@@ -648,6 +674,8 @@ mono_image_load_module (MonoImage *image, int idx)
        GList *list_iter, *valid_modules = NULL;
        MonoImageOpenStatus status;
 
+       mono_error_init (error);
+
        if ((image->module_count == 0) || (idx > image->module_count || idx <= 0))
                return NULL;
        if (image->modules_loaded [idx - 1])
@@ -683,10 +711,18 @@ mono_image_load_module (MonoImage *image, int idx)
                }
                if (valid) {
                        module_ref = g_build_filename (base_dir, name, NULL);
-                       image->modules [idx - 1] = mono_image_open_full (module_ref, &status, refonly);
-                       if (image->modules [idx - 1]) {
-                               mono_image_addref (image->modules [idx - 1]);
-                               image->modules [idx - 1]->assembly = image->assembly;
+                       MonoImage *moduleImage = mono_image_open_full (module_ref, &status, refonly);
+                       if (moduleImage) {
+                               if (!assign_assembly_parent_for_netmodule (moduleImage, image, error)) {
+                                       mono_image_close (moduleImage);
+                                       g_free (module_ref);
+                                       g_free (base_dir);
+                                       g_list_free (valid_modules);
+                                       return NULL;
+                               }
+
+                               image->modules [idx - 1] = moduleImage;
+
 #ifdef HOST_WIN32
                                if (image->modules [idx - 1]->is_module_handle)
                                        mono_image_fixup_vtable (image->modules [idx - 1]);
@@ -705,6 +741,15 @@ mono_image_load_module (MonoImage *image, int idx)
        return image->modules [idx - 1];
 }
 
+MonoImage*
+mono_image_load_module (MonoImage *image, int idx)
+{
+       MonoError error;
+       MonoImage *result = mono_image_load_module_checked (image, idx, &error);
+       mono_error_assert_ok (&error);
+       return result;
+}
+
 static gpointer
 class_key_extract (gpointer value)
 {
@@ -716,7 +761,7 @@ class_key_extract (gpointer value)
 static gpointer*
 class_next_value (gpointer value)
 {
-       MonoClass *klass = (MonoClass *)value;
+       MonoClassDef *klass = (MonoClassDef *)value;
 
        return (gpointer*)&klass->next_class_cache;
 }
@@ -1032,6 +1077,109 @@ install_pe_loader (void)
        mono_install_image_loader (&pe_loader);
 }
 
+/*
+Ignored assemblies.
+
+There are some assemblies we need to ignore because they include an implementation that doesn't work under mono.
+Mono provides its own implementation of those assemblies so it's safe to do so.
+
+The ignored_assemblies list is generated using tools/nuget-hash-extractor and feeding the problematic nugets to it.
+
+Right now the list of nugets are the ones that provide the assemblies in $ignored_assemblies_names.
+
+This is to be removed once a proper fix is shipped through nuget.
+
+*/
+
+typedef enum {
+       SYS_RT_INTEROP_RUNTIME_INFO = 0, //System.Runtime.InteropServices.RuntimeInformation
+       SYS_GLOBALIZATION_EXT = 1, //System.Globalization.Extensions
+       SYS_IO_COMPRESSION = 2, //System.IO.Compression
+       SYS_NET_HTTP = 3, //System.Net.Http
+       SYS_TEXT_ENC_CODEPAGES = 4, //System.Text.Encoding.CodePages
+       SYS_REF_DISP_PROXY = 5, //System.Reflection.DispatchProxy
+       SYS_VALUE_TUPLE = 6, //System.ValueTuple
+} IgnoredAssemblyNames;
+
+typedef struct {
+       int hash;
+       int assembly_name;
+       const char guid [40];
+} IgnoredAssembly;
+
+const char *ignored_assemblies_names[] = {
+       "System.Runtime.InteropServices.RuntimeInformation.dll",
+       "System.Globalization.Extensions.dll",
+       "System.IO.Compression.dll",
+       "System.Net.Http.dll",
+       "System.Text.Encoding.CodePages.dll",
+       "System.Reflection.DispatchProxy.dll",
+       "System.ValueTuple.dll"
+};
+
+#define IGNORED_ASSEMBLY(HASH, NAME, GUID, VER_STR)    { .hash = HASH, .assembly_name = NAME, .guid = GUID }
+
+static const IgnoredAssembly ignored_assemblies [] = {
+       IGNORED_ASSEMBLY (0x1136045D, SYS_GLOBALIZATION_EXT, "475DBF02-9F68-44F1-8FB5-C9F69F1BD2B1", "4.0.0 net46"),
+       IGNORED_ASSEMBLY (0x358C9723, SYS_GLOBALIZATION_EXT, "5FCD54F0-4B97-4259-875D-30E481F02EA2", "4.0.1 net46"),
+       IGNORED_ASSEMBLY (0x450A096A, SYS_GLOBALIZATION_EXT, "E9FCFF5B-4DE1-4BDC-9CE8-08C640FC78CC", "4.3.0 net46"),
+       IGNORED_ASSEMBLY (0x1CBD59A2, SYS_IO_COMPRESSION, "44FCA06C-A510-4B3E-BDBF-D08D697EF65A", "4.1.0 net46"),
+       IGNORED_ASSEMBLY (0x5E393C29, SYS_IO_COMPRESSION, "3A58A219-266B-47C3-8BE8-4E4F394147AB", "4.3.0 net46"),
+       IGNORED_ASSEMBLY (0x27726A90, SYS_NET_HTTP, "269B562C-CC15-4736-B1B1-68D4A43CAA98", "4.1.0 net46"),
+       IGNORED_ASSEMBLY (0x10CADA75, SYS_NET_HTTP, "EA2EC6DC-51DD-479C-BFC2-E713FB9E7E47", "4.1.1 net46"),
+       IGNORED_ASSEMBLY (0x8437178B, SYS_NET_HTTP, "C0E04D9C-70CF-48A6-A179-FBFD8CE69FD0", "4.3.0 net46"),
+       IGNORED_ASSEMBLY (0x4A15555E, SYS_REF_DISP_PROXY, "E40AFEB4-CABE-4124-8412-B46AB79C92FD", "4.0.0 net46"),
+       IGNORED_ASSEMBLY (0xD20D9783, SYS_REF_DISP_PROXY, "2A69F0AD-B86B-40F2-8E4C-5B671E47479F", "4.0.1 netstandard1.3"),
+       IGNORED_ASSEMBLY (0xA33A7E68, SYS_REF_DISP_PROXY, "D4E8D2DB-BD65-4168-99EA-D2C1BDEBF9CC", "4.3.0 netstandard1.3"),
+       IGNORED_ASSEMBLY (0x46A4A1C5, SYS_RT_INTEROP_RUNTIME_INFO, "F13660F8-9D0D-419F-BA4E-315693DD26EA", "4.0.0 net45"),
+       IGNORED_ASSEMBLY (0xD07383BB, SYS_RT_INTEROP_RUNTIME_INFO, "DD91439F-3167-478E-BD2C-BF9C036A1395", "4.3.0 net45"),
+       IGNORED_ASSEMBLY (0x911D9EC3, SYS_TEXT_ENC_CODEPAGES, "C142254F-DEB5-46A7-AE43-6F10320D1D1F", "4.0.1 net46"),
+       IGNORED_ASSEMBLY (0xFA686A38, SYS_TEXT_ENC_CODEPAGES, "FD178CD4-EF4F-44D5-9C3F-812B1E25126B", "4.3.0 net46"),
+       IGNORED_ASSEMBLY (0x75B4B041, SYS_VALUE_TUPLE, "F81A4140-A898-4E2B-B6E9-55CE78C273EC", "4.3.0 netstandard1.0"),
+};
+
+/*
+Equivalent C# code:
+       static void Main  () {
+               string str = "...";
+               int h = 5381;
+        for (int i = 0;  i < str.Length; ++i)
+            h = ((h << 5) + h) ^ str[i];
+
+               Console.WriteLine ("{0:X}", h);
+       }
+*/
+static int
+hash_guid (const char *str)
+{
+       int h = 5381;
+    while (*str) {
+        h = ((h << 5) + h) ^ *str;
+               ++str;
+       }
+
+       return h;
+}
+
+static gboolean
+is_problematic_image (MonoImage *image)
+{
+       int h = hash_guid (image->guid);
+
+       //TODO make this more cache effiecient.
+       // Either sort by hash and bseach or use SoA and make the linear search more cache efficient.
+       for (int i = 0; i < G_N_ELEMENTS (ignored_assemblies); ++i) {
+               if (ignored_assemblies [i].hash == h && !strcmp (image->guid, ignored_assemblies [i].guid)) {
+                       const char *needle = ignored_assemblies_names [ignored_assemblies [i].assembly_name];
+                       size_t needle_len = strlen (needle);
+                       size_t asm_len = strlen (image->name);
+                       if (asm_len > needle_len && !g_ascii_strcasecmp (image->name + (asm_len - needle_len), needle))
+                               return TRUE;
+               }
+       }
+       return FALSE;
+}
+
 static MonoImage *
 do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
                    gboolean care_about_cli, gboolean care_about_pecoff)
@@ -1087,6 +1235,12 @@ do_mono_image_load (MonoImage *image, MonoImageOpenStatus *status,
        if (!mono_image_load_cli_data (image))
                goto invalid_image;
 
+       if (!image->ref_only && is_problematic_image (image)) {
+               mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_ASSEMBLY, "Denying load of problematic image %s", image->name);
+               *status = MONO_IMAGE_IMAGE_INVALID;
+               goto invalid_image;
+       }
+
        if (image->loader == &pe_loader && !image->metadata_only && !mono_verifier_verify_table_data (image, &errors))
                goto invalid_image;
 
@@ -1396,7 +1550,7 @@ mono_image_open_full (const char *fname, MonoImageOpenStatus *status, gboolean r
                fname_utf16 = g_utf8_to_utf16 (absfname, -1, NULL, NULL, NULL);
                module_handle = MonoLoadImage (fname_utf16);
                if (status && module_handle == NULL)
-                       last_error = GetLastError ();
+                       last_error = mono_w32error_get_last ();
 
                /* mono_image_open_from_module_handle is called by _CorDllMain. */
                image = g_hash_table_lookup (loaded_images, absfname);
@@ -1653,6 +1807,17 @@ mono_wrapper_caches_free (MonoWrapperCaches *cache)
        free_hash (cache->thunk_invoke_cache);
 }
 
+static void
+mono_image_close_except_pools_all (MonoImage**images, int image_count)
+{
+       for (int i = 0; i < image_count; ++i) {
+               if (images [i]) {
+                       if (!mono_image_close_except_pools (images [i]))
+                               images [i] = NULL;
+               }
+       }
+}
+
 /*
  * Returns whether mono_image_close_finish() must be called as well.
  * We must unload images in two steps because clearing the domain in
@@ -1773,7 +1938,6 @@ mono_image_close_except_pools (MonoImage *image)
                g_free (image->name);
                g_free (image->guid);
                g_free (image->version);
-               g_free (image->files);
        }
 
        if (image->method_cache)
@@ -1852,12 +2016,8 @@ mono_image_close_except_pools (MonoImage *image)
                g_free (image->image_info);
        }
 
-       for (i = 0; i < image->module_count; ++i) {
-               if (image->modules [i]) {
-                       if (!mono_image_close_except_pools (image->modules [i]))
-                               image->modules [i] = NULL;
-               }
-       }
+       mono_image_close_except_pools_all (image->files, image->file_count);
+       mono_image_close_except_pools_all (image->modules, image->module_count);
        if (image->modules_loaded)
                g_free (image->modules_loaded);
 
@@ -1876,6 +2036,17 @@ mono_image_close_except_pools (MonoImage *image)
        return TRUE;
 }
 
+static void
+mono_image_close_all (MonoImage**images, int image_count)
+{
+       for (int i = 0; i < image_count; ++i) {
+               if (images [i])
+                       mono_image_close_finish (images [i]);
+       }
+       if (images)
+               g_free (images);
+}
+
 void
 mono_image_close_finish (MonoImage *image)
 {
@@ -1891,12 +2062,8 @@ mono_image_close_finish (MonoImage *image)
                image->references = NULL;
        }
 
-       for (i = 0; i < image->module_count; ++i) {
-               if (image->modules [i])
-                       mono_image_close_finish (image->modules [i]);
-       }
-       if (image->modules)
-               g_free (image->modules);
+       mono_image_close_all (image->files, image->file_count);
+       mono_image_close_all (image->modules, image->module_count);
 
 #ifndef DISABLE_PERFCOUNTERS
        mono_perfcounters->loader_bytes -= mono_mempool_get_allocated (image->mempool);
@@ -2153,8 +2320,9 @@ mono_image_get_resource (MonoImage *image, guint32 offset, guint32 *size)
        return data;
 }
 
+// Returning NULL with no error set will be interpeted as "not found"
 MonoImage*
-mono_image_load_file_for_image (MonoImage *image, int fileidx)
+mono_image_load_file_for_image_checked (MonoImage *image, int fileidx, MonoError *error)
 {
        char *base_dir, *name;
        MonoImage *res;
@@ -2162,6 +2330,8 @@ mono_image_load_file_for_image (MonoImage *image, int fileidx)
        const char *fname;
        guint32 fname_id;
 
+       mono_error_init (error);
+
        if (fileidx < 1 || fileidx > t->rows)
                return NULL;
 
@@ -2189,14 +2359,21 @@ mono_image_load_file_for_image (MonoImage *image, int fileidx)
        } else {
                int i;
                /* g_print ("loaded file %s from %s (%p)\n", name, image->name, image->assembly); */
-               res->assembly = image->assembly;
+               if (!assign_assembly_parent_for_netmodule (res, image, error)) {
+                       mono_image_unlock (image);
+                       mono_image_close (res);
+                       return NULL;
+               }
+
                for (i = 0; i < res->module_count; ++i) {
                        if (res->modules [i] && !res->modules [i]->assembly)
                                res->modules [i]->assembly = image->assembly;
                }
 
-               if (!image->files)
+               if (!image->files) {
                        image->files = g_new0 (MonoImage*, t->rows);
+                       image->file_count = t->rows;
+               }
                image->files [fileidx - 1] = res;
                mono_image_unlock (image);
                /* vtable fixup can't happen with the image lock held */
@@ -2212,6 +2389,15 @@ done:
        return res;
 }
 
+MonoImage*
+mono_image_load_file_for_image (MonoImage *image, int fileidx)
+{
+       MonoError error;
+       MonoImage *result = mono_image_load_file_for_image_checked (image, fileidx, &error);
+       mono_error_assert_ok (&error);
+       return result;
+}
+
 /**
  * mono_image_get_strong_name:
  * @image: a MonoImage
@@ -2400,6 +2586,8 @@ mono_image_has_authenticode_entry (MonoImage *image)
 {
        MonoCLIImageInfo *iinfo = (MonoCLIImageInfo *)image->image_info;
        MonoDotNetHeader *header = &iinfo->cli_header;
+       if (!header)
+               return FALSE;
        MonoPEDirEntry *de = &header->datadir.pe_certificate_table;
        // the Authenticode "pre" (non ASN.1) header is 8 bytes long
        return ((de->rva != 0) && (de->size > 8));
@@ -2450,6 +2638,31 @@ mono_image_strdup (MonoImage *image, const char *s)
        return res;
 }
 
+char*
+mono_image_strdup_vprintf (MonoImage *image, const char *format, va_list args)
+{
+       char *buf;
+       mono_image_lock (image);
+       buf = mono_mempool_strdup_vprintf (image->mempool, format, args);
+       mono_image_unlock (image);
+#ifndef DISABLE_PERFCOUNTERS
+       mono_perfcounters->loader_bytes += strlen (buf);
+#endif
+       return buf;
+}
+
+char*
+mono_image_strdup_printf (MonoImage *image, const char *format, ...)
+{
+       char *buf;
+       va_list args;
+
+       va_start (args, format);
+       buf = mono_image_strdup_vprintf (image, format, args);
+       va_end (args);
+       return buf;
+}
+
 GList*
 g_list_prepend_image (MonoImage *image, GList *list, gpointer data)
 {
@@ -2522,6 +2735,7 @@ mono_image_property_lookup (MonoImage *image, gpointer subject, guint32 property
 void
 mono_image_property_insert (MonoImage *image, gpointer subject, guint32 property, gpointer value)
 {
+       CHECKED_METADATA_STORE_LOCAL (image->mempool, value);
        mono_image_lock (image);
        mono_property_hash_insert (image->property_hash, subject, property, value);
        mono_image_unlock (image);