[Mono-dev] Strict strong names assembly loading proposal
anmccl at microsoft.com
Thu Apr 27 16:32:22 UTC 2017
My only concern is about whether the assembly-loader default should be "legacy" or "strict" in the first release. In our testing it seems turning "strict" on is useful (because most of the things it rejects really are incorrect) but often breaks software, because software developed against mono has become accustomed to being allowed to do incorrect things. Moreover, we find that when software breaks in strict mode, the error reporting is confusing. The breakage does not occur at load time, but later as a managed exception when you try to use a class from the unloadable-in-strict-mode assembly, and the message you get is "assembly not found" (rather than something like "assembly not found, found candidate 'blah' but it had the wrong version").
My proposal is that in 5.2, we should make the default "legacy". In some later release, we can improve the error reporting and consider making the default "strict". This way we can unblock people who need strict mode now, and have the strict mode getting tested in our automated tests, without creating a compatibility issue for existing apps.
Also, two things we should consider as add-ons once PR#4759/4760 are in:
- I think it would be good if in addition to "legacy" and "strict", there were a "warn" mode. This mode would behave like "legacy" mode, but on loading an assembly, it would check if the strong name is correct or not and print a message to stderr.
- My understanding from talking to Aleksey is that we don't support strong name culture yet in the current version of strict mode. We are having a lot of trouble finding an example of someone using the culture data outside of satellite assemblies, BUT if we're going to have a mode in 5.2 that supports the standard we should go all the way.
If anyone on the list has a use case for culture in assembly strong names, we'd be very curious to hear it…
On 4/27/17, 12:29 PM, "Mono-devel-list on behalf of Aleksey Kliger via Mono-devel-list" <mono-devel-list-bounces at lists.dot.net on behalf of mono-devel-list at lists.dot.net> wrote:
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://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.xamarin.com%2Fshow_bug.cgi%3Fid%3D580&data=02%7C01%7Canmccl%40microsoft.com%7C9c221e64753f4424006b08d48d8aa32d%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636289073965597894&sdata=lEyUygQFk2h%2B7DnREpNHiWP28II6kQNY0cBmVq2jvQI%3D&reserved=0 from 2011
• https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fbugzilla.xamarin.com%2Fshow_bug.cgi%3Fid%3D49721&data=02%7C01%7Canmccl%40microsoft.com%7C9c221e64753f4424006b08d48d8aa32d%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636289073965597894&sdata=19EAjFfdfVL3fqfoCOfvzpYM3luKCYZ5t58rvkHn%2FiA%3D&reserved=0 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."
Mono-devel-list mailing list
Mono-devel-list at lists.dot.net
More information about the Mono-devel-list