Skip to content

Commit

Permalink
fuzz: ensures wazerolib test running (#2033)
Browse files Browse the repository at this point in the history
Signed-off-by: Takeshi Yoneda <[email protected]>
  • Loading branch information
mathetake authored Feb 9, 2024
1 parent abf0ada commit 939f404
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 145 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ build.spectest.threads:
test:
@go test $(go_test_options) $$(go list ./... | grep -vE '$(spectest_v1_dir)|$(spectest_v2_dir)')
@cd internal/version/testdata && go test $(go_test_options) ./...
@cd internal/integration_test/fuzz/wazerolib && CGO_ENABLED=0 WASM_BINARY_PATH=testdata/test.wasm go test ./...

.PHONY: coverage
# replace spaces with commas
Expand Down
2 changes: 1 addition & 1 deletion internal/integration_test/fuzz/predicate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ echo "Testing $WASM"
export WASM_BINARY_PATH=$WASM

# Run the test and reverse the exit code so that a non-zero exit code indicates interesting case.
./nodiff.test
./nodiff.test -test.run=TestReRunFailedRequireNoDiffCase
exit $((! $?))
140 changes: 140 additions & 0 deletions internal/integration_test/fuzz/wazerolib/extern.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package main

import "C"
import (
"context"
"math"
"reflect"
"strings"
"unsafe"

"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/experimental/opt"
"github.com/tetratelabs/wazero/internal/leb128"
"github.com/tetratelabs/wazero/internal/testing/binaryencoding"
"github.com/tetratelabs/wazero/internal/wasm"
)

func main() {}

// require_no_diff ensures that the behavior is the same between the compiler and the interpreter for any given binary.
// And if there's diff, this also saves the problematic binary and wat into testdata directory.
//
//export require_no_diff
func require_no_diff(binaryPtr uintptr, binarySize int, checkMemory bool) {
// TODO: use unsafe.Slice after flooring Go 1.20.
var wasmBin []byte
wasmHdr := (*reflect.SliceHeader)(unsafe.Pointer(&wasmBin))
wasmHdr.Data = binaryPtr
wasmHdr.Len = binarySize
wasmHdr.Cap = binarySize

failed := true
defer func() {
if failed {
// If the test fails, we save the binary and wat into testdata directory.
saveFailedBinary(wasmBin, "TestReRunFailedRequireNoDiffCase")
}
}()

requireNoDiff(wasmBin, checkMemory, func(err error) {
if err != nil {
panic(err)
}
})

failed = false
}

// validate accepts maybe-invalid Wasm module bytes and ensures that our validation phase works correctly
// as well as the compiler doesn't panic during compilation!
//
//export validate
func validate(binaryPtr uintptr, binarySize int) {
// TODO: use unsafe.Slice after flooring Go 1.20.
var wasmBin []byte
wasmHdr := (*reflect.SliceHeader)(unsafe.Pointer(&wasmBin))
wasmHdr.Data = binaryPtr
wasmHdr.Len = binarySize
wasmHdr.Cap = binarySize

failed := true
defer func() {
if failed {
// If the test fails, we save the binary and wat into testdata directory.
saveFailedBinary(wasmBin, "TestReRunFailedValidateCase")
}
}()

tryCompile(wasmBin)
failed = false
}

//export test_signal_stack
func test_signal_stack() {
// (module
// (func (export "long_loop")
// (loop
// global.get 0
// i32.eqz
// if ;; label = @1
// unreachable
// end
// global.get 0
// i32.const 1
// i32.sub
// global.set 0
// br 0
// )
// )
// (global (;0;) (mut i32) i32.const 2147483648)
// )
bin := binaryencoding.EncodeModule(&wasm.Module{
TypeSection: []wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
GlobalSection: []wasm.Global{{
Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true},
Init: wasm.ConstantExpression{
Opcode: wasm.OpcodeI32Const,
Data: leb128.EncodeInt32(math.MaxInt32),
},
}},
ExportSection: []wasm.Export{{Type: wasm.ExternTypeFunc, Name: "long_loop", Index: 0}},
CodeSection: []wasm.Code{
{
Body: []byte{
wasm.OpcodeLoop, 0,
wasm.OpcodeGlobalGet, 0,
wasm.OpcodeI32Eqz,
wasm.OpcodeIf, 0,
wasm.OpcodeUnreachable,
wasm.OpcodeEnd,
wasm.OpcodeGlobalGet, 0,
wasm.OpcodeI32Const, 1,
wasm.OpcodeI32Sub,
wasm.OpcodeGlobalSet, 0,
wasm.OpcodeBr, 0,
wasm.OpcodeEnd,
wasm.OpcodeEnd,
},
},
},
})
ctx := context.Background()
config := opt.NewRuntimeConfigOptimizingCompiler()
r := wazero.NewRuntimeWithConfig(ctx, config)
module, err := r.Instantiate(ctx, bin)
if err != nil {
panic(err)
}
defer func() {
if err = module.Close(ctx); err != nil {
panic(err)
}
}()

_, err = module.ExportedFunction("long_loop").Call(ctx)
if !strings.Contains(err.Error(), "unreachable") {
panic("long_loop should be unreachable")
}
}
78 changes: 0 additions & 78 deletions internal/integration_test/fuzz/wazerolib/lib.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
package main

