Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / CLR / March 2005

Tip: Looking for answers? Try searching our database.

Assembly.Load fails after assembly preloaded using Assembly.LoadFrom (v1.1.4322)

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
kurbylogic@hotmail.com - 04 Mar 2005 05:55 GMT
I am creating a seperate appdomain to host assemblies having less than
full trust.  The second appdomain has a different ApplicationBase path
then the host.  I created a "DomainBroker" instance that implements
MarshalByRefObject to instruct load and execute the less then full
trust assemblies in the seperate domain so that the extra assembly will
not be loaded into the hosting domain.  This broker class is defined in
the hosting assembly so the host assembly will be loaded into the
second domain as well.  I used
AppDomain.CreateInstanceFromAndUnwrap(brokerType.Assembly.Location,
brokerType.FullName) to create the broker and this works fine.  However
when I send a serializable object also defined in the host assembly to
the broker it fails to deserialize with FileNotFoundExcetion because it
cannot find the hosting assembly, which is already loaded into the
remote domain.

I discovered it seems that when LoadFrom(path) is used, future
resolutions for that same assembly using Assembly.Load(fullname) will
still fail.  Here is a simple test that demonstrates the issue:
Create anotherAssembly.dll just a basic new class library solution with
a strong name.
Then create a console app with the following code:

using System.Reflection;
class Class1
{
[STAThread]
static void Main(string[] args)
{
    try
    {
        Test();
        Console.WriteLine("TEST PASSED");
    }
    catch(System.Exception ex)
    {
        Console.WriteLine("TEST FAILED");
        Console.WriteLine(ex.ToString());
    }
    Console.ReadLine();
}

static void Test()
{
    string anotherAssemblyLocation =
        "../../../AnotherAssembly/bin/debug/AnotherAssembly.dll";

    Assembly anotherAssembly = Assembly.LoadFrom(anotherAssemblyLocation);

    if(!IsLoaded(anotherAssembly.FullName))
        throw new ApplicationException("Assembly is not loaded");
    if(anotherAssembly.GetName().GetPublicKey().Length == 0)
        throw new ApplicationException("Assembly does not have strong name");

    Assembly.Load(anotherAssembly.FullName);
}

static bool IsLoaded(string assemblyName)
{
    foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies())
    {
        if(a.FullName == assemblyName)
            return true;
    }
    return false;
}
}

The LoadFrom works fine I verify that it has been loaded into the
domain (however pointless this seems) and that it has a strong name.
When assembly Assembly.Load() (as I'm assuming the serilizer is doing
when I pass my across my host object) fails even when the assembly is
already loaded.  I've also created another test where
anotherAssembly.dll is placed in the application directory or GAC and
the result is that the assembly is loaded into the application domain
twice.

Is this a bug? or is there a good reason why the framework behaves this
way?

There are a couple of solutions I've found:
Place the host assembly in the GAC and use Load instead of LoadFrom (to
prevent loading same assembly twice).
Disadvantage: Additional deployment steps and requires admin rights to
update GAC (so much for using a post-build script when running VS as
least privalaged user! well I guess I can write a post build utility to
impersonate admin before running gacutil.. there's always something to
making LPU dev more difficult then it needs be..., anyway)

Copy the host assembly to the ApplicationBase directory of the
secondary domain.
Disadvantage: versioning nightmare if not kept in sync, and
deserialization will fail;  Restricting CAS permissions with Url
security policy doens't seem to work correctly. (I found that if I
create a level final code group with url policy file://scriptdir/* with
only execution permission and a child code group with
file://scriptdir/hostassembly.dll with fulltrust then hostassembly.dll
still only recieves execute permission not the union of fulltrust and
execution... even though evaluate assembly policy shows the
hostassembly code group being applied.. perhaps I missed something,
anyways the first reason is bad enough).

Create an AssemblyResolve event handler with this pointless looking
code:
foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
 if(args.Name == a.FullName && a.GetName().GetPublicKey().Length > 0)
   return a;
}
return null;

Disadvantage: Only runs after resolution fails thus slightly degrading
performance; and ? (is there some valid reason the Assembly.Load does
not check the currently loaded assemblies, a security issue perhaps?
and would doing this expose that vunerability? without strong name I
might see this being an issue where a fulltrust assembly loads an
assembly from a different path into the app domain and then a
non-fulltrust assembly attempts to use a differenet assembly with same
name from its applicationbase but instead gets the preloaded one, so I
guess that could be bad.  However, with a strong named assembly I don't
see this being an issue?)

