diff --git a/internal/wasm/memory.go b/internal/wasm/memory.go index c882b0f0..e2d994f9 100644 --- a/internal/wasm/memory.go +++ b/internal/wasm/memory.go @@ -111,7 +111,7 @@ func (m *MemoryInstance) ReadFloat64Le(offset uint32) (float64, bool) { // Read implements the same method as documented on api.Memory. func (m *MemoryInstance) Read(offset, byteCount uint32) ([]byte, bool) { - if !m.hasSize(offset, byteCount) { + if !m.hasSize(offset, uint64(byteCount)) { return nil, false } return m.Buffer[offset : offset+byteCount : offset+byteCount], true @@ -157,7 +157,7 @@ func (m *MemoryInstance) WriteFloat64Le(offset uint32, v float64) bool { // Write implements the same method as documented on api.Memory. func (m *MemoryInstance) Write(offset uint32, val []byte) bool { - if !m.hasSize(offset, uint32(len(val))) { + if !m.hasSize(offset, uint64(len(val))) { return false } copy(m.Buffer[offset:], val) @@ -166,7 +166,7 @@ func (m *MemoryInstance) Write(offset uint32, val []byte) bool { // WriteString implements the same method as documented on api.Memory. func (m *MemoryInstance) WriteString(offset uint32, val string) bool { - if !m.hasSize(offset, uint32(len(val))) { + if !m.hasSize(offset, uint64(len(val))) { return false } copy(m.Buffer[offset:], val) @@ -243,8 +243,8 @@ func (m *MemoryInstance) size() uint32 { // hasSize returns true if Len is sufficient for byteCount at the given offset. // // Note: This is always fine, because memory can grow, but never shrink. -func (m *MemoryInstance) hasSize(offset uint32, byteCount uint32) bool { - return uint64(offset)+uint64(byteCount) <= uint64(len(m.Buffer)) // uint64 prevents overflow on add +func (m *MemoryInstance) hasSize(offset uint32, byteCount uint64) bool { + return uint64(offset)+byteCount <= uint64(len(m.Buffer)) // uint64 prevents overflow on add } // readUint32Le implements ReadUint32Le without using a context. This is extracted as both ints and floats are stored in diff --git a/internal/wasm/memory_test.go b/internal/wasm/memory_test.go index 832b2e45..5957741d 100644 --- a/internal/wasm/memory_test.go +++ b/internal/wasm/memory_test.go @@ -2,8 +2,10 @@ package wasm import ( "math" + "reflect" "strings" "testing" + "unsafe" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/internal/testing/require" @@ -194,7 +196,7 @@ func TestMemoryInstance_HasSize(t *testing.T) { tc := tt t.Run(tc.name, func(t *testing.T) { - require.Equal(t, tc.expected, memory.hasSize(tc.offset, uint32(tc.sizeInBytes))) + require.Equal(t, tc.expected, memory.hasSize(tc.offset, tc.sizeInBytes)) }) } } @@ -735,6 +737,23 @@ func TestMemoryInstance_Write(t *testing.T) { require.False(t, ok) } +func TestMemoryInstance_Write_overflow(t *testing.T) { + mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1} + + // Test overflow + huge := uint64(math.MaxUint32 + 1 + 4) + if huge != uint64(int(huge)) { + t.Skip("Skipping on 32-bit") + } + + buf := []byte{16, 0, 0, 4} + header := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) + header.Len = int(huge) + header.Cap = int(huge) + + require.False(t, mem.Write(4, buf)) +} + func TestMemoryInstance_WriteString(t *testing.T) { mem := &MemoryInstance{Buffer: []byte{0, 0, 0, 0, 16, 0, 0, 0}, Min: 1}