4

我有一些用于将程序集加载到沙盒 AppDomain 的代码(PermissionSet 使用PermissionState.None初始化)。我想将数据从我的主 AppDomain 来回传递到沙盒 AppDomain 中的程序集。通过使我的数据扩展MarshalByRefObject,它可以通过引用传递。问题是这些对象然后在内存中徘徊。我希望尽快收集它们(大多数情况下在几秒钟内)。当我尝试覆盖InitializeLifetimeService时,出现运行时错误。

我整理了一些示例代码来演示这个问题。我的代码分为三个项目:

类库:沙盒(必须签名)

文件名:Sandbox.cs(基于此 stackoverflow 答案

using System;
using System.IO;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;

namespace Sandbox
{
    public class Sandbox : MarshalByRefObject
    {
        const string BaseDirectory = "Untrusted";
        const string DomainName = "Sandbox";

        Assembly assembly;

        public Sandbox()
        {
        }

        public static Sandbox Create()
        {
            var setup = new AppDomainSetup()
            {
                ApplicationBase = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, BaseDirectory),
                ApplicationName = DomainName,
                DisallowBindingRedirects = true,
                DisallowCodeDownload = true,
                DisallowPublisherPolicy = true
            };

            var permissions = new PermissionSet(PermissionState.None);
            permissions.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
            permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));

            var domain = AppDomain.CreateDomain(DomainName, null, setup, permissions,
                typeof(Sandbox).Assembly.Evidence.GetHostEvidence<StrongName>());

            return (Sandbox)Activator.CreateInstanceFrom(domain, typeof(Sandbox).Assembly.ManifestModule.FullyQualifiedName, typeof(Sandbox).FullName).Unwrap();
        }

        public void LoadAssembly(string assemblyPath)
        {
            new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, assemblyPath).Assert();
            this.assembly = Assembly.LoadFile(assemblyPath);
            CodeAccessPermission.RevertAssert();
        }

        public object CreateInstance(string typeName)
        {
            Type type = this.assembly.GetType(typeName);
            return Activator.CreateInstance(type);
        }

        public object InvokeStaticMethod(string typeName, string methodName, params object[] parameters)
        {
            Type type = this.assembly.GetType(typeName);
            return type.GetMethod(methodName).Invoke(null, parameters);
        }

        public override object InitializeLifetimeService()
        {
            return null; // sandboxes don't expire
        }
    }
}

类库:插件

文件名:插件.cs

namespace Plugins
{
    public static class Plugin
    {
        public static string DoSomething(SharedData data)
        {
            data.Set("updated", "yes");
            return data.Get("name");
        }
    }
}

文件名:SharedData.cs

using System;
using System.Collections.Generic;
using System.Runtime.Remoting.Lifetime;

namespace Plugins
{
    public class SharedData : MarshalByRefObject
    {
        Dictionary<string, string> dict = new Dictionary<string, string>();

        public override object InitializeLifetimeService()
        {
            ILease lease = (ILease)base.InitializeLifetimeService();
            if (lease.CurrentState == LeaseState.Initial)
            {
                lease.InitialLeaseTime = TimeSpan.FromMinutes(1);
                lease.RenewOnCallTime = TimeSpan.FromMinutes(1);
                lease.SponsorshipTimeout = TimeSpan.FromMinutes(1);
            }
            return lease;
        }

        public void Set(string key, string value)
        {
            this.dict[key] = value;
        }

        public string Get(string key)
        {
            if (this.dict.ContainsKey(key))
            {
                return this.dict[key];
            }
            return null;
        }

    }
}

控制台项目:示例

文件名:Program.cs

using System;
using System.IO;

namespace Example
{
    class Program
    {
        static void Main(string[] args)
        {
            // create sandbox domain
            var sandbox = Sandbox.Sandbox.Create();
            // load a "plugin" assembly into it
            string assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Plugins.dll");
            sandbox.LoadAssembly(assemblyPath);
            // create some data in the sandbox
            var data = (Plugins.SharedData)sandbox.CreateInstance("Plugins.SharedData");
            data.Set("name", "Nogwater");
            // pass some data into the plugin
            object objRet = sandbox.InvokeStaticMethod("Plugins.Plugin", "DoSomething", data);
            // get updates back from the plugin
            string updated = data.Get("updated");
            Console.WriteLine("{0} {1}", objRet, updated);
        }
    }
}

错误发生在Sandbox.CreateInstance调用this.assembly.GetType(). 我得到的信息是:

覆盖成员时违反了继承安全规则:“Plugins.SharedData.InitializeLifetimeService()”。重写方法的安全可访问性必须与被重写方法的安全可访问性相匹配。

似乎 InitializeLifetimeService 需要比我想给不受信任的插件更多的安全性。如果我在 Plugins 库之外定义 SharedData 类并尝试将其传入(一旦它尝试通过引用编组对象),我会收到类似的错误。

有什么方法可以使这项工作不必诉诸按值复制和序列化?

编辑:仅供参考,我确实最终使用了序列化。这很痛苦,特别是对于数据被更改并且我必须再次序列化才能返回它的地方,但至少它可以工作。我很想知道是否有人可以通过编组来完成这项工作。

4

0 回答 0