Thanks,

- Kurt
Nicole Calinoiu - 04 Mar 2005 15:16 GMT
<snip>
> Is this a bug? or is there a good reason why the framework behaves this
> way?

Your LoadFrom/Load test is behaving as I would expect given that the target
assembly is not located in a Fusion search location for the Load call.  As
for the double-loading, it's quite normal.  See
http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx for more
information concerning both of these behaviours.

> There are a couple of solutions I've found:
> Place the host assembly in the GAC and use Load instead of LoadFrom (to
[quoted text clipped - 4 lines]
> impersonate admin before running gacutil.. there's always something to
> making LPU dev more difficult then it needs be..., anyway)

It's quite normal to require that applications that are to be run as LPU be
installed while running as an admin.  Besides allowing actions such as
GACing, it also ensure that the application files cannot be altered by LPUs
(at least under default ACLs on program files directory on appropriate
OSes).

> Copy the host assembly to the ApplicationBase directory of the
> secondary domain.
> Disadvantage: versioning nightmare if not kept in sync, and
> deserialization will fail;

What's the versioning issue?  The file is presumably written once at
installation time.  When you prepare patch/upgrade deployments, your testing
should very quickly catch any problems due to failure to copy the assembly
to both locations.

> Restricting CAS permissions with Url
> security policy doens't seem to work correctly. (I found that if I
[quoted text clipped - 5 lines]
> hostassembly code group being applied.. perhaps I missed something,
> anyways the first reason is bad enough).

Not sure I really understand what you're getting at here.  If this is still
a problem, could you please post sample code for a simple repro?

> Create an AssemblyResolve event handler with this pointless looking
> code:
[quoted text clipped - 7 lines]
> Disadvantage: Only runs after resolution fails thus slightly degrading
> performance; and ?

Two assemblies with equivalent strong names are not guaranteed to be
identical under all circumstances.  The folks who worked on the Fusion
mechanism presumably spent a great deal of time thinking about all the
repercussions alternative behaviours of Load, and it might be best to trust
that they made the right decisions even if it's inconvenient...

