From fa3090a022bfaed3ef49d1a505a2f8eabb5e3e02 Mon Sep 17 00:00:00 2001 From: Takeshi Yoneda Date: Sun, 2 Apr 2023 17:30:34 -0700 Subject: [PATCH] Allows WithMemoryLimitPages to override the default pages (#1335) Signed-off-by: Takeshi Yoneda --- config.go | 7 ++++-- internal/gojs/compiler_test.go | 23 ++++-------------- internal/wasm/binary/decoder.go | 9 +++++--- internal/wasm/binary/memory_test.go | 36 ++++++++++++++++++++++++----- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/config.go b/config.go index 6e4f32bb..2a8848a8 100644 --- a/config.go +++ b/config.go @@ -48,8 +48,8 @@ type RuntimeConfig interface { WithCoreFeatures(api.CoreFeatures) RuntimeConfig // WithMemoryLimitPages overrides the maximum pages allowed per memory. The - // default is 65536, allowing 4GB total memory per instance. Setting a - // value larger than default will panic. + // default is 65536, allowing 4GB total memory per instance if the maximum is + // not encoded in a Wasm binary. Setting a value larger than default will panic. // // This example reduces the largest possible memory size from 4GB to 128KB: // rConfig = wazero.NewRuntimeConfig().WithMemoryLimitPages(2) @@ -67,6 +67,9 @@ type RuntimeConfig interface { // rConfig = wazero.NewRuntimeConfig().WithMemoryCapacityFromMax(true) // // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#grow-mem + // + // Note: if the memory maximum is not encoded in a Wasm binary, this + // results in allocating 4GB. See the doc on WithMemoryLimitPages for detail. WithMemoryCapacityFromMax(memoryCapacityFromMax bool) RuntimeConfig // WithDebugInfoEnabled toggles DWARF based stack traces in the face of diff --git a/internal/gojs/compiler_test.go b/internal/gojs/compiler_test.go index c7f46b95..6eeadf72 100644 --- a/internal/gojs/compiler_test.go +++ b/internal/gojs/compiler_test.go @@ -16,15 +16,11 @@ import ( "time" "github.com/tetratelabs/wazero" - "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/experimental/gojs" "github.com/tetratelabs/wazero/internal/fstest" internalgojs "github.com/tetratelabs/wazero/internal/gojs" "github.com/tetratelabs/wazero/internal/gojs/config" "github.com/tetratelabs/wazero/internal/gojs/run" - "github.com/tetratelabs/wazero/internal/testing/binaryencoding" - "github.com/tetratelabs/wazero/internal/wasm" - binaryformat "github.com/tetratelabs/wazero/internal/wasm/binary" ) type newConfig func(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *config.Config) @@ -35,8 +31,11 @@ func defaultConfig(moduleConfig wazero.ModuleConfig) (wazero.ModuleConfig, *conf func compileAndRun(ctx context.Context, arg string, config newConfig) (stdout, stderr string, err error) { rt := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfig(). - // https://github.com/tetratelabs/wazero/issues/992 + // In order to avoid race condition on scheduleTimeoutEvent, we need to set the memory max + // and WithMemoryCapacityFromMax(true) above. See #992. WithMemoryCapacityFromMax(true). + // Set max to a high value, e.g. so that Test_stdio_large can pass. + WithMemoryLimitPages(1024). // 64MB WithCompilationCache(cache)) return compileAndRunWithRuntime(ctx, rt, arg, config) // use global runtime } @@ -113,20 +112,6 @@ func TestMain(m *testing.M) { log.Panicln(err) } - // In order to avoid race condition on scheduleTimeoutEvent, we need to set the memory max - // and WithMemoryCapacityFromMax(true) above. - // https://github.com/tetratelabs/wazero/issues/992 - // - // TODO: Maybe add WithMemoryMax API? - parsed, err := binaryformat.DecodeModule(testBin, api.CoreFeaturesV2, wasm.MemoryLimitPages, false, false, false) - if err != nil { - log.Panicln(err) - } - // Set max to a high value, e.g. so that Test_stdio_large can pass - parsed.MemorySection.Max = 1024 // 64MB - parsed.MemorySection.IsMaxEncoded = true - testBin = binaryencoding.EncodeModule(parsed) - // Seed wazero's compilation cache to see any error up-front and to prevent // one test from a cache-miss performance penalty. r := wazero.NewRuntimeWithConfig(testCtx, wazero.NewRuntimeConfig().WithCompilationCache(cache)) diff --git a/internal/wasm/binary/decoder.go b/internal/wasm/binary/decoder.go index 4035ca7a..7387c85f 100644 --- a/internal/wasm/binary/decoder.go +++ b/internal/wasm/binary/decoder.go @@ -35,7 +35,7 @@ func DecodeModule( return nil, ErrInvalidVersion } - memorySizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax) + memSizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax) m := &wasm.Module{} var info, line, str, abbrev, ranges []byte // For DWARF Data. @@ -106,7 +106,7 @@ func DecodeModule( case wasm.SectionIDType: m.TypeSection, err = decodeTypeSection(enabledFeatures, r) case wasm.SectionIDImport: - m.ImportSection, m.ImportFunctionCount, m.ImportGlobalCount, m.ImportMemoryCount, m.ImportTableCount, err = decodeImportSection(r, memorySizer, memoryLimitPages, enabledFeatures) + m.ImportSection, m.ImportFunctionCount, m.ImportGlobalCount, m.ImportMemoryCount, m.ImportTableCount, err = decodeImportSection(r, memSizer, memoryLimitPages, enabledFeatures) if err != nil { return nil, err // avoid re-wrapping the error. } @@ -115,7 +115,7 @@ func DecodeModule( case wasm.SectionIDTable: m.TableSection, err = decodeTableSection(r, enabledFeatures) case wasm.SectionIDMemory: - m.MemorySection, err = decodeMemorySection(r, memorySizer, memoryLimitPages) + m.MemorySection, err = decodeMemorySection(r, memSizer, memoryLimitPages) case wasm.SectionIDGlobal: if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil { return nil, err // avoid re-wrapping the error. @@ -185,6 +185,9 @@ func newMemorySizer(memoryLimitPages uint32, memoryCapacityFromMax bool) memoryS } return minPages, minPages, *maxPages } + if memoryCapacityFromMax { + return minPages, memoryLimitPages, memoryLimitPages + } return minPages, minPages, memoryLimitPages } } diff --git a/internal/wasm/binary/memory_test.go b/internal/wasm/binary/memory_test.go index 1c2c7840..8cff4c62 100644 --- a/internal/wasm/binary/memory_test.go +++ b/internal/wasm/binary/memory_test.go @@ -13,32 +13,36 @@ import ( func Test_newMemorySizer(t *testing.T) { zero := uint32(0) one := uint32(1) - limit := wasm.MemoryLimitPages + defaultLimit := wasm.MemoryLimitPages tests := []struct { name string memoryCapacityFromMax bool + limit uint32 min uint32 max *uint32 expectedMin, expectedCapacity, expectedMax uint32 }{ { name: "min 0", + limit: defaultLimit, min: zero, - max: &limit, + max: &defaultLimit, expectedMin: zero, expectedCapacity: zero, - expectedMax: limit, + expectedMax: defaultLimit, }, { - name: "min 0 defaults max to limit", + name: "min 0 defaults max to defaultLimit", + limit: defaultLimit, min: zero, expectedMin: zero, expectedCapacity: zero, - expectedMax: limit, + expectedMax: defaultLimit, }, { name: "min 0, max 0", + limit: defaultLimit, min: zero, max: &zero, expectedMin: zero, @@ -47,6 +51,7 @@ func Test_newMemorySizer(t *testing.T) { }, { name: "min 0, max 1", + limit: defaultLimit, min: zero, max: &one, expectedMin: zero, @@ -55,6 +60,7 @@ func Test_newMemorySizer(t *testing.T) { }, { name: "min 0, max 1 memoryCapacityFromMax", + limit: defaultLimit, memoryCapacityFromMax: true, min: zero, max: &one, @@ -62,8 +68,26 @@ func Test_newMemorySizer(t *testing.T) { expectedCapacity: one, expectedMax: one, }, + { + name: "min 10, no max", + limit: 200, + min: 10, + expectedMin: 10, + expectedCapacity: 10, + expectedMax: 200, + }, + { + name: "min 10, no max memoryCapacityFromMax", + memoryCapacityFromMax: true, + limit: 200, + min: 10, + expectedMin: 10, + expectedCapacity: 200, + expectedMax: 200, + }, { name: "min=max", + limit: defaultLimit, min: one, max: &one, expectedMin: one, @@ -75,7 +99,7 @@ func Test_newMemorySizer(t *testing.T) { for _, tt := range tests { tc := tt t.Run(tc.name, func(t *testing.T) { - sizer := newMemorySizer(limit, tc.memoryCapacityFromMax) + sizer := newMemorySizer(tc.limit, tc.memoryCapacityFromMax) min, capacity, max := sizer(tc.min, tc.max) require.Equal(t, tc.expectedMin, min) require.Equal(t, tc.expectedCapacity, capacity)