From b53ef7d7aedc5372cc1be5b31c3c65fe2202f53b Mon Sep 17 00:00:00 2001 From: jimboj Date: Tue, 14 Mar 2023 16:39:55 -0600 Subject: [PATCH 1/8] create wrapper around wasmer.Memory and test it --- go.mod | 1 + go.sum | 2 + lib/runtime/testWasmer/memory.go | 35 +++++++ lib/runtime/testWasmer/memory_test.go | 129 ++++++++++++++++++++++++++ 4 files changed, 167 insertions(+) create mode 100644 lib/runtime/testWasmer/memory.go create mode 100644 lib/runtime/testWasmer/memory_test.go diff --git a/go.mod b/go.mod index 4ae5a76238..ecc617943f 100644 --- a/go.mod +++ b/go.mod @@ -38,6 +38,7 @@ require ( github.com/stretchr/testify v1.8.2 github.com/urfave/cli/v2 v2.25.1 github.com/wasmerio/go-ext-wasm v0.3.2-0.20200326095750-0a32be6068ec + github.com/wasmerio/wasmer-go v1.0.4 github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9 golang.org/x/crypto v0.7.0 golang.org/x/exp v0.0.0-20230206171751-46f607a40771 diff --git a/go.sum b/go.sum index 3a6631ca2c..9ad1e2a273 100644 --- a/go.sum +++ b/go.sum @@ -612,6 +612,8 @@ github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMI github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= github.com/wasmerio/go-ext-wasm v0.3.2-0.20200326095750-0a32be6068ec h1:VElCeVyfCWNmCv6UisKQrr+P2/JRG0uf4/FIdCB4pL0= github.com/wasmerio/go-ext-wasm v0.3.2-0.20200326095750-0a32be6068ec/go.mod h1:VGyarTzasuS7k5KhSIGpM3tciSZlkP31Mp9VJTHMMeI= +github.com/wasmerio/wasmer-go v1.0.4 h1:MnqHoOGfiQ8MMq2RF6wyCeebKOe84G88h5yv+vmxJgs= +github.com/wasmerio/wasmer-go v1.0.4/go.mod h1:0gzVdSfg6pysA6QVp6iVRPTagC6Wq9pOE8J86WKb2Fk= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9 h1:Y1/FEOpaCpD21WxrmfeIYCFPuVPRCY2XZTWzTNHGw30= diff --git a/lib/runtime/testWasmer/memory.go b/lib/runtime/testWasmer/memory.go new file mode 100644 index 0000000000..f3eddd9744 --- /dev/null +++ b/lib/runtime/testWasmer/memory.go @@ -0,0 +1,35 @@ +package testWasmer + +import ( + "errors" + "fmt" + + wasmgo "github.com/wasmerio/wasmer-go/wasmer" +) + +var errGrowMemory = errors.New("failed to grow memory") + +// Memory is a thin wrapper around Wasmer memory to support +// Gossamer runtime.Memory interface +type Memory struct { + memory *wasmgo.Memory +} + +// Data returns the memory's data +func (m Memory) Data() []byte { + return m.memory.Data() +} + +// Length returns the memory's length +func (m Memory) Length() uint32 { + return uint32(m.memory.DataSize()) +} + +// Grow grows the memory by the given number of pages +func (m Memory) Grow(numPages uint32) error { + ok := m.memory.Grow(wasmgo.Pages(numPages)) + if !ok { + return fmt.Errorf("%w", errGrowMemory) + } + return nil +} diff --git a/lib/runtime/testWasmer/memory_test.go b/lib/runtime/testWasmer/memory_test.go new file mode 100644 index 0000000000..b440e41dcc --- /dev/null +++ b/lib/runtime/testWasmer/memory_test.go @@ -0,0 +1,129 @@ +package testWasmer + +import ( + "github.com/stretchr/testify/require" + "github.com/wasmerio/wasmer-go/wasmer" + "testing" + "unsafe" +) + +func createInstance(t *testing.T) (*wasmer.Instance, error) { + // We are using the text representation of the module here. + // Taken from: https://github.com/wasmerio/wasmer-go/blob/23d786b6b81ad93e2b974d2f4510bea374f0f37c/examples/example_memory_test.go#L28 //nolint:lll + wasmBytes := []byte(` + (module + (type $mem_size_t (func (result i32))) + (type $get_at_t (func (param i32) (result i32))) + (type $set_at_t (func (param i32) (param i32))) + (memory $mem 1) + (func $get_at (type $get_at_t) (param $idx i32) (result i32) + (i32.load (local.get $idx))) + (func $set_at (type $set_at_t) (param $idx i32) (param $val i32) + (i32.store (local.get $idx) (local.get $val))) + (func $mem_size (type $mem_size_t) (result i32) + (memory.size)) + (export "get_at" (func $get_at)) + (export "set_at" (func $set_at)) + (export "mem_size" (func $mem_size)) + (export "memory" (memory $mem))) + `) + + wasmerConfig := wasmer.NewConfig() + wasmerConfig.UseSinglepassCompiler() + + engine := wasmer.NewEngineWithConfig(wasmerConfig) + store := wasmer.NewStore(engine) + + // Compile module + module, err := wasmer.NewModule(store, wasmBytes) + require.NoError(t, err) + + importObject := wasmer.NewImportObject() + + // Instantiate the Wasm module. + return wasmer.NewInstance(module, importObject) +} + +func TestMemory_Length(t *testing.T) { + const pageLength uint32 = 65536 + instance, err := createInstance(t) + require.NoError(t, err) + + wasmerMemory, err := instance.Exports.GetMemory("memory") + require.NoError(t, err) + + memory := Memory{ + memory: wasmerMemory, + } + + memLength := memory.Length() + require.Equal(t, pageLength, memLength) +} + +func TestMemory_Grow(t *testing.T) { + const pageLength uint32 = 65536 + instance, err := createInstance(t) + require.NoError(t, err) + + wasmerMemory, err := instance.Exports.GetMemory("memory") + require.NoError(t, err) + + memory := Memory{ + memory: wasmerMemory, + } + + memLength := memory.Length() + require.Equal(t, pageLength, memLength) + + err = memory.Grow(1) + require.NoError(t, err) + + memLength = memory.Length() + require.Equal(t, pageLength*2, memLength) +} + +func TestMemory_Data(t *testing.T) { + const pageLength uint32 = 65536 + instance, err := createInstance(t) + require.NoError(t, err) + + // Grab exported utility functions from the module + getAt, err := instance.Exports.GetFunction("get_at") + require.NoError(t, err) + + setAt, err := instance.Exports.GetFunction("set_at") + require.NoError(t, err) + + wasmerMemory, err := instance.Exports.GetMemory("memory") + require.NoError(t, err) + + memory := Memory{ + memory: wasmerMemory, + } + + memAddr := 0x0 + const val int32 = 0xFEFEFFE + _, err = setAt(memAddr, val) + require.NoError(t, err) + + // Compare bytes at address 0x0 + expectedFirstBytes := []byte{254, 239, 239, 15} + memData := memory.Data() + require.Equal(t, expectedFirstBytes, memData[:4]) + + result, err := getAt(memAddr) + require.NoError(t, err) + require.Equal(t, val, result) + + // Write value at end of page + pageSize := 0x1_0000 + memAddr = (pageSize) - int(unsafe.Sizeof(val)) + const val2 int32 = 0xFEA09 + _, err = setAt(memAddr, val2) + require.NoError(t, err) + + result, err = getAt(memAddr) + require.NoError(t, err) + require.Equal(t, val2, result) + +} From 2a8a9b3360a734538d1e6fbe43d247d7299a4307 Mon Sep 17 00:00:00 2001 From: jimboj Date: Tue, 14 Mar 2023 16:43:30 -0600 Subject: [PATCH 2/8] lint and clean --- lib/runtime/testWasmer/memory_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/runtime/testWasmer/memory_test.go b/lib/runtime/testWasmer/memory_test.go index b440e41dcc..58f56ad769 100644 --- a/lib/runtime/testWasmer/memory_test.go +++ b/lib/runtime/testWasmer/memory_test.go @@ -1,15 +1,16 @@ package testWasmer import ( - "github.com/stretchr/testify/require" - "github.com/wasmerio/wasmer-go/wasmer" "testing" "unsafe" + + "github.com/stretchr/testify/require" + "github.com/wasmerio/wasmer-go/wasmer" ) func createInstance(t *testing.T) (*wasmer.Instance, error) { - // We are using the text representation of the module here. - // Taken from: https://github.com/wasmerio/wasmer-go/blob/23d786b6b81ad93e2b974d2f4510bea374f0f37c/examples/example_memory_test.go#L28 //nolint:lll + // We are using the text representation of the module here. Taken from: + // https://github.com/wasmerio/wasmer-go/blob/23d786b6b81ad93e2b974d2f4510bea374f0f37c/examples/example_memory_test.go#L28 wasmBytes := []byte(` (module (type $mem_size_t (func (result i32))) @@ -83,7 +84,6 @@ func TestMemory_Grow(t *testing.T) { } func TestMemory_Data(t *testing.T) { - const pageLength uint32 = 65536 instance, err := createInstance(t) require.NoError(t, err) From f1a5a1730dc32b84663f0e10ddb2e5d98c27b050 Mon Sep 17 00:00:00 2001 From: jimboj Date: Tue, 14 Mar 2023 16:46:00 -0600 Subject: [PATCH 3/8] rename to newWasmer --- lib/runtime/{testWasmer => newWasmer}/memory.go | 2 +- lib/runtime/{testWasmer => newWasmer}/memory_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename lib/runtime/{testWasmer => newWasmer}/memory.go (97%) rename lib/runtime/{testWasmer => newWasmer}/memory_test.go (99%) diff --git a/lib/runtime/testWasmer/memory.go b/lib/runtime/newWasmer/memory.go similarity index 97% rename from lib/runtime/testWasmer/memory.go rename to lib/runtime/newWasmer/memory.go index f3eddd9744..76e5284a92 100644 --- a/lib/runtime/testWasmer/memory.go +++ b/lib/runtime/newWasmer/memory.go @@ -1,4 +1,4 @@ -package testWasmer +package newWasmer import ( "errors" diff --git a/lib/runtime/testWasmer/memory_test.go b/lib/runtime/newWasmer/memory_test.go similarity index 99% rename from lib/runtime/testWasmer/memory_test.go rename to lib/runtime/newWasmer/memory_test.go index 58f56ad769..d5959c0730 100644 --- a/lib/runtime/testWasmer/memory_test.go +++ b/lib/runtime/newWasmer/memory_test.go @@ -1,4 +1,4 @@ -package testWasmer +package newWasmer import ( "testing" From 815fe8d61e0e19093c1f298c9f09325cb49b5616 Mon Sep 17 00:00:00 2001 From: jimboj Date: Tue, 14 Mar 2023 16:48:44 -0600 Subject: [PATCH 4/8] make createInstance a helper --- lib/runtime/newWasmer/memory_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/runtime/newWasmer/memory_test.go b/lib/runtime/newWasmer/memory_test.go index d5959c0730..75b45bb2de 100644 --- a/lib/runtime/newWasmer/memory_test.go +++ b/lib/runtime/newWasmer/memory_test.go @@ -9,6 +9,7 @@ import ( ) func createInstance(t *testing.T) (*wasmer.Instance, error) { + t.Helper() // We are using the text representation of the module here. Taken from: // https://github.com/wasmerio/wasmer-go/blob/23d786b6b81ad93e2b974d2f4510bea374f0f37c/examples/example_memory_test.go#L28 wasmBytes := []byte(` From fb386f6596b8b24f752796ca527c30dedd806549 Mon Sep 17 00:00:00 2001 From: jimboj Date: Wed, 15 Mar 2023 14:19:09 -0600 Subject: [PATCH 5/8] add copyright --- lib/runtime/newWasmer/memory.go | 3 +++ lib/runtime/newWasmer/memory_test.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/lib/runtime/newWasmer/memory.go b/lib/runtime/newWasmer/memory.go index 76e5284a92..13f3fbf9c1 100644 --- a/lib/runtime/newWasmer/memory.go +++ b/lib/runtime/newWasmer/memory.go @@ -1,3 +1,6 @@ +// Copyright 2023 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + package newWasmer import ( diff --git a/lib/runtime/newWasmer/memory_test.go b/lib/runtime/newWasmer/memory_test.go index 75b45bb2de..9cacae329d 100644 --- a/lib/runtime/newWasmer/memory_test.go +++ b/lib/runtime/newWasmer/memory_test.go @@ -1,3 +1,6 @@ +// Copyright 2023 ChainSafe Systems (ON) +// SPDX-License-Identifier: LGPL-3.0-only + package newWasmer import ( From aae4a6a942fa8f9136da438e066b7ff6619e85d6 Mon Sep 17 00:00:00 2001 From: jimboj Date: Wed, 15 Mar 2023 14:31:54 -0600 Subject: [PATCH 6/8] use default compiler --- lib/runtime/newWasmer/memory_test.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/runtime/newWasmer/memory_test.go b/lib/runtime/newWasmer/memory_test.go index 9cacae329d..0ad64351e7 100644 --- a/lib/runtime/newWasmer/memory_test.go +++ b/lib/runtime/newWasmer/memory_test.go @@ -33,10 +33,7 @@ func createInstance(t *testing.T) (*wasmer.Instance, error) { (export "memory" (memory $mem))) `) - wasmerConfig := wasmer.NewConfig() - wasmerConfig.UseSinglepassCompiler() - - engine := wasmer.NewEngineWithConfig(wasmerConfig) + engine := wasmer.NewEngine() store := wasmer.NewStore(engine) // Compile module From 77ed45be4169f73633e253d8be8707412a8b7dd4 Mon Sep 17 00:00:00 2001 From: jimboj Date: Mon, 27 Mar 2023 15:08:18 -0600 Subject: [PATCH 7/8] add safe casting to memory file --- lib/runtime/newWasmer/memory.go | 23 +++++++++++--- lib/runtime/newWasmer/memory_test.go | 47 +++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/lib/runtime/newWasmer/memory.go b/lib/runtime/newWasmer/memory.go index 13f3fbf9c1..3c4586b544 100644 --- a/lib/runtime/newWasmer/memory.go +++ b/lib/runtime/newWasmer/memory.go @@ -1,16 +1,20 @@ // Copyright 2023 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package newWasmer +package wasmer import ( "errors" "fmt" + "math" wasmgo "github.com/wasmerio/wasmer-go/wasmer" ) -var errGrowMemory = errors.New("failed to grow memory") +var ( + errCantGrowMemory = errors.New("failed to grow memory") + errMemoryValueOutOfBounds = errors.New("memory value is out of bounds") +) // Memory is a thin wrapper around Wasmer memory to support // Gossamer runtime.Memory interface @@ -18,6 +22,13 @@ type Memory struct { memory *wasmgo.Memory } +func checkBounds(value uint64) (uint32, error) { + if value > math.MaxUint32 { + return 0, fmt.Errorf("%w", errMemoryValueOutOfBounds) + } + return uint32(value), nil +} + // Data returns the memory's data func (m Memory) Data() []byte { return m.memory.Data() @@ -25,14 +36,18 @@ func (m Memory) Data() []byte { // Length returns the memory's length func (m Memory) Length() uint32 { - return uint32(m.memory.DataSize()) + value, err := checkBounds(uint64(m.memory.DataSize())) + if err != nil { + panic(err) + } + return value } // Grow grows the memory by the given number of pages func (m Memory) Grow(numPages uint32) error { ok := m.memory.Grow(wasmgo.Pages(numPages)) if !ok { - return fmt.Errorf("%w", errGrowMemory) + return fmt.Errorf("%w", errCantGrowMemory) } return nil } diff --git a/lib/runtime/newWasmer/memory_test.go b/lib/runtime/newWasmer/memory_test.go index 0ad64351e7..6907f1a13a 100644 --- a/lib/runtime/newWasmer/memory_test.go +++ b/lib/runtime/newWasmer/memory_test.go @@ -1,9 +1,11 @@ // Copyright 2023 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package newWasmer +package wasmer import ( + "github.com/stretchr/testify/assert" + "math" "testing" "unsafe" @@ -47,6 +49,7 @@ func createInstance(t *testing.T) (*wasmer.Instance, error) { } func TestMemory_Length(t *testing.T) { + t.Parallel() const pageLength uint32 = 65536 instance, err := createInstance(t) require.NoError(t, err) @@ -63,6 +66,7 @@ func TestMemory_Length(t *testing.T) { } func TestMemory_Grow(t *testing.T) { + t.Parallel() const pageLength uint32 = 65536 instance, err := createInstance(t) require.NoError(t, err) @@ -85,6 +89,7 @@ func TestMemory_Grow(t *testing.T) { } func TestMemory_Data(t *testing.T) { + t.Parallel() instance, err := createInstance(t) require.NoError(t, err) @@ -126,5 +131,45 @@ func TestMemory_Data(t *testing.T) { result, err = getAt(memAddr) require.NoError(t, err) require.Equal(t, val2, result) +} +func TestMemory_CheckBounds(t *testing.T) { + t.Parallel() + testCases := []struct { + name string + value uint64 + exp uint32 + expErr error + expErrMsg string + }{ + { + name: "valid cast", + value: uint64(0), + exp: uint32(0), + }, + { + name: "max uint32", + value: uint64(math.MaxUint32), + exp: math.MaxUint32, + }, + { + name: "out of bounds", + value: uint64(math.MaxUint32 + 1), + expErr: errMemoryValueOutOfBounds, + expErrMsg: errMemoryValueOutOfBounds.Error(), + }, + } + for _, test := range testCases { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + res, err := checkBounds(test.value) + assert.ErrorIs(t, err, test.expErr) + if test.expErr != nil { + assert.EqualError(t, err, test.expErrMsg) + } + assert.Equal(t, test.exp, res) + }) + } } From 0ecc1a6420c07086f7da8519ce54faace0d271d9 Mon Sep 17 00:00:00 2001 From: jimboj Date: Mon, 27 Mar 2023 15:12:43 -0600 Subject: [PATCH 8/8] respond to PR feedback --- lib/runtime/newWasmer/memory.go | 10 +++++----- lib/runtime/newWasmer/memory_test.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/runtime/newWasmer/memory.go b/lib/runtime/newWasmer/memory.go index 3c4586b544..e18f8856ee 100644 --- a/lib/runtime/newWasmer/memory.go +++ b/lib/runtime/newWasmer/memory.go @@ -1,14 +1,14 @@ // Copyright 2023 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package wasmer +package latestwasmer import ( "errors" "fmt" "math" - wasmgo "github.com/wasmerio/wasmer-go/wasmer" + "github.com/wasmerio/wasmer-go/wasmer" ) var ( @@ -19,7 +19,7 @@ var ( // Memory is a thin wrapper around Wasmer memory to support // Gossamer runtime.Memory interface type Memory struct { - memory *wasmgo.Memory + memory *wasmer.Memory } func checkBounds(value uint64) (uint32, error) { @@ -45,9 +45,9 @@ func (m Memory) Length() uint32 { // Grow grows the memory by the given number of pages func (m Memory) Grow(numPages uint32) error { - ok := m.memory.Grow(wasmgo.Pages(numPages)) + ok := m.memory.Grow(wasmer.Pages(numPages)) if !ok { - return fmt.Errorf("%w", errCantGrowMemory) + return fmt.Errorf("%w: by %d pages", errCantGrowMemory, numPages) } return nil } diff --git a/lib/runtime/newWasmer/memory_test.go b/lib/runtime/newWasmer/memory_test.go index 6907f1a13a..6e3bfe94ec 100644 --- a/lib/runtime/newWasmer/memory_test.go +++ b/lib/runtime/newWasmer/memory_test.go @@ -1,14 +1,14 @@ // Copyright 2023 ChainSafe Systems (ON) // SPDX-License-Identifier: LGPL-3.0-only -package wasmer +package latestwasmer import ( - "github.com/stretchr/testify/assert" "math" "testing" "unsafe" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/wasmerio/wasmer-go/wasmer" )