From a653c8a8f741cc26923aa7d2bc4f866fcc9f1b82 Mon Sep 17 00:00:00 2001 From: Edoardo Vacchi Date: Tue, 17 Oct 2023 08:54:37 +0200 Subject: [PATCH] wazevo: add fuzz test cases (#1792) Signed-off-by: Edoardo Vacchi --- .../wazevo/backend/isa/arm64/lower_instr.go | 6 +- .../engine/wazevo/backend/isa/arm64/reg.go | 2 +- .../engine/wazevo/backend/regalloc/assign.go | 4 +- internal/engine/wazevo/frontend/lower.go | 3 + internal/engine/wazevo/ssa/instructions.go | 4 +- .../fuzzcases/fuzzcases_test.go | 95 +++++++++++++----- .../fuzzcases/testdata/1792.wat | 30 ++++++ .../fuzzcases/testdata/1792a.wasm | Bin 0 -> 138 bytes .../fuzzcases/testdata/1792a.wat | 36 +++++++ .../fuzzcases/testdata/1792b.wasm | Bin 0 -> 113 bytes .../fuzzcases/testdata/1792b.wat | 29 ++++++ .../fuzzcases/testdata/1792c.wasm | Bin 0 -> 137 bytes 12 files changed, 174 insertions(+), 35 deletions(-) create mode 100644 internal/integration_test/fuzzcases/testdata/1792.wat create mode 100644 internal/integration_test/fuzzcases/testdata/1792a.wasm create mode 100644 internal/integration_test/fuzzcases/testdata/1792a.wat create mode 100644 internal/integration_test/fuzzcases/testdata/1792b.wasm create mode 100644 internal/integration_test/fuzzcases/testdata/1792b.wat create mode 100644 internal/integration_test/fuzzcases/testdata/1792c.wasm diff --git a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go index 22609516..e47b0501 100644 --- a/internal/engine/wazevo/backend/isa/arm64/lower_instr.go +++ b/internal/engine/wazevo/backend/isa/arm64/lower_instr.go @@ -1497,7 +1497,7 @@ func (m *machine) lowerVFcmp(si *ssa.Instruction) { cmp.asVecRRR(vecOpFcmeq, rd, rn, rm, arr) m.insert(cmp) not := m.allocateInstr() - not.asVecMisc(vecOpNot, rd, rn, vecArrangement16B) + not.asVecMisc(vecOpNot, rd, rd, vecArrangement16B) m.insert(not) case ge: cmp := m.allocateInstr() @@ -1516,10 +1516,6 @@ func (m *machine) lowerVFcmp(si *ssa.Instruction) { cmp.asVecRRR(vecOpFcmge, rd, rm, rn, arr) // rm, rn are swapped m.insert(cmp) } - - cset := m.allocateInstr() - cset.asCSet(rd.reg(), flag) - m.insert(cset) } func (m *machine) lowerVfpuToInt(rd, rn operand, arr vecArrangement, signed bool) { diff --git a/internal/engine/wazevo/backend/isa/arm64/reg.go b/internal/engine/wazevo/backend/isa/arm64/reg.go index 81e6edc4..45737516 100644 --- a/internal/engine/wazevo/backend/isa/arm64/reg.go +++ b/internal/engine/wazevo/backend/isa/arm64/reg.go @@ -287,7 +287,7 @@ func formatVRegSized(r regalloc.VReg, size byte) (ret string) { panic("BUG: invalid register size") } default: - panic(fmt.Sprintf("BUG: invalid register type: %d fro %s", r.RegType(), r)) + panic(fmt.Sprintf("BUG: invalid register type: %d for %s", r.RegType(), r)) } } return diff --git a/internal/engine/wazevo/backend/regalloc/assign.go b/internal/engine/wazevo/backend/regalloc/assign.go index 00567ab5..8dc4c2ba 100644 --- a/internal/engine/wazevo/backend/regalloc/assign.go +++ b/internal/engine/wazevo/backend/regalloc/assign.go @@ -68,7 +68,7 @@ func (a *Allocator) assignRegistersPerInstr(f Function, pc programCounter, instr continue } if wazevoapi.RegAllocLoggingEnabled { - fmt.Printf("%s uses %d\n", instr, u.ID()) + fmt.Printf("%s uses %s(%d)\n", instr, u.RegType(), u.ID()) } n := vRegIDToNode[u.ID()] if !n.spill() { @@ -86,7 +86,7 @@ func (a *Allocator) assignRegistersPerInstr(f Function, pc programCounter, instr d := defs[0] if !d.IsRealReg() { if wazevoapi.RegAllocLoggingEnabled { - fmt.Printf("%s defines %d\n", instr, d.ID()) + fmt.Printf("%s defines %s(%d)\n", instr, d.RegType(), d.ID()) } n := vRegIDToNode[d.ID()] diff --git a/internal/engine/wazevo/frontend/lower.go b/internal/engine/wazevo/frontend/lower.go index b4e10909..a2506150 100644 --- a/internal/engine/wazevo/frontend/lower.go +++ b/internal/engine/wazevo/frontend/lower.go @@ -1692,6 +1692,9 @@ func (c *Compiler) lowerCurrentOpcode() { state.pc += 8 hi := binary.LittleEndian.Uint64(c.wasmFunctionBody[state.pc:]) state.pc += 7 + if state.unreachable { + break + } ret := builder.AllocateInstruction().AsVconst(lo, hi).Insert(builder).Return() state.push(ret) case wasm.OpcodeVecV128Load: diff --git a/internal/engine/wazevo/ssa/instructions.go b/internal/engine/wazevo/ssa/instructions.go index f7f97ce6..92727c80 100644 --- a/internal/engine/wazevo/ssa/instructions.go +++ b/internal/engine/wazevo/ssa/instructions.go @@ -828,7 +828,7 @@ var instructionReturnTypes = [opcodeEnd]returnTypesFn{ OpcodeVbitselect: returnTypesFnV128, OpcodeVanyTrue: returnTypesFnI32, OpcodeVallTrue: returnTypesFnI32, - OpcodeVhighBits: returnTypesFnV128, + OpcodeVhighBits: returnTypesFnI32, OpcodeVIadd: returnTypesFnV128, OpcodeVSaddSat: returnTypesFnV128, OpcodeVUaddSat: returnTypesFnV128, @@ -977,7 +977,7 @@ var instructionReturnTypes = [opcodeEnd]returnTypesFn{ OpcodeVFsub: returnTypesFnV128, OpcodeVFmul: returnTypesFnV128, OpcodeVFdiv: returnTypesFnV128, - OpcodeVFcmp: returnTypesFnI32, + OpcodeVFcmp: returnTypesFnV128, OpcodeVCeil: returnTypesFnV128, OpcodeVFloor: returnTypesFnV128, OpcodeVTrunc: returnTypesFnV128, diff --git a/internal/integration_test/fuzzcases/fuzzcases_test.go b/internal/integration_test/fuzzcases/fuzzcases_test.go index acb686e9..ab8283d9 100644 --- a/internal/integration_test/fuzzcases/fuzzcases_test.go +++ b/internal/integration_test/fuzzcases/fuzzcases_test.go @@ -18,11 +18,14 @@ import ( var ctx = context.Background() +// Note: the name of the test is the PR number. It may be followed by a letter +// if the PR includes more than one test (e.g. "1234a", "1234b"). +// //go:embed testdata/*.wasm var testcases embed.FS -func getWasmBinary(t *testing.T, number int) []byte { - ret, err := testcases.ReadFile(fmt.Sprintf("testdata/%d.wasm", number)) +func getWasmBinary(t *testing.T, testId string) []byte { + ret, err := testcases.ReadFile(fmt.Sprintf("testdata/%s.wasm", testId)) require.NoError(t, err) return ret } @@ -67,7 +70,7 @@ func run(t *testing.T, runner func(t *testing.T, r wazero.Runtime)) { // Test695 requires two functions to exit with "out of bounds memory access" consistently across the implementations. func Test695(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - module, err := r.Instantiate(ctx, getWasmBinary(t, 695)) + module, err := r.Instantiate(ctx, getWasmBinary(t, "695")) require.NoError(t, err) _, err = module.ExportedFunction("i8x16s").Call(ctx) @@ -82,7 +85,7 @@ func Test695(t *testing.T) { func Test696(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - module, err := r.Instantiate(ctx, getWasmBinary(t, 696)) + module, err := r.Instantiate(ctx, getWasmBinary(t, "696")) require.NoError(t, err) for _, tc := range []struct { fnName string @@ -106,7 +109,7 @@ func Test696(t *testing.T) { func Test699(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { defer r.Close(ctx) - _, err := r.Instantiate(ctx, getWasmBinary(t, 699)) + _, err := r.Instantiate(ctx, getWasmBinary(t, "699")) require.NoError(t, err) }) } @@ -114,7 +117,7 @@ func Test699(t *testing.T) { // Test701 requires two functions to exit with "out of bounds memory access" consistently across the implementations. func Test701(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - module, err := r.Instantiate(ctx, getWasmBinary(t, 701)) + module, err := r.Instantiate(ctx, getWasmBinary(t, "701")) require.NoError(t, err) _, err = module.ExportedFunction("i32.extend16_s").Call(ctx) @@ -129,14 +132,14 @@ func Test701(t *testing.T) { func Test704(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - _, err := r.Instantiate(ctx, getWasmBinary(t, 704)) + _, err := r.Instantiate(ctx, getWasmBinary(t, "704")) require.NoError(t, err) }) } func Test708(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - _, err := r.Instantiate(ctx, getWasmBinary(t, 708)) + _, err := r.Instantiate(ctx, getWasmBinary(t, "708")) require.NotNil(t, err) require.Contains(t, err.Error(), "out of bounds memory access") }) @@ -144,7 +147,7 @@ func Test708(t *testing.T) { func Test709(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 709)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "709")) require.NoError(t, err) f := mod.ExportedFunction("f64x2.promote_low_f32x4") @@ -159,7 +162,7 @@ func Test709(t *testing.T) { func Test715(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 715)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "715")) require.NoError(t, err) f := mod.ExportedFunction("select on conditional value after table.size") @@ -173,7 +176,7 @@ func Test715(t *testing.T) { func Test716(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 716)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "716")) require.NoError(t, err) f := mod.ExportedFunction("select on ref.func") @@ -187,7 +190,7 @@ func Test716(t *testing.T) { func Test717(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 717)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "717")) require.NoError(t, err) f := mod.ExportedFunction("vectors") @@ -205,7 +208,7 @@ func Test717(t *testing.T) { func Test718(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 718)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "718")) require.NoError(t, err) f := mod.ExportedFunction("v128.load_zero on the ceil") @@ -217,7 +220,7 @@ func Test718(t *testing.T) { func Test719(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 719)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "719")) require.NoError(t, err) f := mod.ExportedFunction("require unreachable") @@ -230,7 +233,7 @@ func Test719(t *testing.T) { func Test720(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 720)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "720")) require.NoError(t, err) f := mod.ExportedFunction("access memory after table.grow") @@ -243,7 +246,7 @@ func Test720(t *testing.T) { func Test721(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 721)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "721")) require.NoError(t, err) f := mod.ExportedFunction("conditional before elem.drop") @@ -257,7 +260,7 @@ func Test721(t *testing.T) { func Test722(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 722)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "722")) require.NoError(t, err) f := mod.ExportedFunction("conditional before data.drop") @@ -272,7 +275,7 @@ func Test722(t *testing.T) { func Test725(t *testing.T) { functions := []string{"i32.load8_s", "i32.load16_s"} run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 725)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "725")) require.NoError(t, err) for _, fn := range functions { @@ -303,7 +306,7 @@ func Test730(t *testing.T) { } run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 730)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "730")) require.NoError(t, err) for _, tc := range tests { @@ -320,7 +323,7 @@ func Test730(t *testing.T) { func Test733(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 733)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "733")) require.NoError(t, err) name := "out of bounds" @@ -356,14 +359,14 @@ func Test733(t *testing.T) { func Test873(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - _, err := r.Instantiate(ctx, getWasmBinary(t, 873)) + _, err := r.Instantiate(ctx, getWasmBinary(t, "873")) require.NoError(t, err) }) } func Test874(t *testing.T) { run(t, func(t *testing.T, r wazero.Runtime) { - _, err := r.Instantiate(ctx, getWasmBinary(t, 874)) + _, err := r.Instantiate(ctx, getWasmBinary(t, "874")) require.NoError(t, err) }) } @@ -395,7 +398,7 @@ func Test888(t *testing.T) { _, err := r.InstantiateWithConfig(ctx, imported, wazero.NewModuleConfig().WithName("host")) require.NoError(t, err) - _, err = r.InstantiateWithConfig(ctx, getWasmBinary(t, 888), + _, err = r.InstantiateWithConfig(ctx, getWasmBinary(t, "888"), wazero.NewModuleConfig().WithName("test")) require.NoError(t, err) }) @@ -408,7 +411,7 @@ func Test1054(t *testing.T) { modules := make([]api.Module, 0, 2) run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 1054)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "1054")) require.NoError(t, err) modules = append(modules, mod) }) @@ -428,7 +431,7 @@ func Test1777(t *testing.T) { } run(t, func(t *testing.T, r wazero.Runtime) { - mod, err := r.Instantiate(ctx, getWasmBinary(t, 1777)) + mod, err := r.Instantiate(ctx, getWasmBinary(t, "1777")) require.NoError(t, err) f := mod.ExportedFunction("") require.NotNil(t, f) @@ -437,3 +440,45 @@ func Test1777(t *testing.T) { require.Equal(t, []uint64{18446626425965379583, 4607736361554183979}, res) }) } + +// Test1792a tests that v128.const i32x4 is not skipped when state is unreachable. +// This test fails at build-time. +func Test1792a(t *testing.T) { + if !platform.CompilerSupported() { + return + } + run(t, func(t *testing.T, r wazero.Runtime) { + _, err := r.Instantiate(ctx, getWasmBinary(t, "1792a")) + require.NoError(t, err) + }) +} + +// Test1792b tests that OpcodeVhighBits (v128.Bitmask) is typed as V128. +// This test fails at build-time. +func Test1792b(t *testing.T) { + if !platform.CompilerSupported() { + return + } + run(t, func(t *testing.T, r wazero.Runtime) { + _, err := r.Instantiate(ctx, getWasmBinary(t, "1792b")) + require.NoError(t, err) + }) +} + +// Test1792c tests that OpcodeVFcmp (f32x4.eq) is typed as V128. +func Test1792c(t *testing.T) { + if !platform.CompilerSupported() { + return + } + run(t, func(t *testing.T, r wazero.Runtime) { + mod, err := r.Instantiate(ctx, getWasmBinary(t, "1792c")) + require.NoError(t, err) + f := mod.ExportedFunction("") + require.NotNil(t, f) + _, err = f.Call(ctx, 0, 0, 0) + require.NoError(t, err) + m := mod.(*wasm.ModuleInstance) + require.Equal(t, uint64(5044022786561933312), m.Globals[0].Val) + require.Equal(t, uint64(9205357640488583168), m.Globals[0].ValHi) + }) +} diff --git a/internal/integration_test/fuzzcases/testdata/1792.wat b/internal/integration_test/fuzzcases/testdata/1792.wat new file mode 100644 index 00000000..2e4e779f --- /dev/null +++ b/internal/integration_test/fuzzcases/testdata/1792.wat @@ -0,0 +1,30 @@ +(module + (type (;0;) (func (param funcref funcref f32))) + (func (;0;) (type 0) (param funcref funcref f32) + (local v128) + global.get 1 + i32.eqz + if ;; label = @1 + unreachable + end + global.get 1 + i32.const 1 + i32.sub + global.set 1 + v128.const i32x4 0x0145ff40 0x45ffffff 0xffff4545 0xffffffff + f32x4.trunc + local.tee 3 + v128.const i32x4 0x7fc00000 0x7fc00000 0x7fc00000 0x7fc00000 + local.get 3 + local.get 3 + f32x4.eq + v128.bitselect + global.get 0 + v128.xor + global.set 0 + ) + (global (;0;) (mut v128) v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (global (;1;) (mut i32) i32.const 1000) + (export "" (func 0)) + (export "1" (global 0)) +) \ No newline at end of file diff --git a/internal/integration_test/fuzzcases/testdata/1792a.wasm b/internal/integration_test/fuzzcases/testdata/1792a.wasm new file mode 100644 index 0000000000000000000000000000000000000000..93656dcf7ea762b47aafb6b7ec445325cdc8b28c GIT binary patch literal 138 zcmY+7F$#n*6hvo!f*SnL7z23m`-V5zXz%_St7GOwHmZ!m8w0kGjD z#{nmVSN@nZY4enwXM5`k`*Tcc+BpwMQ3PKOk2ePdetd)lj$C@e#d^4F5(_!!@(To< H-yUWc;3pH` literal 0 HcmV?d00001 diff --git a/internal/integration_test/fuzzcases/testdata/1792a.wat b/internal/integration_test/fuzzcases/testdata/1792a.wat new file mode 100644 index 00000000..cf6bbfc5 --- /dev/null +++ b/internal/integration_test/fuzzcases/testdata/1792a.wat @@ -0,0 +1,36 @@ +(module + (type (;0;) (func (param f64 f64))) + (func (;0;) (type 0) (param f64 f64) + global.get 1 + i32.eqz + if ;; label = @1 + unreachable + end + global.get 1 + i32.const 1 + i32.sub + global.set 1 + unreachable + loop ;; label = @1 + global.get 1 + i32.eqz + if ;; label = @2 + unreachable + end + global.get 1 + i32.const 1 + i32.sub + global.set 1 + v128.const i32x4 0x40400200 0xff404040 0x0001ffff 0x00000000 + global.get 0 + v128.xor + global.set 0 + end + ) + (table (;0;) 1000 1000 funcref) + (global (;0;) (mut v128) v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000) + (global (;1;) (mut i32) i32.const 1000) + (export "\00\00\02\00" (func 0)) + (export "" (table 0)) + (export "2" (global 0)) +) \ No newline at end of file diff --git a/internal/integration_test/fuzzcases/testdata/1792b.wasm b/internal/integration_test/fuzzcases/testdata/1792b.wasm new file mode 100644 index 0000000000000000000000000000000000000000..81a04722c2dd507860926a90b9158eb06e7e585d GIT binary patch literal 113 zcmZQbEY4+QU|?WmWlUgTtYKziWME}sWaQ#v<6)|2bY$QL5--@f*?E{jiVT?;7>$@2 zxU3j07#Zu88C_W%7`T-g9T~G#7%$}i<@x`A J%fR5k4FFiK9S;Bi literal 0 HcmV?d00001 diff --git a/internal/integration_test/fuzzcases/testdata/1792b.wat b/internal/integration_test/fuzzcases/testdata/1792b.wat new file mode 100644 index 00000000..9225d397 --- /dev/null +++ b/internal/integration_test/fuzzcases/testdata/1792b.wat @@ -0,0 +1,29 @@ +(module + (type (;0;) (func (result f64))) + (func (;0;) (type 0) (result f64) + (local i32) + global.get 1 + i32.eqz + if ;; label = @1 + unreachable + end + global.get 1 + i32.const 1 + i32.sub + global.set 1 + ref.null extern + v128.const i32x4 0xb1b1ffff 0xb1b1b0b1 0xb1b1b1b1 0xffffffb1 + i32x4.bitmask + global.get 0 + i32.xor + global.set 0 + drop + f64.const 0x1.000ffffff0914p+1 (;=2.0004882812219282;) + ) + (memory (;0;) 10 10) + (global (;0;) (mut i32) i32.const 0) + (global (;1;) (mut i32) i32.const 1000) + (export "" (func 0)) + (export "1" (memory 0)) + (export "2" (global 0)) +) \ No newline at end of file diff --git a/internal/integration_test/fuzzcases/testdata/1792c.wasm b/internal/integration_test/fuzzcases/testdata/1792c.wasm new file mode 100644 index 0000000000000000000000000000000000000000..ff3fb515e19b4c8093750881437cf513176ef9e3 GIT binary patch literal 137 zcmZQbEY4+QU|?WmXG~x&D5zy%W@2PulVPf6{L2FrMgiRQjE*nZx!F0GfWnN1%nV#! zj2?`P)yj;nEDj9Z%8ZVT*(yMN4*y*l|NsB*>goz4f#7eZ5;MrO1NF#Qfmwn1ujAh! LWrn|jDh%8J&iNtc literal 0 HcmV?d00001