import "C"
import (
"context"
"crypto/sha256"
_ "embed"
"encoding/hex"
"fmt"
"math"
"os"
"path"
"strings"

"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/experimental/opt"
"github.com/tetratelabs/wazero/internal/leb128"
"github.com/tetratelabs/wazero/internal/testing/binaryencoding"
"github.com/tetratelabs/wazero/internal/wasm"
)

func main() {}

const failedCasesDir = "wazerolib/testdata"

// saveFailedBinary writes binary and wat into failedCasesDir so that it is easy to reproduce the error.
Expand Down Expand Up @@ -62,72 +53,3 @@ func newCompilerConfig() wazero.RuntimeConfig {
}
return c
}

//export test_signal_stack
func test_signal_stack() {
// (module
// (func (export "long_loop")
// (loop
// global.get 0
// i32.eqz
// if ;; label = @1
// unreachable
// end
// global.get 0
// i32.const 1
// i32.sub
// global.set 0
// br 0
// )
// )
// (global (;0;) (mut i32) i32.const 2147483648)
// )
bin := binaryencoding.EncodeModule(&wasm.Module{
TypeSection: []wasm.FunctionType{{}},
FunctionSection: []wasm.Index{0},
GlobalSection: []wasm.Global{{
Type: wasm.GlobalType{ValType: wasm.ValueTypeI32, Mutable: true},
Init: wasm.ConstantExpression{
Opcode: wasm.OpcodeI32Const,
Data: leb128.EncodeInt32(math.MaxInt32),
},
}},
ExportSection: []wasm.Export{{Type: wasm.ExternTypeFunc, Name: "long_loop", Index: 0}},
CodeSection: []wasm.Code{
{
Body: []byte{
wasm.OpcodeLoop, 0,
wasm.OpcodeGlobalGet, 0,
wasm.OpcodeI32Eqz,
wasm.OpcodeIf, 0,
wasm.OpcodeUnreachable,
wasm.OpcodeEnd,
wasm.OpcodeGlobalGet, 0,
wasm.OpcodeI32Const, 1,
wasm.OpcodeI32Sub,
wasm.OpcodeGlobalSet, 0,
wasm.OpcodeBr, 0,
wasm.OpcodeEnd,
wasm.OpcodeEnd,
},
},
},
})
ctx := context.Background()
config := opt.NewRuntimeConfigOptimizingCompiler()
r := wazero.NewRuntimeWithConfig(ctx, config)
module, err := r.Instantiate(ctx, bin)
if err != nil {
panic(err)
}
defer func() {
if err = module.Close(ctx); err != nil {
panic(err)
}
}()

_, err = module.ExportedFunction("long_loop").Call(ctx)
if !strings.Contains(err.Error(), "unreachable") {
panic("long_loop should be unreachable")
}
}
39 changes: 5 additions & 34 deletions internal/integration_test/fuzz/wazerolib/nodiff.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package main

