/// <summary> /// Destroy the AppDomain. /// </summary> private void DestroyDomain(bool disposing) { Console.WriteLine("Unloading AppDomain '" + mAppDomain.FriendlyName + "', id=" + mAppDomain.Id + ", disposing=" + disposing); if (mPluginLoader != null) { mPluginLoader.Dispose(); mPluginLoader = null; } if (mAppDomain != null) { // We can't simply invoke AppDomain.Unload() from a finalizer. // The unload is handled by a thread that won't run at the // same time as the finalizer thread, so if we got here // through finalization we will deadlock. Fortunately the // runtime sees the situation and throws an exception out of // Unload(). // // If we don't have a finalizer, and we forget to make an // explicit cleanup call, the AppDomain will stick around. // // So we use a workaround from // https://stackoverflow.com/q/4064749/294248 and invoke it // asynchronously. if (disposing) { AppDomain.Unload(mAppDomain); } else { new Action <AppDomain>(AppDomain.Unload).BeginInvoke( mAppDomain, null, null); } mAppDomain = null; } }
/// <summary> /// Creates a new AppDomain. If our plugin is just executing /// pre-compiled code we can lock the permissions down, but if /// it needs to dynamically compile code we need to open things up. /// </summary> /// <param name="appDomainName">The "friendly" name.</param> /// <param name="cap">Permission set.</param> public void CreateDomain(string appDomainName, DomainCapabilities cap) { if (mAppDomain != null) { throw new Exception("Domain already created"); } PermissionSet permSet; if (LEASE_TEST) { // Use this when overriding InitializeLifetimeService in // PluginLoader. See the comments there for more details. permSet = new PermissionSet(PermissionState.Unrestricted); } else if (cap == DomainCapabilities.ALLOW_DYNAMIC) { // Set the permissions in a way that allows the plugin to // run the CSharpCodeProvider. It looks like the compiler // requires "FullTrust", which limits our options here. // TODO: see if we can narrow this down. permSet = new PermissionSet(PermissionState.Unrestricted); } else { // Start with everything disabled. permSet = new PermissionSet(PermissionState.None); // Allow code execution. permSet.AddPermission(new SecurityPermission( SecurityPermissionFlag.Execution)); // Allow changes to Remoting stuff. Without this, we can't // register our ISponsor. TODO: figure out if there's a better way. permSet.AddPermission(new SecurityPermission( SecurityPermissionFlag.RemotingConfiguration)); // Allow read-only file access, but only in the plugin directory. // This is necessary to allow PluginLoader to load the assembly. FileIOPermission fp = new FileIOPermission( FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, mPluginPath); permSet.AddPermission(fp); } // Configure the AppDomain. Setting the ApplicationBase // property is apparently very important, as it mitigates the // risk of certain exploits from untrusted plugin code. AppDomainSetup adSetup = new AppDomainSetup(); adSetup.ApplicationBase = mPluginPath; //string hostAppBase = // AppDomain.CurrentDomain.SetupInformation.ApplicationBase; // Create the AppDomain. We're not passing in Evidence or // StrongName[]. Not sure that's important. mAppDomain = AppDomain.CreateDomain("Plugin AppDomain", null, adSetup, permSet); Console.WriteLine("Created AppDomain '" + appDomainName + "', id=" + mAppDomain.Id); // Create a PluginLoader in the remote AppDomain. The local // object is actually a proxy. PluginLoader pl = (PluginLoader)mAppDomain.CreateInstanceAndUnwrap( typeof(PluginLoader).Assembly.FullName, typeof(PluginLoader).FullName); // Wrap it so it doesn't disappear on us. mPluginLoader = new Sponsor <PluginLoader>(pl); }