Git Product home page Git Product logo

dotnetisolator's Introduction

DotNetIsolator [EXPERIMENTAL]

Lets your .NET code run other .NET code in an isolated environment easily.

Basic concept:

  1. Create as many IsolatedRuntime instances as you like.
    • Each one is actually a WebAssembly sandbox built with dotnet-wasi-sdk and running on Wasmtime.
    • Each one has a completely separate memory space and no direct access to the host machine's disk/network/OS/etc.
  2. Make .NET calls into IsolatedRuntime instances.
    • Either create IsolatedObject instances within those runtimes then invoke their methods
    • ... or just call a lambda method directly
    • You can pass/capture/return arbitrary values across the boundary, and they will be serialized automatically via Messagepack

This is experimental and unsupported. It may or may not be developed any further. There will definitely be functional gaps. There are no guarantees about security.

Getting started

First, install the package:

dotnet add package DotNetIsolator --prerelease

Now try this code:

// Set up an isolated runtime
using var host = new IsolatedRuntimeHost().WithBinDirectoryAssemblyLoader();
using var runtime = new IsolatedRuntime(host);

// Output: I'm running on X64
Console.WriteLine($"I'm running on {RuntimeInformation.OSArchitecture}");

runtime.Invoke(() =>
{
    // Output: I'm running on Wasm
    Console.WriteLine($"I'm running on {RuntimeInformation.OSArchitecture}");
});

Or, for a more involved example:

// Set up the runtime
using var host = new IsolatedRuntimeHost().WithBinDirectoryAssemblyLoader();
using var isolatedRuntime = new IsolatedRuntime(host);

// Evaluate the environment info in both the host runtime and the isolated one
var realInfo = GetEnvironmentInfo();
var isolatedInfo = isolatedRuntime.Invoke(GetEnvironmentInfo);
Console.WriteLine($"Real env: {realInfo}");
Console.WriteLine($"Isolated env: {isolatedInfo}");

static EnvInfo GetEnvironmentInfo()
{
    var sysRoot = Environment.GetEnvironmentVariable("SystemRoot") ?? "(Not set)";
    return new EnvInfo(
        Environment.GetEnvironmentVariables().Count,
        $"SystemRoot={sysRoot}");
}

// Demonstrates that you can return arbitrarily-typed objects
record EnvInfo(int NumEnvVars, string ExampleEnvVar)
{
    public override string ToString() => $"{NumEnvVars} entries, including {ExampleEnvVar}";
}

Output, which will differ slighly on macOS/Linux:

Real env: 64 entries, including SystemRoot=C:\WINDOWS
Isolated env: 0 entries, including SystemRoot=(Not set)

API guides

Creating an IsolatedRuntimeHost

First you must create an IsolatedRuntimeHost. These host objects can be shared across users, since they don't hold any per-runtime state. If you're using DI, you could register it as a singleton.

The purpose of this is to:

  • Start up Wasmtime. This takes ~400ms so you only want to do it once and not every time you instantiate an IsolatedRuntime
  • Configure assembly loading

Configuring assembly loading

The isolated .NET runtime instances need to load .NET assemblies in order to do anything useful. This package includes a WebAssembly-specific .NET base class library (BCL) for low-level .NET types such as int, string, Dictionary<T, U>, etc. Isolated runtimes always have permission to load these prebundled BCL assemblies.

You will almost always also want to load application-specific assemblies into your isolated runtimes, so that you can run your own code. The easiest way to configure assembly loading is to use WithBinDirectoryAssemblyLoader:

using var host = new IsolatedRuntimeHost()
    .WithBinDirectoryAssemblyLoader();

This grants permission to load .NET assemblies from your host application's bin directory. This makes it possible to:

  • Invoke lambda methods (since the code for those methods is inside the DLLs in your bin directory)
  • Instantiate objects of arbitrary types inside isolated runtimes
  • Pass/return types declared in your own application or other packages that you reference

Note that WithBinDirectoryAssemblyLoader does not allow the guest code to escape from its sandbox. Even though it can load assemblies from the host application, it can only use them within its sandbox.

If you want to impose stricter controls over assembly loading, then instead of WithBinDirectoryAssemblyLoader, you can use WithAssemblyLoader:

