[Mono-dev] Strict strong names assembly loading proposal
alklig at microsoft.com
Thu Apr 27 16:29:46 UTC 2017
Below is a proposal for some changes to how mono handles assemblies with strong names during loading.
Strict strong names assembly loading proposal
Background: assembly names on .NET
Assemblies on .NET may optionally have a strong name: which includes a simple name, a culture, a version number and a public key token. Two names are equal when all the components are equal.
Assembly bindings, BCL and Facades
There is a separate more complicated story about configuration files that can remap requests for certain assembly versions to others. Additionally, Mono has special rules for BCL assemblies that ship as part of Mono itself and for Facades. It is interesting and important to get right, but somewhat obscures the issue at hand. From this point forward, assume that any remapping of requests by all these mechanisms have already happened.
Loading strong named assemblies on .NET
If there is a request for an assembly with a strong name (either statically - via a reference from another assembly, or dynamically via reflection), then the names of any assembly images found using the usual search mechanism are examined and if that assembly image has an incorrect version or public key, it is skipped and the search continues.
Loading strong named assemblies on Mono
Currently if there is a request for a strong named assembly the following steps happen in Mono until one of them returns true.:
1. We look in the search_path of the current application domain (ApplicationBase suffixed by each subdir in PrivateBinPath)
2. We look in the MONO_PATH
3. We look in the GAC
4. We look in the default path (typically $prefix/lib/mono/4.5/ and its Facades/ subdir)
5. We call back into managed code to the application domain's AssemblyResolve event delegate
6. We look in the search_path of the current application domain
7. We look in the MONO_PATH
8. We look in the GAC
9. We look in the base directory of the requesting assembly
10. We look in the default path
(Steps 6-8, and 10 are redundant, but nonetheless this is what we do. I'm not planning on changing this right now.)
Only the GAC steps check strong names (indirectly - the directory structure of the GAC encodes public key tokens and version numbers).
In all the other steps if we find an image whose filename matches the assembly's simple name (ie: "System.Reflection.dll" for "System.Reflection") we accept it and don't look at the version or the public key at all.
When Mono's mechanism is not a problem
The Xamarin.iOS, Xamarin.Mac, and Xamarin.Android products have a limitation that multiple versions of the same assembly can't be used.
They copy all the assemblies that an app will need to a single directory, AOT (if applicable) all the assemblies and expect all assembly resolution to succeed and only look at the chosen set of assemblies. (ie they don't use the GAC or domains and generally set things up so that Mono only looks in a single directory).
I'm told that support has instructions for a workaround for customers who run into this limitation (can't have multiple versions in one app).
When Mono's mechanism is a problem
On the desktop it is possible (and some frameworks rely on) loading different versions of the same assembly into the same application domain.
Because we take the first file that matches, this can sometimes result in silently substituting an unexpected version of an assembly. This can result in incorrect code execution.
We have a couple of bugzilla bugs about the issue over the years:
• https://bugzilla.xamarin.com/show_bug.cgi?id=580 from 2011
• https://bugzilla.xamarin.com/show_bug.cgi?id=49721 from 2016
1. We add a command line argument --assembly-loader=[strict,legacy] Defaulting to strict.
In strict mode: Each of the assembly resolution steps, above, checks that any assembly image with the right filename also has the right public key and version.
In legacy mode: We keep mono's existing behavior.
2. In addition we rename the existing feature flag --enable-minimal=assembly_remapping to --enable-minimal=desktop_loader to disables strict mode (and the existing feature of assembly_remapping that disables the special remapping of BCL assembly versions). The --assembly-loader command line option will be silently accepted and ignored. So that products can continue to use the existing Mono behavior.
Think of disabling the desktop loader as "I know precisely which assemblies I need and I put them all in one place."
More information about the Mono-devel-list