Загрузка скомпилированной сборки Roslyn в песочницу AppDomain - PullRequest
7 голосов
/ 29 ноября 2011

У меня есть фрагмент кода, который компилирует скрипт с обработчиком сценариев, и я получаю сборку как байтовый массив.

Теперь я хочу загрузить Assembly в Песочницу, эточто у меня есть:

Assembly _dynamicAssembly;
ScriptEngine _engine;
Session _session;

public string Execute(string code)
{
    // Setup sandbox
    var e = new Evidence();
    e.AddHostEvidence(new Zone(SecurityZone.Internet));
    var ps = SecurityManager.GetStandardSandbox(e);
    var setup = new AppDomainSetup 
                         { ApplicationBase = Environment.CurrentDirectory };
    var domain = 
        AppDomain.CreateDomain("Sandbox", 
                               AppDomain.CurrentDomain.Evidence, setup, ps);
    AppDomain.CurrentDomain.AssemblyResolve += DomainAssemblyResolve;

    // Process code
    var submission = _engine.CompileSubmission<object>(code, _session);
    submission.Compilation.Emit(memoryStream);
    var assembly = memoryStream.ToArray();

    _dynamicAssembly = Assembly.Load(assembly);

    var loaded = domain.Load(assembly);

    // Rest of the code...
}

Это обработчик событий для AssemblyResolve:

Assembly DomainAssemblyResolve(object sender, ResolveEventArgs args)
{
    return _dynamicAssembly;
}

Это означает, что когда я сделаю domain.Load(assembly), я получу _dynamicAssembly, если я наденуне подписаться на это событие и вернуть Assembly, я получаю FileNotFoundException.

Выше компилируется и запускается, но проблема в том, что код, который выполняется в сборке домена, на самом деле не выполняетсяв песочнице.Когда я получаю метод представления и вызываю фабрику в нем и возвращаю AppDomain.CurrentDomain.FriendlyName, результат будет: MyRoslynApplication.vshost.exe, который не песочница AppDomain

Я загружаю свойbyte[] - неправильная сборка?

Ответы [ 2 ]

9 голосов
/ 29 ноября 2011

Если вы хотите загрузить тип, который запускается в изолированной программной среде, и получить к нему доступ из основного домена приложения, вам необходимо использовать метод, подобный CreateInstanceFromAndUnwrap . Тип должен быть MarshalByRefObject, чтобы он мог создать прозрачный прокси в вызывающем AppDomain для доступа.

Если основной домен приложений разрешает сборку, он будет загружен в основной домен приложений (а также в домен приложения «песочница»), чтобы в итоге были загружены две копии. Ваш основной AppDomain должен всегда оставаться изолированным от песочницы через прокси, чтобы можно было получить доступ только к MarshalByRefObject и сериализуемым объектам. Обратите внимание, что тип, на который вы ссылаетесь, также не может быть определен в сборке, которую вы хотите загрузить в песочницу; вы захотите определить интерфейсы и, возможно, сериализуемые типы в третьей общей сборке, которая затем будет загружена как в основной, так и в песочницу домены приложений.


Я провел дополнительное копание, и похоже, что все методы для загрузки сборки в другой домен приложений и генерации прокси-сервера требуют разрешения имени сборки. Я не уверен, что в этом случае можно загрузить через byte []; вам может понадобиться сохранить сборку на диск и загрузить ее. Я буду копать немного больше.


Я думаю, что вы можете сделать это (это не проверено, но кажется правдоподобным).

Они должны быть в сборке «интерфейс», доступной как для вашего основного приложения, так и для песочницы (я буду называть это Services.dll):

public interface IMyService
{
    //.... service-specific methods you'll be using
}

public interface IStubLoader
{
    Object CreateInstanceFromAndUnwrap(byte[] assemblyBytes, string typeName);
}

Следующий класс в StubLoader.dll. Вы не будете ссылаться на эту сборку напрямую; здесь вы будете вызывать первый AppDomain.CreateInstanceFromAndUnwrap, предоставляя его в качестве имени сборки и StubLoader в качестве имени типа.

public sealed class StubLoader: MarshalByRefObject, IStubLoader
    {
        public object CreateInstanceFromAndUnwrap(byte[] assemblyBytes, string typeName)
        {
            var assembly = Assembly.Load(assemblyBytes);
            return assembly.CreateInstance(typeName);
        }
    }

Теперь, чтобы использовать его с вашего основного домена приложений, вы делаете это:

//Create transparent proxy for the stub loader, which will live in the sandbox
var stubLoader = (IStubLoader)sandboxDomain.CreateInstanceFromAndUnwrap("Stubloader.dll", "StubLoader");

//Have the stub loader marshal a proxy to a dynamically loaded assembly (via byte[]) where MyService is the type name implementing MarshalByRefObject and IMyService
var myService = (IMyService)stubLoader.CreateInstanceFromAndUnwrap(assemblyBytes, "MyService");

К сожалению, домены приложений не просты в использовании. Это связано с тем, что они обеспечивают высокую степень изоляции и, следовательно, требуют проксирования, чтобы разрешить использование через границы домена приложений.


В ответ на то, как вы могли бы упорядочить не сериализуемый и не-MarshalByRefObject класс, вот примерный пример того, что может быть в DLL общего интерфейса:

public interface ISessionWrapper
{
    void DoSomethingWithSession();
}

public sealed class SessionWrapper : MarshalByRefObject, ISessionWrapper
{
    private readonly Session _session;

    public SessionWrapper(Session session)
    {
        _session = session;
    }

    public void DoSomethingWithSession()
    {
        //Do something with the wrapped session...
        //This executes inside the sandbox, even though it can be called (via proxy) from outside the sandbox
    }
}

Теперь везде, где ваш исходный сервис должен был работать с Session, он может вместо этого передавать ISessionWrapper, чьи вызовы будут маршалироваться за кулисами, чтобы весь реальный код выполнялся в песочнице реального экземпляра Session, живущего в песочнице.

0 голосов
/ 13 июня 2017

, вероятно, это также может помочь:

https://docs.microsoft.com/en-us/dotnet/framework/misc/how-to-run-partially-trusted-code-in-a-sandbox

Кроме того, мне пришлось сохранить поток созданного файла сборки на диске, чтобы заставить Песочницу работать. Я все еще борюсь с сильным наименованием и модульным тестированием решения (существует более 16 проектов), однако я вернусь к вам с рабочей копией.

...