Download the assembled Roslyn build to the AppDomain sandbox

I have a piece of code that compiles a script using a script mechanism, and I return the assembly as an array of bytes.

Now I want to load this Assembly in the sandbox, here is what I have:

 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... } 

This is the AssemblyResolve event handler:

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

This means that when I do domain.Load(assembly) , I will get _dynamicAssembly, if I did not subscribe to this event and return it, Assembly , I get a FileNotFoundException .

The above compiles and runs, but the problem is that the code that runs in the domain assembly does not actually run in the sandbox. When I get the view method and call the factory in it and return this AppDomain.CurrentDomain.FriendlyName , the result is MyRoslynApplication.vshost.exe , which is not , the AppDomain sandbox

Am I loading my byte[] assembler incorrectly?

+6
source share
2 answers

If you want to load a type that runs in the sandbox and access it from the main AppDomain, you need to use a method like CreateInstanceFromAndUnwrap . The type must be MarshalByRefObject so that it can create a transparent proxy in the calling AppDomain application for access.

If the main AppDomain allows the build, it will be uploaded to the main AppDomain (as well as to the Sandbox AppDomain) so that you end up downloading two copies. Your main AppDomain should always remain isolated from the sandbox through proxies so that you can only access MarshalByRefObject objects and serializable objects. Please note that the type you are referencing also cannot be defined in the assembly you want to load into the sandbox; you will want to define the interfaces and possibly serializable types in the third general assembly, which will then be loaded into both the main and sandboxes of the AppDomains.


I did some additional digging, and it seems that all methods of loading the assembly into another AppDomain and creating a proxy require the assembly name to resolve. I am not sure if it is possible to load via byte [] in this case; You may need to save the assembly to disk and load it. I will dig a little more.


I think you can do it (this is untested, but seems plausible).

They should be in the "interface" assembly, available both for your main application and for the sandbox (I will call it as Service.dll):

 public interface IMyService { //.... service-specific methods you'll be using } public interface IStubLoader { Object CreateInstanceFromAndUnwrap(byte[] assemblyBytes, string typeName); } 

Next is the class in StubLoader.dll. You will not refer to this assembly directly; here you will call the first AppDomain.CreateInstanceFromAndUnwrap, specifying this as the assembly name and StubLoader as the type name.

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

Now, to use it from the main AppDomain, you do this:

 //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"); 

Unfortunately, AppDomains are not easy to use. This is due to the fact that they provide a high degree of isolation and, therefore, require proxying for use within the boundaries of AppDomain.


In response to how you could marshal a non-serializable and non-MarshalByRefObject class, here is an example of what might be in a common interface 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 } } 

Now that your original service needs to work with the session, it can instead pass ISessionWrapper, whose calls will be sorted behind the scenes so that all executable code is executed in the sandbox in a real session instance living in the sandbox.

+7
source

perhaps this could also help:

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

I also had to transfer the generated stream of assembly files to disk to get Sandbox to work. I am still struggling with strong naming and unit testing of the solution (there are more than 16 projects), however I will get back to you with a working copy.

0
source

Source: https://habr.com/ru/post/902657/


All Articles