From 1353ca24fef0a57a3a342d75f20357a6e9d3be35 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Mon, 16 Sep 2024 10:28:30 +0100 Subject: [PATCH] experimental: allow custom memory allocator Reallocate to fail (#2315) Signed-off-by: Nuno Cruces --- experimental/memory.go | 2 ++ internal/wasm/memory.go | 6 +++++- internal/wasm/memory_test.go | 14 +++++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/experimental/memory.go b/experimental/memory.go index e379bf0538..26540648ba 100644 --- a/experimental/memory.go +++ b/experimental/memory.go @@ -35,6 +35,8 @@ type LinearMemory interface { // Notes: // - To back a shared memory, Reallocate can't change the address of the // backing []byte (only its length/capacity may change). + // - Reallocate may return nil if fails to grow the LinearMemory. This + // condition may or may not be handled gracefully by the Wasm module. Reallocate(size uint64) []byte // Free the backing memory buffer. Free() diff --git a/internal/wasm/memory.go b/internal/wasm/memory.go index 8e072fd127..9a1f0c6d1d 100644 --- a/internal/wasm/memory.go +++ b/internal/wasm/memory.go @@ -77,6 +77,7 @@ func NewMemoryInstance(memSec *Memory, allocator experimental.MemoryAllocator, m if allocator != nil { expBuffer = allocator.Allocate(capBytes, maxBytes) buffer = expBuffer.Reallocate(minBytes) + _ = buffer[:minBytes] // Bounds check that the minimum was allocated. } else if memSec.IsShared { // Shared memory needs a fixed buffer, so allocate with the maximum size. // @@ -238,12 +239,15 @@ func (m *MemoryInstance) Grow(delta uint32) (result uint32, ok bool) { return currentPages, true } - // If exceeds the max of memory size, we push -1 according to the spec. newPages := currentPages + delta if newPages > m.Max || int32(delta) < 0 { return 0, false } else if m.expBuffer != nil { buffer := m.expBuffer.Reallocate(MemoryPagesToBytesNum(newPages)) + if buffer == nil { + // Allocator failed to grow. + return 0, false + } if m.Shared { if unsafe.SliceData(buffer) != unsafe.SliceData(m.Buffer) { panic("shared memory cannot move, this is a bug in the memory allocator") diff --git a/internal/wasm/memory_test.go b/internal/wasm/memory_test.go index d10c8276c7..16cb01b61a 100644 --- a/internal/wasm/memory_test.go +++ b/internal/wasm/memory_test.go @@ -33,13 +33,15 @@ func TestMemoryBytesNumToPages(t *testing.T) { func TestMemoryInstance_Grow_Size(t *testing.T) { tests := []struct { - name string - capEqualsMax bool - expAllocator bool + name string + capEqualsMax bool + expAllocator bool + failAllocator bool }{ {name: ""}, {name: "capEqualsMax", capEqualsMax: true}, {name: "expAllocator", expAllocator: true}, + {name: "failAllocator", failAllocator: true}, } for _, tt := range tests { @@ -58,6 +60,9 @@ func TestMemoryInstance_Grow_Size(t *testing.T) { case tc.expAllocator: expBuffer := sliceAllocator(0, maxBytes) m = &MemoryInstance{Max: max, Buffer: expBuffer.Reallocate(0), expBuffer: expBuffer} + case tc.failAllocator: + expBuffer := sliceAllocator(0, maxBytes) + m = &MemoryInstance{Max: max * 2, Buffer: expBuffer.Reallocate(0), expBuffer: expBuffer} } m.ownerModuleEngine = me @@ -1023,6 +1028,9 @@ type sliceBuffer struct { func (b *sliceBuffer) Free() {} func (b *sliceBuffer) Reallocate(size uint64) []byte { + if size > b.max { + return nil + } if cap := uint64(cap(b.buf)); size > cap { b.buf = append(b.buf[:cap], make([]byte, size-cap)...) } else {