> (is there some valid reason the Assembly.Load does
> not check the currently loaded assemblies, a security issue perhaps?
[quoted text clipped - 9 lines]
>
> - Kurt
kurbylogic@hotmail.com - 04 Mar 2005 17:26 GMT
> <snip>
> > Is this a bug? or is there a good reason why the framework behaves this
[quoted text clipped - 30 lines]
> should very quickly catch any problems due to failure to copy the assembly
> to both locations.

Okay not a "versioning nightmare" wrong words, a "deployment concern".
The concern is essentially that the exact same version of the file must
exist in 2 seperate physical locations and both must be exactly the
same or the application will not run properly.  Tus easily be caught
during testing.  As you said this only needs to be done once during
installation but also included in any patch/upgrade deployments, my
mistake.  It is still a disadvantage as if I must have to have 2 copies
of the same file I'd much rather use the GAC.

> > Restricting CAS permissions with Url
> > security policy doens't seem to work correctly. (I found that if I
[quoted text clipped - 8 lines]
> Not sure I really understand what you're getting at here.  If this is still
> a problem, could you please post sample code for a simple repro?

Create a folder c:\scriptdir
Create a strong named assembly and place it in the directory.
Open the .Net configuration tool
Create a URL CodeGroup file://c:/scriptdir/* with execute permission
set.
Check level final.
Create a child code group using strongname permission and import public
key from the assembly placed in the directory, grant full trust.
Using the Evaluate Assemlby select the strong named assembly previously
copied to this directory.  It should have recieved the FullTrust (union
of Execute and FullTrust), however it only recieves execute permission.

This is the code group section from the policy file, obviously your
strong name will differ so if you copy and paste you'll need to update
that.
<CodeGroup class="UnionCodeGroup"
                         version="1"
                         PermissionSetName="FullTrust"
                         Name="All_Code"
                         Description="Code group grants all code full
trust and forms the root of the code group tree.">
                 <IMembershipCondition class="AllMembershipCondition"
                                       version="1"/>
                 <CodeGroup class="UnionCodeGroup"
                            version="1"
                            PermissionSetName="Execution"
                            Attributes="Exclusive"
                            Name="ScriptDir"
                            Description="">
                    <IMembershipCondition
class="UrlMembershipCondition"
                                          version="1"

Url="file://C:/scriptdir/*"/>
                    <CodeGroup class="UnionCodeGroup"
                               version="1"
                               PermissionSetName="FullTrust"
                               Name="ScriptHost"
                               Description="FullTrust to host
assembly">
                       <IMembershipCondition
class="StrongNameMembershipCondition"
                                             version="1"

PublicKeyBlob="0024000004800000940000000602000000240000525341310004000001000100E7D3DD7E620B266EA32514C042881CE1905B0CCC968DC543B20286740D3B881B3BCCE3FB6EBC4B256DAB41512BE874B33658683CDCCF47D1A4532E60D54B7F1E6432B9700AC319FAF2B4592D4C13D146913395E19C64BB4DF474227855952D5F5E191DDFC1ECA667B5D52C6D08480D2579A9B1C868796227343EF44856B72ACC"/>
                    </CodeGroup>
                 </CodeGroup>
              </CodeGroup>

> > Create an AssemblyResolve event handler with this pointless looking
> > code:
[quoted text clipped - 13 lines]
> repercussions alternative behaviours of Load, and it might be best to trust
> that they made the right decisions even if it's inconvenient...

I was looking for a bit more than "presumably thought of" as if MS
thinks of everything.  I don't think that the possiblility of two
assemblies with the same strong name was the underlying issue however,
otherwise I would think Fusion not check the GAC before probing the
applicatonbase if a conflict were to occur then I would choose to use
assembly deployed with the app vs the one in the gac deployed by
whomever.  I also would not have expected the security team to use the
StrongNameMembershipCondition as a source of evidence to grant full
trust to the system assemblies.  I suspect the issue is rather subtle
if it is an issue at all, which I suspect not.  The duplicate loading
is probably not only expected but desired for non-strongnamed
assembiles, my suspision is that loading two instances of a
strong-named assembly into the same appdomain is unnecessary but
because it was desired for non-strong named assemblies there did not
seem to be any reason not allow strong-named to be loaded twice even if
unnecessary it probably was not thought of (aside from the small amount
of extra memory usage) to have any observable affects to the
application.  So personally I think its a bug.  Although minor
concidering I can use the GAC, so I guess I'll quit whining and just
place the assembly in the GAC.

The link you provided also mentioned another source used during Load
that even though I probably read this artical two or three times the
last few days I didn't notice before...  "..., a host assembly store
(if hosted),..."
It sounds like that is exactly what I need, but I don't see a
"HostAssemblyStore" or something like it in AppDomainSetup class, any
idea how this store(location/path?) is set or how assemblies added to
it?

> > (is there some valid reason the Assembly.Load does
> > not check the currently loaded assemblies, a security issue perhaps?
[quoted text clipped - 9 lines]
> >
> > - Kurt
"Rick Byers [MSFT]" - 07 Mar 2005 19:54 GMT
Hi Kurt,
As Nicole pointed out, Suzanne's blog is the best source of information on
this issue (she has several relevant articles).  Her general guidance of
"avoid LoadFrom as much as possible" is precisely due to issues like this.  

One reason why assemblies already loaded in the LoadFrom context aren't
consulted when doing a Load is that for the scenarios in which it was
designed, it is often preferable to have the results of LoadFrom isolated
from the rest of the application.  You generally do not want the behavior
of your application changing because some code previously did a LoadFrom.  
Imagine an ILDasm like tool where the user supplies a path name to an
assembly they want to examine.  Suppose they choose to examine an assembly
with the same strong name as one used by the tool itself (eg. two different
service packs of a system assembly) - do you really want that to mean the
application itself now uses that different file?  Predictability is
extremely important for the loader.  A decrease in predictability here can
also lead to scenarios with increased security risk (eg. if an assembly
private key is compromised, it would increase the number of scenarios where
it would lead to compromising the application / machine).  Things get even
worse when it's some 3rd party component that is doing the LoadFrom and
affecting the behavior of another component.  

Admittedly, this does make some more dynamic scenarios (like what you're
trying to do) more awkward.  I agree that (assuming you can't just set the
AppBase of your new AppDomain to the directory with the assembly) there
currently isn't an ideal way to do what you want to do.  Your evaluation of
the alternatives and their trade-offs seems accurate to me.  Note that I am
aware of at least one other customer who is "unifying the binding contexts"
using an AssemblyResolve event handler as you suggest (although it
certainly has it's downsides).

I hope this helps to clarify the motivation here,
    Rick
--------
This posting is provided "AS IS" with no warranties, and confers no rights.

--------------------
> From: kurbylogic@hotmail.com
> Newsgroups:
microsoft.public.dotnet.framework.clr,microsoft.public.dotnet.security
> Subject: Re: Assembly.Load fails after assembly preloaded using
Assembly.LoadFrom (v1.1.4322)
> Date: 4 Mar 2005 09:26:35 -0800
>
[quoted text clipped - 116 lines]
> class="StrongNameMembershipCondition"
>                                               version="1"

PublicKeyBlob="0024000004800000940000000602000000240000525341310004000001000
100E7D3DD7E620B266EA32514C042881CE1905B0CCC968DC543B20286740D3B881B3BCCE3FB6
EBC4B256DAB41512BE874B33658683CDCCF47D1A4532E60D54B7F1E6432B9700AC319FAF2B45
92D4C13D146913395E19C64BB4DF474227855952D5F5E191DDFC1ECA667B5D52C6D08480D257
9A9B1C868796227343EF44856B72ACC"/>
>                      </CodeGroup>
>                   </CodeGroup>
[quoted text clipped - 71 lines]
> > >
> > > - Kurt
Nicole Calinoiu - 07 Mar 2005 20:00 GMT
<snip>
> Okay not a "versioning nightmare" wrong words, a "deployment concern".
> The concern is essentially that the exact same version of the file must
[quoted text clipped - 4 lines]
> mistake.  It is still a disadvantage as if I must have to have 2 copies
> of the same file I'd much rather use the GAC.

Your application, your call.  Personally, I suspect that I would prefer to
use a different assembly entirely as the "director" in the second app
domain, but that's a different discussion entirely. <g>

> Create a folder c:\scriptdir
> Create a strong named assembly and place it in the directory.
[quoted text clipped - 7 lines]
> copied to this directory.  It should have recieved the FullTrust (union
> of Execute and FullTrust), however it only recieves execute permission.

You marked the parent group as final, so why are you expecting the child
group to be considered at all?  If you don't mark the URL group as final,
the assembly should be granted full trust.  Of course, under default policy,
it would have been granted full trust anyway since it's running from a local
drive.

> I was looking for a bit more than "presumably thought of" as if MS
> thinks of everything.

Sorry about that.  I'd been having a discussion elsewhere with someone who
was being rather obstinate about a particular design decision in which he
hadn't participated, and I guess my frustration about that unrelated issue
leaked through to my post...

> I don't think that the possiblility of two
> assemblies with the same strong name was the underlying issue however,
> otherwise I would think Fusion not check the GAC before probing the
> applicatonbase if a conflict were to occur then I would choose to use
> assembly deployed with the app vs the one in the gac deployed by
> whomever.

I get the impression that Microsoft folks think that the GACed assembly is
likely to be more reliable than the one deployed in whatever directory might
happen to be used by any given applicatio.  Of course, if you go the route
of using the GAC as your deployment point, your chances of using the same
assembly in both app domains does increase considerably. <g>

> I also would not have expected the security team to use the
> StrongNameMembershipCondition as a source of evidence to grant full
> trust to the system assemblies.

It's actually just a backup (presumably in folks naively meddle with the
policy) since all local code is fully trusted by default.  Personally, I
think full trust by default is even worse than the use of the
StrongNameMembershipCondition, but ymmv...

> I suspect the issue is rather subtle
> if it is an issue at all, which I suspect not.  The duplicate loading
[quoted text clipped - 8 lines]
> concidering I can use the GAC, so I guess I'll quit whining and just
> place the assembly in the GAC.

You might want to keep in mind that this is a mechanism that is primarily
meant to support remoting, where there's no reason to expect that both app
domains would be using the same source assembly.  Given this, I would tend
to see your prefered behaviour as an unsupported scenario rather than
viewing the double-loading as a bug.

> The link you provided also mentioned another source used during Load
> that even though I probably read this artical two or three times the
[quoted text clipped - 4 lines]
> idea how this store(location/path?) is set or how assemblies added to
> it?

I believe that she's referring to hosting of the CLR (e.g.:
http://msdn.microsoft.com/msdnmag/issues/01/03/clr/default.aspx).  Not
necessarily something you want to be doing just to avoid double-loading...
David Levine - 08 Mar 2005 01:33 GMT
> The LoadFrom works fine I verify that it has been loaded into the
> domain (however pointless this seems) and that it has a strong name.
[quoted text clipped - 4 lines]
> the result is that the assembly is loaded into the application domain
> twice.

I've run into the same issue before. You might be able to use codebase hints
in the app.config file
to tell the fusion layer where to look when locating assemblies.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.