using var host = new IsolatedRuntimeHost()
    .WithAssemblyLoader(assemblyName =>
    {
        switch (assemblyName)
        {
            case "MyAssembly":
                return File.ReadAllBytes("some/path/to/MyAssembly.dll");
        }

        return null; // Unknown assembly. Maybe another loader will find it.
    });

You can register as many assembly loaders as you wish.

Creating an IsolatedRuntime

Once you have an IsolatedRuntimeHost, it's trivial to create as many runtimes as you like:

using var runtime1 = new IsolatedRuntime(host);
using var runtime2 = new IsolatedRuntime(host);

Currently, each runtime takes ~8ms to instantiate.

Calling lambdas

Once you have an IsolatedRuntime, you can dispatch calls into them by using Invoke and lambda methods:

var person1 = new Person(3);
var person2 = new Person(9);

var sumOfAges = runtime.Invoke(() =>
{
    // This runs inside the isolated runtime.
    // Notice that we can use closure-captured values/objects too.
    // They will be serialized in using MessagePack.
    return person1.Age + person2.Age;
});

// Output: The isolated runtime calculated the result: 12
Console.WriteLine($"The isolated runtime calculated the result: {sumOfAges}");

record Person(int Age);

Note that if the lambda mutates the value of captured objects or static fields, those changes will only take effect inside the isolated runtime. It cannot affect objects in the host runtime, since there is no direct sharing of memory:

public static int StaticCounter = 0;

private static void Main(string[] args)
{
    using var host = new IsolatedRuntimeHost().WithBinDirectoryAssemblyLoader();
    using var runtime = new IsolatedRuntime(host);

    int localValue = 0;

    runtime.Invoke(() =>
    {
        StaticCounter++;
        localValue++;
        Console.WriteLine($"(isolated) StaticCounter={StaticCounter}, localValue={localValue}");
    });

    Console.WriteLine($"(host)     StaticCounter={StaticCounter}, localValue={localValue}");

    // The output is:
    // (isolated) StaticCounter=1, localValue=1
    // (host)     StaticCounter=0, localValue=0
}

Instantiating isolated objects

