サンドボックス化された AppDomain にアセンブリをロードするためのコードがいくつかあります (PermissionSet は PermissionState.None で初期化されます)。メインの AppDomain とサンドボックス化された AppDomain のアセンブリにデータをやり取りしたいと考えています。私のデータをMarshalByRefObject拡張することで、参照渡しが可能になります。問題は、これらのオブジェクトがメモリ内でハングアップすることです。できるだけ早く(ほとんどの場合数秒以内に)収集してもらいたいと思います。InitializeLifetimeServiceをオーバーライドしようとすると、ランタイム エラーが発生します。
この問題を示すサンプル コードをまとめました。私のコードは 3 つのプロジェクトに編成されています。
クラス ライブラリ: サンドボックス(これは署名する必要があります)
ファイル名: Sandbox.cs (このスタックオーバーフローの回答に基づく)
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
}
}
}
クラス ライブラリ: プラグイン
ファイル名: Plugin.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 には、信頼できないプラグインに与えるよりも多くのセキュリティが必要なようです。プラグイン ライブラリの外部で SharedData クラスを定義し、それを渡そうとすると (参照によってオブジェクトをマーシャリングしようとすると)、同様のエラーが発生します。
値によるコピーとシリアル化に頼らずにこれを機能させる方法はありますか?
編集:参考までに、シリアル化を使用することになりました。特にデータが変更され、それを返すために再度シリアル化する必要がある場所では苦痛ですが、少なくとも機能します。マーシャリングでこれを機能させることができるかどうか知りたいです。