Home All Groups Group Topic Archive Search About

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

Author
4 Mar 2005 5:55 AM
kurbylogic
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

Author
4 Mar 2005 3:16 PM
Nicole Calinoiu
<kurbylo***@hotmail.com> wrote in message
news:1109915702.864652.123600@o13g2000cwo.googlegroups.com...
<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
> 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)

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
> 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).

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:
> 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 ?

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...


Show quoteHide quote
> (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
>
Author
4 Mar 2005 5:26 PM
kurbylogic
Nicole Calinoiu wrote:
> <kurbylo***@hotmail.com> wrote in message
> news:1109915702.864652.123600@o13g2000cwo.googlegroups.com...
> <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
Show quoteHide quote
> 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
> > 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)
>
> 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.
>
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.

Show quoteHide quote
>
> > 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).
>
> 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>



Show quoteHide quote
>
>
> > 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 ?
>
> 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...
>

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?

Show quoteHide quote
>
> > (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
> >
Author
7 Mar 2005 8:00 PM
Nicole Calinoiu
<kurbylo***@hotmail.com> wrote in message
news:1109957195.859924.19410@g14g2000cwa.googlegroups.com...
<snip>
> 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.

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.
> 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.

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
> 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.

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
> 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?

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...
Author
8 Mar 2005 1:33 AM
David Levine
>
>
> 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.

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.