Using lambdas is convenient, but only works if the isolated runtime is allowed to load the assemblies from your bin directory (because that's where the code is).

As an alternative, you can manually instantiate isolated objects inside the isolated runtime, then call methods on them. For example:

// Generic API
IsolatedObject obj1 = runtime.CreateObject<Person>();

// String-based API (useful if the host app doesn't reference the assembly containing the type)
IsolatedObject obj2 = runtime.CreateObject("MyAssembly", "MyNamespace", "Person");

CreateObject requires the object type to have a parameterless constructor. Support for constructor parameters isn't yet implemented (but would be simple to do).

Calling methods on isolated objects

You can use Invoke or InvokeVoid to find a method and invoke it in a single step. For example, if the object has a method void DoSomething(int value):

isolatedObject.InvokeVoid("DoSomething", 123);

If it has a return value, you must specify the type as a generic parameter. For example, if the object has a method TimeSpan GetAge(bool includeGestation):

TimeSpan result = isolatedObject.Invoke<bool, TimeSpan>("GetAge", /* includeGestation */ true);

Alternatively you can capture a reference to an IsolateMethod so you can invoke it later. This is similar to a MethodInfo so it isn't bound to a specific target object.

var getAgeMethod = isolatedObject.FindMethod("GetAge");

// ... then later:
var age = getAgeMethod.Invoke<bool, TimeSpan>(isolatedObject, /* includeGestation */ true);

You can also find methods without having to instantiate any objects first:

var getAgeMethod = isolatedRuntime.GetMethod(typeof(Person), "GetAge");

Calling the host from the guest

The host may register named callbacks that can be invoked from guest code. For example:

using var runtime = new IsolatedRuntime(host);
runtime.RegisterCallback("addTwoNumbers", (int a, int b) => a + b);
runtime.RegisterCallback("getHostTime", () => DateTime.Now);

To call these from guest code, have the guest code's project reference the DotNetIsolator.Guest package, and then use DotNetIsolatorHost.Invoke, e.g.:

var sum = DotNetIsolatorHost.Invoke<int>("addTwoNumbers", 123, 456);
var hostTime = DotNetIsolatorHost.Invoke<DateTime>("getHostTime");

Note that if you're calling via a lambda, then the guest code is in the same assembly as the host code, so in that case you need the host project to reference the DotNetIsolator.Guest package.

Security notes

If you want to rely on this isolation as a critical security boundary in your application, you should bear in mind that:

  • This is an experimental prerelease package. No security review has taken place. There could be defects that allow guest code to cause unintentional effects on the host.
  • WebAssembly itself defines an extremely well-proven sandbox (browsers run untrusted WebAssembly modules from any website, and have done so for years with a solid track record), but:
    • Wasmtime is a different implementation than what runs inside your browser. Learn more at Security and Correctness in Wasmtime.
    • The security model for WebAssembly doesn't directly address side-channel attacks (e.g., spectre). There are robust solutions for this but it's outside the scope of this repo.

In summary:

  • If you used this as one layer in a multi-layered security model, it would be a pretty good layer! But nobody's promising it's bulletproof on its own.
  • If you're not running potentially hostile code, and are merely using this to manage the isolation of your own code, most of the above considerations don't apply.

Support and feedback

This is completely unsupported. There are no promises that this will be developed any further. It is published only to help people explore what they could do with this sort of capability.

You are free to report issues but please don't assume you'll get any response, much less a fix.

dotnetisolator's People

Contributors

stevesandersonms avatar teo-tsirpanis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dotnetisolator's Issues

MessagePackDynamicObjectResolverException from IsolatedRuntime.Invoke

๐Ÿ‘‹ Hello,

I'm trying to recreate the Minimal API sample from your recent YouTube video (great video btw), but I seem to get a MessagePack serialization issue with the latest version of DotNetIsolator (0.1.0-preview.10032). The error details are below.

I have the following environment:

  • macOS
  • .NET 8.0.100-preview.4.23260.5
  • DotNetIsolator 0.1.0-preview.10032
MessagePack.Internal.MessagePackDynamicObjectResolverException: can't find matched constructor. type:DotNetIsolator.IsolatedRuntime
         at MessagePack.Internal.ObjectSerializationInfo.CreateOrNull(Type type, Boolean forceStringKey, Boolean contractless, Boolean allowPrivate, Boolean dynamicMethod)
         at MessagePack.Internal.DynamicObjectTypeBuilder.BuildFormatterToDynamicMethod(Type type, Boolean forceStringKey, Boolean contractless, Boolean allowPrivate)
         at MessagePack.Resolvers.DynamicContractlessObjectResolverAllowPrivate.FormatterCache`1..cctor()
      --- End of stack trace from previous location ---
         at MessagePack.FormatterResolverExtensions.Throw(TypeInitializationException ex)
         at Serialize(Byte[][], Object[], MessagePackWriter&, <>c__DisplayClass0_0, MessagePackSerializerOptions)
         at MessagePack.Internal.AnonymousSerializableFormatter`1.Serialize(MessagePackWriter& writer, T value, MessagePackSerializerOptions options)
         at MessagePack.Formatters.TypelessFormatter.Serialize(MessagePackWriter& writer, Object value, MessagePackSerializerOptions options)
         at MessagePack.MessagePackSerializer.Serialize[T](MessagePackWriter& writer, T value, MessagePackSerializerOptions options)
         --- End of inner exception stack trace ---
         at MessagePack.MessagePackSerializer.Serialize[T](MessagePackWriter& writer, T value, MessagePackSerializerOptions options)
         at MessagePack.MessagePackSerializer.Serialize[T](T value, MessagePackSerializerOptions options, CancellationToken cancellationToken)
         at MessagePack.MessagePackSerializer.Typeless.Serialize(Object obj, MessagePackSerializerOptions options, CancellationToken cancellationToken)
         at DotNetIsolator.IsolatedRuntime.CopyObject[T](T value)
         at DotNetIsolator.IsolatedRuntime.Invoke[TRes](Func`1 value)
         at Program.<>c__DisplayClass0_0.<<Main>$>b__0() in /Users/khalidabuhakmeh/RiderProjects/HelloWasi/WebApplication1/Program.cs:line 15
         at lambda_method1(Closure, Object, HttpContext)
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

Simple example of taking dynamic code for Blazor Server app

Steve, this is so exciting, thank you so much!

I don't think Steve has released the Blazor spreadsheet demo yet, but I had a play myself, and if anyone wants to quickly try this, just start with the default Blazor Server template (.NET 7) then make these changes:

Add these nuget packages in csproj:

  <ItemGroup>
    <PackageReference Include="DotNetIsolator" Version="0.1.0-preview.10024" />
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0-1.final" />
  </ItemGroup>

In Program.cs register the following service:

builder.Services.AddSingleton<CompilerService>();

Here is the code for the above CompilerService service using Roslyn:

using DotNetIsolator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;

public class CompilerService
{
    static CSharpCompilationOptions options = new(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: Microsoft.CodeAnalysis.OptimizationLevel.Release);

    static List<PortableExecutableReference> references = AppDomain.CurrentDomain.GetAssemblies()
      .Where(asm => !asm.IsDynamic && !string.IsNullOrEmpty(asm.Location))
      .Select(asm => MetadataReference.CreateFromFile(asm.Location))
      //.Concat(new[] {MetadataReference.CreateFromFile(Assembly.Load(new AssemblyName("Microsoft.CSharp")).Location) }) // Dynamic support
      .ToList();

    // We will put generated assemblies here
    static Dictionary<string, byte[]> assemblies = new();

    static IsolatedRuntimeHost host = new IsolatedRuntimeHost()
    .WithBinDirectoryAssemblyLoader()
    .WithAssemblyLoader(name => assemblies.ContainsKey(name) ? assemblies[name] : null);

    public string CompileAndRun(string code)
    {
        var parsedCode = CSharpSyntaxTree.ParseText(SourceText.From($$"""
            using System;
            using System.IO;
            using System.Linq;
            using System.Text.Json;
            using System.Collections.Generic;
            using static System.Console;
            using static System.Math;
            using static System.Text.Json.JsonSerializer;

            public class MyLib
            {
                public static string RunCode()
                {
                    var writer = new StringWriter();
                    Console.SetOut(writer);
                    Console.SetError(writer);
                    {{code}}
                    Console.Out.Flush();
                    return writer.ToString();
                }
            }
            """));

        var assemblyName = Guid.NewGuid().ToString();
        using var templateAssemblyStream = new MemoryStream();
        using var templatePdbStream = new MemoryStream();

        var compilation = CSharpCompilation.Create(assemblyName, new SyntaxTree[] { parsedCode }, references, options);
        var compilationResult = compilation.Emit(templateAssemblyStream, templatePdbStream, options: new(debugInformationFormat: DebugInformationFormat.Pdb));
        if (!compilationResult.Success)
        {
            return string.Join("\n", compilationResult.Diagnostics.Select(i => i.ToString()));
        }
        else
        {
            assemblies.Add(assemblyName, templateAssemblyStream.ToArray());
        }

        var runtime = new IsolatedRuntime(host);

        // Now we will invoke the method
        var method = runtime.GetMethod(assemblyName, null, null, "MyLib", "RunCode");
        return method.Invoke<string>(null);
    }
}

Then in the Blazor Server app's Index.razor file, replace content with this code:

@page "/"
@inject CompilerService cs

<h1>Compiler Service</h1>

<textarea @bind="source" @bind:after="Compile" @bind:event="oninput" style="width:100%" rows="10"></textarea> 

<pre style="white-space:pre-wrap">
    @output
</pre>

@code {
    string source = "// var num = 10; Console.WriteLine(\"Number was: \" + num);";
    string output = "Type some code to compile and run.";

    void Compile()
    {
        try
        {
            output = cs.CompileAndRun(source);
        }
        catch (Exception ex)
        {
            output = ex.Message;
        }
    }
}

Now you can type code like this:

var name = "David";
Console.WriteLine("Hello " + name);

It recompiles with every keystroke. Note I am currently caching the old assemblies generated in a list, so this is just a demo, but lots of fun!

Is support for .NET Framework possible?

Hello,

I like the idea of this project. The thing that came to my mind right away when I seen the concept is a matter that I deal with very often in my work. I have to use legacy APIs to communicate with some external systems that are in form of .NET Framework assemblies. So far I usually solved it by running the .NET Framework API in a separate process and communicated via something like IPC etc. I would really love if it would be possible to get rid of separate processes and just run isolated .NET Framework assembly inside .NET Core process using this. Is this even possible or is it strictly for .NET Core code?

PS. Great work with this whole idea.

Question: DotNetWasmRunner Possibility

Hey @SteveSandersonMS, You are a brilliant man and I admire all you work.
Science you did all your research around WASM and DotNet.

There is a common well known issue with dotnet published applications that I was trying to solve.

The Issue

The published dotnet binaries are very easy to decompile using any free de-compiletion tools.
and it can be easily reveres engineered out of the box if there no protection was applied.
After watching your videos I got an idea.

The Solution

Dotnet core EXE acts like a bootstrap that runs an external .DLL files containing your applications code.
Why not convert that code to a WASM file. (If Possible)
For example publish as .WASM.
Then we build a generic simple DotNetWasmRunner. that run the .WASM file that contains your Dotnet code.
I am pretty sure if this was possible then it will apply a very simple and extra layer of protection to published binaries.

Help needed

I have no idea about its limitations or performance impact because I did not do much research in this area.
but I assume you might have an idea.
I got this idea when I first saw your WASI video but did not know when or where to ask you but after watching the DotNetIsolator videos I decided to just ask you here.

cant resolve internal call to "System.Threading.ThreadPool::QueueCallback"

Full disclosure I 100% know that what I'm trying to do is bananas, especially given the infancy of this project and do not expect any assistance.

However, I figured I'd post it anyway in the offchance that this is known issue related to wasm's threading capabilities or perhaps something really stupid that I'm overlooking,

Code

using DotNetIsolator;
using System.Management.Automation;
using System.Runtime.InteropServices;

// Set up an isolated runtime
using var host = new IsolatedRuntimeHost().WithBinDirectoryAssemblyLoader();
using var runtime = new IsolatedRuntime(host);

// Output: I'm running on X64
var action = () =>
{
    // Output: I'm running on Wasm
    Console.WriteLine($"I'm running on {RuntimeInformation.OSArchitecture}");
    var result = PowerShell.Create().AddScript("'Hello, World'").Invoke();
    foreach (var item in result)
    {
        Console.WriteLine(item.ToString());
    }
    Thread.Sleep(5000);
};
action();
runtime.Invoke(action);

Console Output

I'm running on X64
Hello, World
I'm running on Wasm
[wasm_trace_logger] cant resolve internal call to "System.Threading.ThreadPool::QueueCallback" (tested without signature also)

Your mono runtime and class libraries are out of sync.
The out of sync library is: System.Private.CoreLib.dll

When you update one from git you need to update, compile and install
the other too.
Do not report this as a bug unless you're sure you have updated correctly:
you probably have a broken mono install.
If you see other errors or faults after this message they are probably related
and you need to fix your mono install first.

Exception details

DotNetIsolator.IsolatedException
  HResult=0x80131500
  Message=System.TypeInitializationException: TypeInitialization_Type, Microsoft.PowerShell.Telemetry.ApplicationInsightsTelemetry
 ---> System.TypeInitializationException: TypeInitialization_Type, Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration
 ---> System.TypeInitializationException: TypeInitialization_Type, Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnostics.SelfDiagnosticsInitializer
 ---> System.Threading.Tasks.TaskSchedulerException: TaskSchedulerException_ctor_DefaultMessage
 ---> System.MissingMethodException:  assembly:<unknown assembly> type:<unknown type> member:(null)
   at System.Threading.ThreadPool.RequestWorkerThread()
   at System.Threading.ThreadPoolWorkQueue.Enqueue(Object callback, Boolean forceGlobal)
   at System.Threading.ThreadPool.UnsafeQueueUserWorkItemInternal(Object callBack, Boolean preferLocal)
   at System.Threading.Tasks.ThreadPoolTaskScheduler.QueueTask(Task task)
   at System.Threading.Tasks.TaskScheduler.InternalQueueTask(Task task)
   at System.Threading.Tasks.Task.ScheduleAndStart(Boolean needsProtection)
   Exception_EndOfInnerExceptionStack
   at System.Threading.Tasks.Task.ScheduleAndStart(Boolean needsProtection)
   at System.Threading.Tasks.Task`1[[System.Threading.Tasks.Task, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].StartNew(Task parent, Func`1 function, CancellationToken cancellationToken, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
   at System.Threading.Tasks.TaskFactory`1[[System.Threading.Tasks.Task, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].StartNew(Func`1 function, CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler)
   at System.Threading.Tasks.Task.Run(Func`1 function, CancellationToken cancellationToken)
   at Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnostics.SelfDiagnosticsConfigRefresher..ctor()
   at Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnostics.SelfDiagnosticsInitializer..ctor()
   at Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing.SelfDiagnostics.SelfDiagnosticsInitializer..cctor()
   Exception_EndOfInnerExceptionStack
   at Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration..cctor()
   Exception_EndOfInnerExceptionStack
   at Microsoft.PowerShell.Telemetry.ApplicationInsightsTelemetry..cctor()
   Exception_EndOfInnerExceptionStack
   at System.Management.Automation.PowerShell..ctor(PSCommand command, Collection`1 extraCommands, Object rsConnection)
   at System.Management.Automation.PowerShell.Create()
   at Program.<>c.<<Main>$>b__0_0()
  Source=DotNetIsolator
  StackTrace:
   at DotNetIsolator.IsolatedRuntime.InvokeDotNetMethod[TRes](Int32 monoMethodPtr, IsolatedObject instance, ReadOnlySpan`1 argAddresses)
   at DotNetIsolator.IsolatedMethod.Invoke[TRes](IsolatedObject instance)
   at DotNetIsolator.IsolatedMethod.InvokeVoid(IsolatedObject instance)
   at DotNetIsolator.IsolatedRuntime.Invoke(Action value)
   at Program.<Main>$(String[] args) in C:\Users\DKattan\source\repos\TestPowerShell73Dependency2\TestPowerShell73Dependency2\Program.cs:line 23

.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <PropertyGroup>

      <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
  </PropertyGroup>
  
  <ItemGroup>
    <PackageReference Include="DotNetIsolator" Version="0.1.0-preview.10024" />
    <PackageReference Include="Microsoft.PowerShell.Commands.Diagnostics" Version="7.3.3" />
    <PackageReference Include="Microsoft.PowerShell.Commands.Management" Version="7.3.3" />
    <PackageReference Include="Microsoft.PowerShell.Commands.Utility" Version="7.3.3" />
    <PackageReference Include="Microsoft.PowerShell.ConsoleHost" Version="7.3.3" />
    <PackageReference Include="Microsoft.WSMan.Management" Version="7.3.3" />
    <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="8.0.0-preview.2.23128.3" />
    <PackageReference Include="System.Management.Automation" Version="7.3.3">
      <PrivateAssets>contentfiles;analyzers</PrivateAssets>
      <GeneratePathProperty>True</GeneratePathProperty>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <ModuleFiles Include="$(TargetDir)runtimes\win\**\*.dll" />
  </ItemGroup>
  <Target Name="PublishModule" AfterTargets="PostBuildEvent">
    <Copy SourceFiles="@(ModuleFiles)" DestinationFolder="$(TargetDir)" />
  </Target>
</Project>

global.json

{
  "sdk": {
    "version": "7.0.200"
  }
}

The PowerShell related nuget packages are also using .net 7.0.2

I tried upgrading to .net8 with the PowerShell 7.4 preview but there is a known issue with the depenencies right now preventing the usage of Microsoft.PowerShell.Commands.Utility from being imported (it is referencing an unreleased nuget package).

Too many invocations cause WasmtimeException "sgen-stw.c:77, condition ... not met"

Setup

I wanted to try out the limits of DotNetIsolator and changed the ConsoleSample.Program.cs file to have call runtime.Invoke(...) many more times (because you don't know how often a 3rd party code would call Invoke(...)). I also emptied the lambda body to speed up the invocation and to not have parameters, captured variables or return values have any influence.

Any non-empty lambda body causes the error to happen at an even lower invocation count.

Issue

Up to 35317 invocations there is no problem: The final output of the host is printed in the console (with impressive 0,04ms/call!).

But when incrementing numCalls by just 1 to 35318, it breaks with a WasmtimeException (see below).

Maybe this is an issue of Wasmtime itself, but I'm not sure as I'm not experienced in this kind of magic ๐Ÿ˜‰.

Edited ConsoleSample

using var host = new IsolatedRuntimeHost().WithBinDirectoryAssemblyLoader();
using var runtime = new IsolatedRuntime(host);

//int numCalls = 35317; // GOOD
int numCalls = 35318; // BAD
var sw = new Stopwatch();
sw.Start();
for (var i = 0; i < numCalls; i++)
{
    runtime.Invoke(() =>
    {
    });
}
sw.Stop();
Console.WriteLine($"Done in {sw.ElapsedMilliseconds:F0}ms ({(double)sw.ElapsedMilliseconds / numCalls:F4} ms/call)");

Raised Exception

[wasm_trace_logger] * Assertion at /home/runner/work/dotnet-wasi-sdk/dotnet-wasi-sdk/modules/runtime/src/mono/mono/metadata/sgen-stw.c:77, condition `info->client_info.stack_start >= info->client_info.info.stack_start_limit && info->client_info.stack_start < info->client_info.info.stack_end' not met

Unhandled exception. Wasmtime.WasmtimeException: error while executing at wasm backtrace:
    0: 0x40754b - <unknown>!<wasm function 7705>
    1: 0x405089 - <unknown>!<wasm function 7618>
    2: 0x407a16 - <unknown>!<wasm function 7714>
    3: 0xe2e68 - <unknown>!<wasm function 709>
    4: 0x2d2c58 - <unknown>!<wasm function 4879>
    5: 0x3f7937 - <unknown>!<wasm function 7321>
    6: 0x3f77e3 - <unknown>!<wasm function 7319>
    7: 0x3f7a2b - <unknown>!<wasm function 7323>
    8: 0x3f7ab6 - <unknown>!<wasm function 7324>
    9: 0x2b7fce - <unknown>!<wasm function 4637>
   10: 0x2b7d14 - <unknown>!<wasm function 4635>
   11: 0x30b42c - <unknown>!<wasm function 5606>
   12: 0x30ade8 - <unknown>!<wasm function 5605>
   13: 0x30ac21 - <unknown>!<wasm function 5604>
   14: 0x30ab95 - <unknown>!<wasm function 5603>
   15: 0x3003b6 - <unknown>!<wasm function 5444>
   16: 0x2bd5e5 - <unknown>!<wasm function 4725>
   17: 0x2292bf - <unknown>!<wasm function 3284>
   18: 0x229110 - <unknown>!<wasm function 3283>
   19: 0x92199 - <unknown>!<wasm function 333>
   20: 0x62d24 - <unknown>!<wasm function 332>
   21: 0x367621 - <unknown>!<wasm function 6309>
   22: 0x2110a7 - <unknown>!<wasm function 3085>
   23: 0x212d90 - <unknown>!<wasm function 3101>
   24: 0x21c3f8 - <unknown>!<wasm function 3187>
   25: 0xe3202 - <unknown>!<wasm function 714>
   26: 0xe3427 - <unknown>!<wasm function 715>
   27: 0x3cd2 - <unknown>!<wasm function 43>
   28: 0x4477 - <unknown>!<wasm function 46>
   29: 0x419242 - <unknown>!<wasm function 7924>

Caused by:
    Exited with i32 exit status 1
   at Wasmtime.Function.Invoke(Span`1 argumentsAndResults, StoreContext storeContext)
   at Wasmtime.Function.<>c__DisplayClass187_0`3.<WrapFunc>b__0(T1 p0, T2 p1)
   at DotNetIsolator.IsolatedRuntime.CopyObject[T](T value) in C:\source\DotNetIsolator\src\DotNetIsolator\IsolatedRuntime.cs:line 115
   at DotNetIsolator.IsolatedRuntime.Invoke(Action value) in C:\source\DotNetIsolator\src\DotNetIsolator\IsolatedRuntime.cs:line 308
   at Program.Main(String[] args) in C:\source\DotNetIsolator\sample\ConsoleSample\Program.cs:line 17

Expected Result

There should be no upper limit in invocation count.

Additional Information

Tested on 6dbe1a3
This error also happened on previous versions of DotNetIsolator.

dotnet --info
.NET SDK: Version: 8.0.100-preview.3.23178.7 Commit: e300b0e1e6

Runtime Environment:
OS Name: Windows
OS Version: 10.0.22621
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\8.0.100-preview.3.23178.7\

.NET workloads installed:
There are no installed workloads to display.

Host:
Version: 8.0.0-preview.3.23174.8
Architecture: x64
Commit: 47bad717bd

.NET SDKs installed:
3.1.426 [C:\Program Files\dotnet\sdk]
5.0.408 [C:\Program Files\dotnet\sdk]
5.0.416 [C:\Program Files\dotnet\sdk]
6.0.311 [C:\Program Files\dotnet\sdk]
7.0.100-preview.7.22377.5 [C:\Program Files\dotnet\sdk]
7.0.100-rc.1.22431.12 [C:\Program Files\dotnet\sdk]
7.0.105 [C:\Program Files\dotnet\sdk]
7.0.302 [C:\Program Files\dotnet\sdk]
7.0.400-preview.23225.8 [C:\Program Files\dotnet\sdk]
8.0.100-preview.3.23178.7 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.29 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.16 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.0-preview.7.22376.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.0-rc.1.22427.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.0-preview.3.23177.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.29 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.16 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.0-preview.7.22375.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.0-rc.1.22426.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.0-preview.3.23174.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.21 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 3.1.29 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.15 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.16 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.0-preview.7.22377.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.0-rc.1.22427.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.0-preview.3.23178.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
arm64 [C:\Program Files\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\arm64\InstallLocation]
x86 [C:\Program Files (x86)\dotnet]
registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
Not set

global.json file:
Not found

dotnet workload list
Installed Workload Id Manifest Version Installation Source --------------------------------------------------------------------------------------------- macos 13.1.462-net8-p3/8.0.100-preview.3 VS 17.7.33711.374 maccatalyst 16.2.462-net8-p3/8.0.100-preview.3 VS 17.7.33711.374 maui-windows 8.0.0-preview.3.8149/8.0.100-preview.3 VS 17.7.33711.374 maui-maccatalyst 8.0.0-preview.3.8149/8.0.100-preview.3 VS 17.7.33711.374 maui-ios 8.0.0-preview.3.8149/8.0.100-preview.3 VS 17.7.33711.374 wasm-tools 8.0.0-preview.3.23174.8/8.0.100-preview.3 VS 17.7.33711.374 wasm-tools-net6 8.0.0-preview.3.23174.8/8.0.100-preview.3 VS 17.7.33711.374 runtimes-windows 8.0.0-preview.3.23174.8/8.0.100-preview.3 VS 17.7.33711.374 ios 16.2.462-net8-p3/8.0.100-preview.3 VS 17.7.33711.374 android 34.0.0-preview.3.224/8.0.100-preview.3 VS 17.7.33711.374 maui-android 8.0.0-preview.3.8149/8.0.100-preview.3 VS 17.7.33711.374

Personal Note

That's some awesome stuff your doing here! Please keep it on!

Component Model & WIT files?

Have you had any thoughts on how the component model and *.wit files might fits with this for languages other than c#?

Do you know of anyone working on component model binding generation for *.wit files for dotnet/c#? If so can you point me in the direction of that work?

Thank you.

A way for the host to provide methods the isolated code can invoke?

Is there a way for the host to provide an instance or methods that the isolated environment could invoke on the host? I love that the isolated environment has no file or network access but i would like for the host to provide a method the isolated code could call to say read a file or get a url. This would allow the host app to verify if the isolated code should get access to that resource and then choose to provide that data indirectly to that isolated environment.

Interface instead of individual methods

I noticed both with calls in and the calls back to the host you always need to provide the method names and Params by hand. I was wondering if maybe you could use generics and interfaces instead to make it strongly typed and less error prone and get compile time checks. As well as maybe a runtime check method you could call to check the wsam implements the interface as well so you could error nicely.

Now you have call backs to the host I plan to start using your lib so this is from someone that has only looked at the examples so sorry if someone like it already exists. If it is not possible I can always just put add a class as a custom wrapper but built in would be better.

Execute Async?

how do i execute async function

image

cant wait for the day when this stuff is simple.... my god is it hard to say... here is some code run in isolation.

Reading Xml inside Invoke Lamda Expression

I tried to read XML file inside the invoke Lamda Expression but it make exception as below
System.Xml.XmlException: Xml_MissingRoot
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.ThrowWithoutLineInfo(String res)
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.XmlReader.MoveToContent()
at System.Xml.Linq.XElement.Load(XmlReader reader, LoadOptions options)
at System.Xml.Linq.XElement.Load(String uri, LoadOptions options)
at System.Xml.Linq.XElement.Load(String uri)

while i tried to read the xml out side the invoke ,it can be read without any issue , are there any limitation for what i can do inside the invoke Expression?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.