Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fuzz: ensures wazerolib test running #2033

Merged
merged 5 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading