Loading the Same Assembly with Different Evidence


Assembly.Load provides overloads that take an Evidence object in addition to the name of the assembly to load.  This leads to the question — what happens if you were to load the same assembly multiple times with different Evidence.


It’s easy enough to figure that out with a few lines of code:



Evidence myComputer = new Evidence(
    new object[] { new Zone(SecurityZone.MyComputer) },
    new object[] { });
Evidence intranet = new Evidence(
    new object[] { new Zone(SecurityZone.Intranet) },
    new object[] { });

Assembly intranetAsm = Assembly.Load(“TestAssembly”, intranet);
Assembly myComputerAsm = Assembly.Load(“TestAssembly”, myComputer);

Console.WriteLine(
    “myComputerAsm ReferenceEquals intranetAsm? {0}”,
    Object.ReferenceEquals(myComputerAsm, intranetAsm));

Console.WriteLine();
Console.WriteLine(“myComputerAsm Evidence”);
foreach(object o in myComputerAsm.Evidence)
    if(o is Zone)
       Console.WriteLine(o);

Console.WriteLine();
Console.WriteLine(“localIntranetAsm Evidence”);
foreach(object o in intranetAsm.Evidence)
    if(o is Zone)
        Console.WriteLine(o);


This program loads TestAssembly twice, first with evidence saying it came from the intranet, and then with evidence saying it belongs to the MyComputer zone.  After loading the assembly, we then check to see if we’ve got the same Assembly instance from each load, and dump out any Zone evidence that we find.


Running this program shows:



myComputerAsm ReferenceEquals intranetAsm? True

myComputerAsm Evidence
<System.Security.Policy.Zone version=”1″>
<Zone>Intranet</Zone>
</System.Security.Policy.Zone>


localIntranetAsm Evidence
<System.Security.Policy.Zone version=”1″>
<Zone>Intranet</Zone>
</System.Security.Policy.Zone>


The results may be somewhat surprising.  Even though we explicitly specified that the assembly be loaded with two different sets of evidence, we actually ended up with the exact same Assembly objects.  Obviously, since they were the same objects, they also both have the same Evidence which matches the first Evidence we applied.


Why is this?  Well, an assembly can only be loaded once into each AppDomain.  The CLR doesn’t support the concept of having two copies of an assembly floating around a domain with different grant sets.  Given that an assembly can only be loaded once, the Evidence is applied and evaluated for the first Load; further attempts to load the same assembly just return the already loaded version without evaluating policy.


Why doesn’t the CLR notice that the evidence objects are different and throw an exception warning the user?  There’s actually no real good way for us to do that.  Since Evidence is basically just a collection of Object, we have no way of compelling any object that’s being used as evidence to create a useful override of Equals.  That means internally the CLR doesn’t have a good way of actually determining if two sets of Evidence are equivalent or not.  Using the default Equals implementation won’t work, since we don’t want to disallow loading an assembly twice with different instances of equivalent evidence objects.


All that being said, remember that assemblies can in fact have different permission sets in different AppDomains.  So if you really want to have an assembly be loaded into your process with multiple grant sets, you’ll just need to ensure that each time you load the Assembly with a new grant set you place it in a new AppDomain.

Comments (5)

  1. mihailik says:

    Consider behavior, where just first Load with Evidency parameter is valid. And any subsequent Load should be done with null Evidency.

    So, 2nd, 3rd and further calls to Load with non-null Evidency parameter should throw exception.

    It is much more consistent, with normal practice. If parameter is invalid for current state, it should throw. What would you say if FileStream ctor with Write access argument will fail silently and return you Read-Only object instead of throwing?

  2. True — there are many other designs we could have gone with. One possible issue with that design is that it exposes race conditions. Any threads that need to load assemblies need to hold a "loader lock" of sorts, otherwise the unlucky second thread to load with Evidence will end up with the exception.

    -Shawn

  3. Chris says:

    I have a problem with loading assemblies of plug-ins at runtime because they relate to some of the assemblies of the main application which have a slightly newer version.

    E.g. We have some plug-ins (running also in the main AppDomain because they are well known and installed by an administrator) which are compiled against say version 1.0.0.0 of the Hostapplication. Now if we replace the host application through 1.0.0.1 the plug-ins fail to load because they expect to find version 1.0.0.0 of a referenced assembly.

    Any ideas what I have to change that the plug-ins automatically take the assembly version that is loaded? Can I emit a publisher policy at runtime as I know which assemblies are loaded and have the old assemblies in hands (as binary and reflection-only assemblies)?

    Thanks for any help

    Chris

  4. Hi Chris,

    The AddIn team is doing a lot of work to enable AddIns and hosts to version independently of each other.  You might want to check out their blog here: http://blogs.msdn.com/clraddins/ for more information on their versioning solution.

    -Shawn

  5. Chris says:

    Great, that seems to be exactely the right blog for me.

    Thanks you and bye

    Chris