From 94743cc21e0ae6cfd7cc8476e0e340ee453762b0 Mon Sep 17 00:00:00 2001 From: Edoardo Vacchi Date: Sat, 25 Feb 2023 00:31:23 +0100 Subject: [PATCH] Set Memory.Max to min(user-memory-limit, sys-memory-limit) on load (#1165) Updated `newMemorySizer()` to return the updated value, but also ensure that an invalid `max` still throws an error (invalid module). - Minor cleanup to use the `memorySizer` type instead of the full func signature for clarity - Added a wat+wasm under `internal/integration_test/vs/testdata/` simply because that's where the other cache-related testdata was. Closes #1153. Signed-off-by: Edoardo Vacchi --- cache_test.go | 34 ++++++++++++++++++ .../vs/testdata/mem_grow.wasm | Bin 0 -> 59 bytes .../integration_test/vs/testdata/mem_grow.wat | 14 ++++++++ internal/wasm/binary/decoder.go | 8 +++++ internal/wasm/binary/import.go | 2 +- internal/wasm/binary/memory.go | 2 +- internal/wasm/binary/memory_test.go | 26 +++++++++++--- internal/wasm/binary/section.go | 4 +-- 8 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 internal/integration_test/vs/testdata/mem_grow.wasm create mode 100644 internal/integration_test/vs/testdata/mem_grow.wat diff --git a/cache_test.go b/cache_test.go index 1fd145cd..2df01a07 100644 --- a/cache_test.go +++ b/cache_test.go @@ -17,6 +17,9 @@ import ( //go:embed internal/integration_test/vs/testdata/fac.wasm var facWasm []byte +//go:embed internal/integration_test/vs/testdata/mem_grow.wasm +var memGrowWasm []byte + func TestCompilationCache(t *testing.T) { ctx := context.Background() // Ensures the normal Wasm module compilation cache works. @@ -81,6 +84,37 @@ func TestCompilationCache(t *testing.T) { // Ensures they are different. require.NotEqual(t, fooCompiled, barCompiled) }) + + t.Run("memory limit should not affect caches", func(t *testing.T) { + // Creates new cache instance and pass it to the config. + c := NewCompilationCache() + config := NewRuntimeConfig().WithCompilationCache(c) + + // create two different runtimes with separate memory limits + rt0 := NewRuntimeWithConfig(ctx, config) + rt1 := NewRuntimeWithConfig(ctx, config.WithMemoryLimitPages(2)) + rt2 := NewRuntimeWithConfig(ctx, config.WithMemoryLimitPages(4)) + + // the compiled module is not equal because the memory limits are applied to the Memory instance + module0, _ := rt0.CompileModule(ctx, memGrowWasm) + module1, _ := rt1.CompileModule(ctx, memGrowWasm) + module2, _ := rt2.CompileModule(ctx, memGrowWasm) + + max0, _ := module0.ExportedMemories()["memory"].Max() + max1, _ := module1.ExportedMemories()["memory"].Max() + max2, _ := module2.ExportedMemories()["memory"].Max() + require.Equal(t, uint32(5), max0) + require.Equal(t, uint32(2), max1) + require.Equal(t, uint32(4), max2) + + compiledModule0 := module0.(*compiledModule) + compiledModule1 := module1.(*compiledModule) + compiledModule2 := module2.(*compiledModule) + + // compare the compiled engine which contains the underlying "codes" + require.Equal(t, compiledModule0.compiledEngine, compiledModule1.compiledEngine) + require.Equal(t, compiledModule1.compiledEngine, compiledModule2.compiledEngine) + }) } func getCacheSharedRuntimes(ctx context.Context, t *testing.T) (foo, bar *runtime) { diff --git a/internal/integration_test/vs/testdata/mem_grow.wasm b/internal/integration_test/vs/testdata/mem_grow.wasm new file mode 100644 index 0000000000000000000000000000000000000000..a2c9f66d0f0408979282d9cb627d387cc046d0b4 GIT binary patch literal 59 zcmV~$I}Uh*;v`Dq|V!-}AF6 wasm.MemoryLimitPages { + return minPages, minPages, *maxPages + } + // This is a valid value, but it goes over the run-time limit: return the limit. + if *maxPages > memoryLimitPages { + return minPages, memoryLimitPages, memoryLimitPages + } return minPages, minPages, *maxPages } return minPages, minPages, memoryLimitPages diff --git a/internal/wasm/binary/import.go b/internal/wasm/binary/import.go index 094b488b..d5b502ad 100644 --- a/internal/wasm/binary/import.go +++ b/internal/wasm/binary/import.go @@ -12,7 +12,7 @@ import ( func decodeImport( r *bytes.Reader, idx uint32, - memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32), + memorySizer memorySizer, memoryLimitPages uint32, enabledFeatures api.CoreFeatures, ) (i *wasm.Import, err error) { diff --git a/internal/wasm/binary/memory.go b/internal/wasm/binary/memory.go index 7ce40e43..420f9a55 100644 --- a/internal/wasm/binary/memory.go +++ b/internal/wasm/binary/memory.go @@ -11,7 +11,7 @@ import ( // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-memory func decodeMemory( r *bytes.Reader, - memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32), + memorySizer memorySizer, memoryLimitPages uint32, ) (*wasm.Memory, error) { min, maxP, err := decodeLimitsType(r) diff --git a/internal/wasm/binary/memory_test.go b/internal/wasm/binary/memory_test.go index c5fb3fab..ce983505 100644 --- a/internal/wasm/binary/memory_test.go +++ b/internal/wasm/binary/memory_test.go @@ -88,9 +88,10 @@ func TestMemoryType(t *testing.T) { max := wasm.MemoryLimitPages tests := []struct { - name string - input *wasm.Memory - expected []byte + name string + input *wasm.Memory + memoryLimitPages uint32 + expected []byte }{ { name: "min 0", @@ -122,6 +123,12 @@ func TestMemoryType(t *testing.T) { input: &wasm.Memory{Min: max, Cap: max, Max: max, IsMaxEncoded: true}, expected: []byte{0x1, 0x80, 0x80, 0x4, 0x80, 0x80, 0x4}, }, + { + name: "min 0, max largest, wazero limit", + input: &wasm.Memory{Max: max, IsMaxEncoded: true}, + memoryLimitPages: 512, + expected: []byte{0x1, 0, 0x80, 0x80, 0x4}, + }, } for _, tt := range tests { @@ -133,9 +140,18 @@ func TestMemoryType(t *testing.T) { }) t.Run(fmt.Sprintf("decode %s", tc.name), func(t *testing.T) { - binary, err := decodeMemory(bytes.NewReader(b), newMemorySizer(max, false), max) + tmax := max + expectedDecoded := tc.input + if tc.memoryLimitPages != 0 { + // If a memory limit exists, then the expected module Max, Cap reflect that limit. + tmax = tc.memoryLimitPages + expectedDecoded.Max = tmax + expectedDecoded.Cap = tmax + } + + binary, err := decodeMemory(bytes.NewReader(b), newMemorySizer(tmax, false), tmax) require.NoError(t, err) - require.Equal(t, binary, tc.input) + require.Equal(t, binary, expectedDecoded) }) } } diff --git a/internal/wasm/binary/section.go b/internal/wasm/binary/section.go index d0a87cbc..c4baba64 100644 --- a/internal/wasm/binary/section.go +++ b/internal/wasm/binary/section.go @@ -27,7 +27,7 @@ func decodeTypeSection(enabledFeatures api.CoreFeatures, r *bytes.Reader) ([]*wa func decodeImportSection( r *bytes.Reader, - memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32), + memorySizer memorySizer, memoryLimitPages uint32, enabledFeatures api.CoreFeatures, ) ([]*wasm.Import, error) { @@ -84,7 +84,7 @@ func decodeTableSection(r *bytes.Reader, enabledFeatures api.CoreFeatures) ([]*w func decodeMemorySection( r *bytes.Reader, - memorySizer func(minPages uint32, maxPages *uint32) (min, capacity, max uint32), + memorySizer memorySizer, memoryLimitPages uint32, ) (*wasm.Memory, error) { vs, _, err := leb128.DecodeUint32(r)