From 6c9303ee109c7869439b51e2f46fb8d82e38f3d7 Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Fri, 5 Jan 2024 13:31:38 +0900 Subject: [PATCH] threads: add function validation of atomics (#1898) Signed-off-by: Anuraag Agrawal --- internal/wasm/func_validation.go | 372 ++++++++ internal/wasm/func_validation_test.go | 1168 +++++++++++++++++++++++++ 2 files changed, 1540 insertions(+) diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go index e9aef72c..2174bf3c 100644 --- a/internal/wasm/func_validation.go +++ b/internal/wasm/func_validation.go @@ -86,6 +86,8 @@ func (m *Module) validateFunctionWithMaxStackValues( instName = MiscInstructionName(body[pc+1]) } else if op == OpcodeVecPrefix { instName = VectorInstructionName(body[pc+1]) + } else if op == OpcodeAtomicPrefix { + instName = AtomicInstructionName(body[pc+1]) } else { instName = InstructionName(op) } @@ -1404,6 +1406,376 @@ func (m *Module) validateFunctionWithMaxStackValues( } valueTypeStack.pushStackLimit(len(bt.Params)) pc += num + } else if op == OpcodeAtomicPrefix { + pc++ + // Atomic instructions come with two bytes where the first byte is always OpcodeAtomicPrefix, + // and the second byte determines the actual instruction. + atomicOpcode := body[pc] + // TODO: Check that CoreFeatureThreads is enabled + pc++ + + if atomicOpcode == OpcodeAtomicFence { + // No memory requirement and no arguments or return, however the immediate byte value must be 0. + imm := body[pc] + if imm != 0x0 { + return fmt.Errorf("invalid immediate value for %s", AtomicInstructionName(atomicOpcode)) + } + continue + } + + // All atomic operations except fence (checked above) require memory + if memory == nil { + return fmt.Errorf("memory must exist for %s", AtomicInstructionName(atomicOpcode)) + } + align, _, read, err := readMemArg(pc, body) + if err != nil { + return err + } + pc += read - 1 + switch atomicOpcode { + case OpcodeAtomicMemoryNotify: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicMemoryWait32: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicMemoryWait64: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Load: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI64Load: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI32Load8U: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Load32U: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI32Store: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI64Store: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI32Store8: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI32Store16: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI64Store8: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI64Store16: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI64Store32: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + case OpcodeAtomicI32RmwAdd, OpcodeAtomicI32RmwSub, OpcodeAtomicI32RmwAnd, OpcodeAtomicI32RmwOr, OpcodeAtomicI32RmwXor, OpcodeAtomicI32RmwXchg: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Rmw8AddU, OpcodeAtomicI32Rmw8SubU, OpcodeAtomicI32Rmw8AndU, OpcodeAtomicI32Rmw8OrU, OpcodeAtomicI32Rmw8XorU, OpcodeAtomicI32Rmw8XchgU: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Rmw16AddU, OpcodeAtomicI32Rmw16SubU, OpcodeAtomicI32Rmw16AndU, OpcodeAtomicI32Rmw16OrU, OpcodeAtomicI32Rmw16XorU, OpcodeAtomicI32Rmw16XchgU: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI64RmwAdd, OpcodeAtomicI64RmwSub, OpcodeAtomicI64RmwAnd, OpcodeAtomicI64RmwOr, OpcodeAtomicI64RmwXor, OpcodeAtomicI64RmwXchg: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw8AddU, OpcodeAtomicI64Rmw8SubU, OpcodeAtomicI64Rmw8AndU, OpcodeAtomicI64Rmw8OrU, OpcodeAtomicI64Rmw8XorU, OpcodeAtomicI64Rmw8XchgU: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw16AddU, OpcodeAtomicI64Rmw16SubU, OpcodeAtomicI64Rmw16AndU, OpcodeAtomicI64Rmw16OrU, OpcodeAtomicI64Rmw16XorU, OpcodeAtomicI64Rmw16XchgU: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw32AddU, OpcodeAtomicI64Rmw32SubU, OpcodeAtomicI64Rmw32AndU, OpcodeAtomicI64Rmw32OrU, OpcodeAtomicI64Rmw32XorU, OpcodeAtomicI64Rmw32XchgU: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI32RmwCmpxchg: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Rmw8CmpxchgU: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI32Rmw16CmpxchgU: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI32) + case OpcodeAtomicI64RmwCmpxchg: + if 1< 64/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw8CmpxchgU: + if 1< 1 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw16CmpxchgU: + if 1< 16/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + case OpcodeAtomicI64Rmw32CmpxchgU: + if 1< 32/8 { + return fmt.Errorf("invalid memory alignment") + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI64); err != nil { + return err + } + if err := valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil { + return err + } + valueTypeStack.push(ValueTypeI64) + default: + return fmt.Errorf("invalid atomic opcode: 0x%x", atomicOpcode) + } } else if op == OpcodeLoop { br.Reset(body[pc+1:]) bt, num, err := DecodeBlockType(m.TypeSection, br, enabledFeatures) diff --git a/internal/wasm/func_validation_test.go b/internal/wasm/func_validation_test.go index e571ad2e..2ec55e76 100644 --- a/internal/wasm/func_validation_test.go +++ b/internal/wasm/func_validation_test.go @@ -3678,3 +3678,1171 @@ func Test_SplitCallStack(t *testing.T) { }) } } + +func TestModule_funcValidation_Atomic(t *testing.T) { + t.Run("valid bytecode", func(t *testing.T) { + tests := []struct { + name string + body []byte + noDropBeforeReturn bool + }{ + { + name: "i32.atomic.load8_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load8U, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.load16_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load16U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.load", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.load8_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load8U, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.load16_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load16U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.load32_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load32U, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.load", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.store8", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store8, 0x0, 0x8, // alignment=2^0, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.store16", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store16, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.store", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store, 0x2, 0x8, // alignment=2^2, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store8", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store8, 0x0, 0x8, // alignment=2^0, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store16", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store16, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store32", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store32, 0x2, 0x8, // alignment=2^2, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store, 0x3, 0x8, // alignment=2^3, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.rmw8.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8AddU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.add", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwAdd, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8AddU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32AddU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.add", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwAdd, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8SubU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.sub", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwSub, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8SubU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32SubU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.sub", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwSub, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8AndU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.and", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwAnd, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8AndU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32AndU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.and", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwAnd, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8OrU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwOr, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8OrU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32OrU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwOr, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8XorU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.xor", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwXor, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8XorU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32XorU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.xor", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwXor, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8XchgU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.xchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwXchg, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8XchgU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32XchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.xchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwXchg, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8CmpxchgU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw.cmpxchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwCmpxchg, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8CmpxchgU, 0x0, 0x8, // alignment=2^0, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32CmpxchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw.cmpxchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwCmpxchg, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "memory.atomic.wait32", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicMemoryWait32, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "memory.atomic.wait64", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicMemoryWait64, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "memory.atomic.notify", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicMemoryNotify, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "memory.atomic.fence", + body: []byte{ + OpcodeAtomicPrefix, OpcodeAtomicFence, 0x0, + }, + noDropBeforeReturn: true, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + body := append([]byte{}, tc.body...) + if !tt.noDropBeforeReturn { + body = append(body, OpcodeDrop) + } + body = append(body, OpcodeEnd) + m := &Module{ + TypeSection: []FunctionType{v_v}, + FunctionSection: []Index{0}, + CodeSection: []Code{{Body: body}}, + } + + t.Run("with memory", func(t *testing.T) { + err := m.validateFunction(&stacks{}, api.CoreFeaturesV2, + 0, []Index{0}, nil, &Memory{}, []Table{}, nil, bytes.NewReader(nil)) + require.NoError(t, err) + }) + + t.Run("without memory", func(t *testing.T) { + err := m.validateFunction(&stacks{}, api.CoreFeaturesV2, + 0, []Index{0}, nil, nil, []Table{}, nil, bytes.NewReader(nil)) + // Only fence doesn't require memory + if tc.name == "memory.atomic.fence" { + require.NoError(t, err) + } else { + require.Error(t, err, fmt.Sprintf("memory must exist for %s", tc.name)) + } + }) + }) + } + }) + + t.Run("atomic.fence bad immediate", func(t *testing.T) { + body := []byte{ + OpcodeAtomicPrefix, OpcodeAtomicFence, 0x1, + OpcodeEnd, + } + m := &Module{ + TypeSection: []FunctionType{v_v}, + FunctionSection: []Index{0}, + CodeSection: []Code{{Body: body}}, + } + err := m.validateFunction(&stacks{}, api.CoreFeaturesV2, + 0, []Index{0}, nil, &Memory{}, []Table{}, nil, bytes.NewReader(nil)) + require.Error(t, err, "invalid immediate value for atomic.fence") + }) + + t.Run("bad alignment", func(t *testing.T) { + tests := []struct { + name string + body []byte + noDropBeforeReturn bool + }{ + { + name: "i32.atomic.load8_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load8U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.load16_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load16U, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.load", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Load, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.load8_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load8U, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.load16_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load16U, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.load32_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load32U, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.load", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI64Load, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.store8", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store8, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.store16", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store16, 0x2, 0x8, // alignment=2^2, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.store", + body: []byte{ + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x0, + OpcodeAtomicPrefix, OpcodeAtomicI32Store, 0x3, 0x8, // alignment=2^3, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store8", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store8, 0x1, 0x8, // alignment=2^1, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store16", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store16, 0x2, 0x8, // alignment=2^2, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store32", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store32, 0x3, 0x8, // alignment=2^3, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i64.atomic.store", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Store, 0x4, 0x8, // alignment=2^4, offset=8 + }, + noDropBeforeReturn: true, + }, + { + name: "i32.atomic.rmw8.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16AddU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.add", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwAdd, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8AddU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16AddU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.add_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32AddU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.add", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwAdd, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16SubU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.sub", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwSub, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8SubU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16SubU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.sub_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32SubU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.sub", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwSub, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16AndU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.and", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwAnd, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8AndU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16AndU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.and_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32AndU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.and", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwAnd, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16OrU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwOr, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8OrU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16OrU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.or_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32OrU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.or", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwOr, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16XorU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.xor", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwXor, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8XorU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16XorU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.xor_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32XorU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.xor", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwXor, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16XchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.xchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwXchg, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8XchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16XchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32XchgU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.xchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwXchg, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "i32.atomic.rmw8.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw8CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i32.atomic.rmw16.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32Rmw16CmpxchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i32.atomic.rmw.cmpxchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI32Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI32RmwCmpxchg, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw8.xchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw8CmpxchgU, 0x1, 0x8, // alignment=2^1, offset=8 + }, + }, + { + name: "i64.atomic.rmw16.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw16CmpxchgU, 0x2, 0x8, // alignment=2^2, offset=8 + }, + }, + { + name: "i64.atomic.rmw32.cmpxchg_u", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicI64Rmw32CmpxchgU, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "i64.atomic.rmw.cmpxchg", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicI64RmwCmpxchg, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "memory.atomic.wait32", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicMemoryWait32, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + { + name: "memory.atomic.wait64", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI64Const, 0x1, + OpcodeI64Const, 0x2, + OpcodeAtomicPrefix, OpcodeAtomicMemoryWait64, 0x4, 0x8, // alignment=2^4, offset=8 + }, + }, + { + name: "memory.atomic.notify", + body: []byte{ + OpcodeI32Const, 0x0, + OpcodeI32Const, 0x1, + OpcodeAtomicPrefix, OpcodeAtomicMemoryNotify, 0x3, 0x8, // alignment=2^3, offset=8 + }, + }, + } + + for _, tt := range tests { + tc := tt + t.Run(tc.name, func(t *testing.T) { + body := append([]byte{}, tc.body...) + if !tt.noDropBeforeReturn { + body = append(body, OpcodeDrop) + } + body = append(body, OpcodeEnd) + m := &Module{ + TypeSection: []FunctionType{v_v}, + FunctionSection: []Index{0}, + CodeSection: []Code{{Body: body}}, + } + err := m.validateFunction(&stacks{}, api.CoreFeaturesV2, + 0, []Index{0}, nil, &Memory{}, []Table{}, nil, bytes.NewReader(nil)) + require.Error(t, err, "invalid memory alignment") + }) + } + }) +}