import "C"
import (
"bytes"
"context"
"errors"
"fmt"
"reflect"
"sort"
"strings"
"unsafe"
Expand All @@ -18,35 +16,6 @@ import (
"github.com/tetratelabs/wazero/internal/wasm"
)

// require_no_diff ensures that the behavior is the same between the compiler and the interpreter for any given binary.
// And if there's diff, this also saves the problematic binary and wat into testdata directory.
//
//export require_no_diff
func require_no_diff(binaryPtr uintptr, binarySize int, checkMemory bool) {
// TODO: use unsafe.Slice after flooring Go 1.20.
var wasmBin []byte
wasmHdr := (*reflect.SliceHeader)(unsafe.Pointer(&wasmBin))
wasmHdr.Data = binaryPtr
wasmHdr.Len = binarySize
wasmHdr.Cap = binarySize

failed := true
defer func() {
if failed {
// If the test fails, we save the binary and wat into testdata directory.
saveFailedBinary(wasmBin, "TestReRunFailedRequireNoDiffCase")
}
}()

requireNoDiff(wasmBin, checkMemory, func(err error) {
if err != nil {
panic(err)
}
})

failed = false
}

// We haven't had public APIs for referencing all the imported entries from wazero.CompiledModule,
// so we use the unsafe.Pointer and the internal memory layout to get the internal *wasm.Module
// from wazero.CompiledFunction. This must be synced with the struct definition of wazero.compiledModule (internal one).
Expand Down Expand Up @@ -154,10 +123,12 @@ func ensureMutableGlobalsMatch(compilerMod, interpreterMod api.Module, requireNo
}

if !ok {
if ig.Type.ValType == wasm.ValueTypeV128 {
es = append(es, fmt.Sprintf("mutable global[%d] value mismatch: (%v,%v) != (%v,%v)", i, cVal, cValHi, iVal, iValHi))
if typ := ig.Type.ValType; typ == wasm.ValueTypeV128 {
es = append(es, fmt.Sprintf("\t[%d] %s: (%v,%v) != (%v,%v)",
i, wasm.ValueTypeName(wasm.ValueTypeV128), cVal, cValHi, iVal, iValHi))
} else {
es = append(es, fmt.Sprintf("mutable global[%d] value mismatch: %v != %v", i, cVal, iVal))
es = append(es, fmt.Sprintf("\t[%d] %s: %v != %v",
i, wasm.ValueTypeName(typ), cVal, iVal))
}
}
}
Expand Down
15 changes: 10 additions & 5 deletions internal/integration_test/fuzz/wazerolib/nodiff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ func Test_ensureMutableGlobalsMatch(t *testing.T) {
{Val: 11, Type: wasm.GlobalType{Mutable: true, ValType: wasm.ValueTypeI32}},
},
},
expErr: "mutable global[1] value mismatch: 10 != 11",
expErr: `mutable globals mismatch:
[1] i32: 10 != 11`,
},
{
name: "i64 match",
Expand Down Expand Up @@ -93,7 +94,8 @@ func Test_ensureMutableGlobalsMatch(t *testing.T) {
{Val: 1 << 63, Type: wasm.GlobalType{Mutable: true, ValType: wasm.ValueTypeI64}},
},
},
expErr: "mutable global[2] value mismatch: 4611686018427387904 != 9223372036854775808",
expErr: `mutable globals mismatch:
[2] i64: 4611686018427387904 != 9223372036854775808`,
},
{
name: "f32 match",
Expand Down Expand Up @@ -124,7 +126,8 @@ func Test_ensureMutableGlobalsMatch(t *testing.T) {
{Val: 11, Type: wasm.GlobalType{Mutable: true, ValType: wasm.ValueTypeF32}},
},
},
expErr: "mutable global[1] value mismatch: 10 != 11",
expErr: `mutable globals mismatch:
[1] f32: 10 != 11`,
},
{
name: "f64 match",
Expand Down Expand Up @@ -157,7 +160,8 @@ func Test_ensureMutableGlobalsMatch(t *testing.T) {
{Val: 1 << 63, Type: wasm.GlobalType{Mutable: true, ValType: wasm.ValueTypeF64}},
},
},
expErr: "mutable global[2] value mismatch: 4611686018427387904 != 9223372036854775808",
expErr: `mutable globals mismatch:
[2] f64: 4611686018427387904 != 9223372036854775808`,
},

{
Expand Down Expand Up @@ -191,7 +195,8 @@ func Test_ensureMutableGlobalsMatch(t *testing.T) {
{Val: 1 << 62, ValHi: 1234, Type: wasm.GlobalType{Mutable: true, ValType: wasm.ValueTypeV128}},
},
},
expErr: "mutable global[2] value mismatch: (4611686018427387904,0) != (4611686018427387904,1234)",
expErr: `mutable globals mismatch:
[2] v128: (4611686018427387904,0) != (4611686018427387904,1234)`,
},
} {
t.Run(tc.name, func(t *testing.T) {
Expand Down
Empty file.
Binary file not shown.
Loading

0 comments on commit 939f404

Please sign in to comment.