wasmtime-dotnet icon indicating copy to clipboard operation
wasmtime-dotnet copied to clipboard

`Memory` with more than 32767 pages cannot be accessed

Open kpreisser opened this issue 2 years ago • 0 comments

Hi, consider the following C# program (.NET 6.0, using Wasmtime 1.0.0):

using var engine = new Engine();

using var module1 = Module.FromText(
    engine,
    "hello",
    @"
(module 
    (memory $mem1 65536)
    (export ""memory"" (memory $mem1))
)");

using (var linker = new Linker(engine)) {
    using var store = new Store(engine);
    var instance = linker.Instantiate(store, module1);

    var memory = instance.GetMemory("memory")!;

    try {
        memory.WriteInt16(10, 123);
        Console.WriteLine("Success");
    }
    catch (Exception ex) {
        Console.WriteLine(ex.ToString());
    }

    try {
        var memorySpan = memory.GetSpan();
        Console.WriteLine("Success");
    }
    catch (Exception ex) {
        Console.WriteLine(ex.ToString());
    }
}

Actual behavior: When running the program, an OverflowException occurs when the memory is accessed. E.g. with Memory.WriteInt16():

System.OverflowException: Arithmetic operation resulted in an overflow.
   at System.UIntPtr.ToUInt32()
   at Wasmtime.Memory.GetSpan() in C:\Users\VPC\Desktop\WebAssembly-Test\wasmtime-dotnet\src\Memory.cs:line 92
   at Wasmtime.Memory.WriteInt16(Int32 address, Int16 value) in C:\Users\VPC\Desktop\WebAssembly-Test\wasmtime-dotnet\src\Memory.cs:line 228
   at Program.<<Main>$>g__TestBigMemoryCustomWasm|0_3() in C:\Users\VPC\Desktop\WebAssembly-Test\TestWasmtime\Program.cs:line 326

Or, when changing the memory definition to (memory $mem1 32768), a similar OverflowException occurs:

System.OverflowException: Value was either too large or too small for an Int32.
   at System.Convert.ThrowInt32OverflowException()
   at System.Convert.ToInt32(UInt32 value)
   at Wasmtime.Memory.GetSpan() in C:\Users\VPC\Desktop\WebAssembly-Test\wasmtime-dotnet\src\Memory.cs:line 92
   at Wasmtime.Memory.WriteInt16(Int32 address, Int16 value) in C:\Users\VPC\Desktop\WebAssembly-Test\wasmtime-dotnet\src\Memory.cs:line 228
   at Program.<<Main>$>g__TestBigMemoryCustomWasm|0_3() in C:\Users\VPC\Desktop\WebAssembly-Test\TestWasmtime\Program.cs:line 326

The Memory can currently only be accessed by calling GetSpan() that returns a Span<byte> over the whole memory area. Unfortunately, a Span uses an Int32 length, so it can only have a max length of 2^31-1. Using a memory with 32768 or more pages would exceed that value for a Span<byte> (using a memory with 65536 pages would even exceed the storage of a 32-bit unsigned integer for the length, see also https://github.com/bytecodealliance/wasmtime/pull/3134).

I'm not sure of the best approach to solve this, but I could imagine that we may need new APIs using a long instead of int for addresses (like WriteInt16(long address, short value) etc.), and maybe a Span<byte> GetSpan(long address, int length) that allows to retrieve a Span for the given address and length (when the length doesn't exceed 2^31-1). Additionally, to allow the caller to access the whole memory at once, we might need to allow retrieving a pointer (IntPtr) to the memory start.

(With the Memory64 proposal, a memory can even be larger than 4 GiB. As far is I understand it, it would theoretically support a memory length of 2^64 bytes, which might need to use an ulong as address to access more than 2^63 bytes, but ulong in .NET isn't CLS-compliant, and such a memory size is as of today only a theoretical situation, so I think using a long should be fine.)

What do you think?

Thanks!

kpreisser avatar Oct 14 '22 07:10 kpreisser