Question

I have a full trust .Net 4.5 application which creates a partial trust AppDomain. I'm trying to communicate with this domain via a WCF named pipe.

I've successfully created the service and connected to it. However, when I try to call any of the service's methods I get this not terribly useful error:

An unhandled exception of type 'System.Security.SecurityException' occurred in System.ServiceModel.Internals.dll

Additional information: Request failed.

>   System.ServiceModel.Internals.dll!System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(uint error, uint bytesRead, System.Threading.NativeOverlapped* nativeOverlapped) + 0x75 bytes 
    mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) + 0x6e bytes    
    [Native to Managed Transition]  
    [Appdomain Transition]  
    [Native to Managed Transition]  
    kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes    
    ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes   
    ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes    

And that's all, no InnerException.

As the callstack suggests the exception is thrown from the restricted AppDomain. Unsurprisingly, if I create the domain with full trust the problem goes away.

The assembly containing the service is fully trusted in the restricted AppDomain (see code below). It could create the pipe, so why is it choking when I try to use it? Is this a question of coaxing it into asserting its permissions when receiving service requests?

Full-trust code:

var evidence = new Evidence(null, new EvidenceBase[] { new Zone(SecurityZone.Internet) });

var setup = new AppDomainSetup() { ApplicationBase = InstallDirectory.FullName };
setup.PrivateBinPath = String.Join(Path.PathSeparator.ToString(), "Episodes", "bin");

var permissions = new PermissionSet(null);
permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

GameDomain = AppDomain.CreateDomain("Bulletin.Game", evidence, setup, permissions, 
    typeof(Shared.IBulletinGame).Assembly.Evidence.GetHostEvidence<StrongName>(),
    typeof(Bulletin.Game).Assembly.Evidence.GetHostEvidence<StrongName>()
    );

Activator.CreateInstanceFrom(GameDomain, Path.Combine(InstallDirectory.FullName, "bin", "Bulletin.dll"), "Bulletin.Game");

GameService = new ChannelFactory<Shared.IBulletinGame>(
    new NetNamedPipeBinding(),
    new EndpointAddress("net.pipe://localhost/Bulletin/Game")
    ).CreateChannel();

GameService.Test(); // throws exception

Partial-trust Code:

[assembly: AllowPartiallyTrustedCallers()]

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class Game : Shared.IBulletinGame
{
    // The constructor completes successfully.
    [SecurityCritical]
    public Game()
    {
        Host = new ServiceHost(this);
        Host.AddServiceEndpoint(typeof(Shared.IBulletinGame), new NetNamedPipeBinding(), "net.pipe://localhost/Bulletin/Game");
        Host.Open();
    }

    // Control doesn't even reach this method when I try to remotely call it,
    // unless I give the AppDomain full trust.
    public void Test()
    {
        System.Diagnostics.Debugger.Break();
    }
}
Was it helpful?

Solution

The answer for me was to ditch WCF and use good-old-fashioned MarshalByRefObject to implement my interface. Everything now works perfectly.

Despite the dire warnings across the MSDN docs about remoting being obsolete, it seems that it's still a perfectly good choice (and sometimes the only choice...) for intra-process communication. Judging from the amount it's used in the framework it's not going away any time soon, either.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top