From eb14b51c7e2647acb906cb2cfdee01d7308ac7de Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 Mar 2026 08:48:23 -0700 Subject: [PATCH 1/7] go --- src/wasm-interpreter.h | 3 +++ test/lit/exec/atomic.wast | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index d7dae5c59ab..6b9586b74c6 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -4213,6 +4213,9 @@ class ModuleRunnerBase : public ExpressionRunner { VISIT(timeout, curr->timeout) auto bytes = curr->expectedType.getByteSize(); auto info = getMemoryInstanceInfo(curr->memory); + if (!info.instance->wasm.getMemory(info.name)->shared) { + trap("cannot atomic.wait a non-shared memory"); + } auto memorySizeBytes = info.instance->getMemorySizeBytes(info.name); auto addr = info.instance->getFinalAddress( curr, ptr.getSingleValue(), bytes, memorySizeBytes); diff --git a/test/lit/exec/atomic.wast b/test/lit/exec/atomic.wast index 771d0c691ff..9e509e0bf06 100644 --- a/test/lit/exec/atomic.wast +++ b/test/lit/exec/atomic.wast @@ -5,17 +5,31 @@ (module (import "fuzzing-support" "log-i32" (func $log (param i32))) - (memory $0 23 256 shared) + (memory $shared 23 256 shared) + (memory $unshared 10 20) ;; CHECK: [fuzz-exec] export wait_and_log ;; CHECK-NEXT: [LoggingExternalInterface logging 2] (func $wait_and_log (export "wait_and_log") (call $log - (memory.atomic.wait64 + (memory.atomic.wait64 $shared (i32.const 0) (i64.const 0) (i64.const 0) ) ) ) + + ;; CHECK: [fuzz-exec] export wait_unshared + ;; CHECK-NEXT: [trap cannot atomic.wait a non-shared memory] + (func $wait_unshared (export "wait_unshared") + ;; Waiting on an unshared memory traps. + (drop + (memory.atomic.wait32 $unshared + (i32.const 0) + (i32.const 0) + (i64.const 0) + ) + ) + ) ) From f1faf3479262291c29a9c6824dc1a7c6029b4da7 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 Mar 2026 13:01:57 -0700 Subject: [PATCH 2/7] struct --- src/wasm-interpreter.h | 4 +++ test/lit/exec/waitqueue.wast | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 test/lit/exec/waitqueue.wast diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 6b9586b74c6..c4858b7bf6e 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2254,6 +2254,10 @@ class ExpressionRunner : public OverriddenVisitor { VISIT(expected, curr->expected) VISIT(timeout, curr->timeout) + if (!curr->ref->type.isShared()) { + trap("cannot struct.wait a non-shared object"); + } + auto data = ref.getSingleValue().getGCData(); if (!data) { trap("null ref"); diff --git a/test/lit/exec/waitqueue.wast b/test/lit/exec/waitqueue.wast new file mode 100644 index 00000000000..8db23873ec6 --- /dev/null +++ b/test/lit/exec/waitqueue.wast @@ -0,0 +1,49 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited. + +;; RUN: wasm-opt %s -all --fuzz-exec-before -q -o /dev/null 2>&1 | filecheck %s + +(module + ;; CHECK: (type $t (struct (field (mut waitqueue)))) + ;; RTRIP: (type $t (struct (field (mut waitqueue)))) + (type $shared (shared (struct (field (mut waitqueue))))) + + (type $unshared (struct (field (mut waitqueue)))) + + ;; CHECK: (func $wait (type $0) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (struct.wait $t 0 + ;; CHECK-NEXT: (global.get $g) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i64.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; RTRIP: (func $wait (type $0) + ;; RTRIP-NEXT: (drop + ;; RTRIP-NEXT: (struct.wait $t 0 + ;; RTRIP-NEXT: (global.get $g) + ;; RTRIP-NEXT: (i32.const 0) + ;; RTRIP-NEXT: (i64.const 0) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + ;; RTRIP-NEXT: ) + (func $wait-shared (param $t (ref $t)) + (drop + (struct.wait $shared 0 + (struct.new_default $shared) + (i32.const 0) + (i64.const 0) + ) + ) + ) + + (func $wait-unshared (param $t (ref $t)) + (drop + (struct.wait $unshared 0 + (struct.new_default $unshared) + (i32.const 0) + (i64.const 0) + ) + ) + ) +) From ac240db8aa432b28cf8e1b6ed65ebf1acb29c188 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 Mar 2026 13:21:52 -0700 Subject: [PATCH 3/7] go --- src/wasm-interpreter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index c4858b7bf6e..d34ad08dba3 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2254,7 +2254,7 @@ class ExpressionRunner : public OverriddenVisitor { VISIT(expected, curr->expected) VISIT(timeout, curr->timeout) - if (!curr->ref->type.isShared()) { + if (!curr->ref->type.getHeapType().isShared()) { trap("cannot struct.wait a non-shared object"); } From 2ce7bde8f89caaf7ee4d3ab47b37a37d577a2300 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 Mar 2026 13:23:57 -0700 Subject: [PATCH 4/7] go --- test/lit/exec/waitqueue.wast | 38 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/test/lit/exec/waitqueue.wast b/test/lit/exec/waitqueue.wast index 8db23873ec6..584377e6a84 100644 --- a/test/lit/exec/waitqueue.wast +++ b/test/lit/exec/waitqueue.wast @@ -3,41 +3,23 @@ ;; RUN: wasm-opt %s -all --fuzz-exec-before -q -o /dev/null 2>&1 | filecheck %s (module - ;; CHECK: (type $t (struct (field (mut waitqueue)))) - ;; RTRIP: (type $t (struct (field (mut waitqueue)))) (type $shared (shared (struct (field (mut waitqueue))))) (type $unshared (struct (field (mut waitqueue)))) - ;; CHECK: (func $wait (type $0) - ;; CHECK-NEXT: (drop - ;; CHECK-NEXT: (struct.wait $t 0 - ;; CHECK-NEXT: (global.get $g) - ;; CHECK-NEXT: (i32.const 0) - ;; CHECK-NEXT: (i64.const 0) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; CHECK-NEXT: ) - ;; RTRIP: (func $wait (type $0) - ;; RTRIP-NEXT: (drop - ;; RTRIP-NEXT: (struct.wait $t 0 - ;; RTRIP-NEXT: (global.get $g) - ;; RTRIP-NEXT: (i32.const 0) - ;; RTRIP-NEXT: (i64.const 0) - ;; RTRIP-NEXT: ) - ;; RTRIP-NEXT: ) - ;; RTRIP-NEXT: ) - (func $wait-shared (param $t (ref $t)) - (drop - (struct.wait $shared 0 - (struct.new_default $shared) - (i32.const 0) - (i64.const 0) - ) + ;; CHECK: [fuzz-exec] export wait-shared + ;; CHECK-NEXT: [fuzz-exec] note result: wait-shared => 2 + (func $wait-shared (export "wait-shared") (result i32) + (struct.wait $shared 0 + (struct.new_default $shared) + (i32.const 0) + (i64.const 0) ) ) - (func $wait-unshared (param $t (ref $t)) + ;; CHECK: [fuzz-exec] export wait-unshared + ;; CHECK-NEXT: [trap cannot struct.wait a non-shared object] + (func $wait-unshared (export "wait-unshared") (drop (struct.wait $unshared 0 (struct.new_default $unshared) From da4deef5cadcce46c4f18e8578cb9e297f754a4e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 Mar 2026 14:56:14 -0700 Subject: [PATCH 5/7] update spec test --- test/spec/waitqueue.wast | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/waitqueue.wast b/test/spec/waitqueue.wast index 2dd030204b5..4ad5e715c0c 100644 --- a/test/spec/waitqueue.wast +++ b/test/spec/waitqueue.wast @@ -76,7 +76,7 @@ ) (module - (type $t (struct (field (mut waitqueue)))) + (type $t (shared (struct (field (mut waitqueue))))) (global $g (mut (ref null $t)) (struct.new $t (i32.const 0))) From a91a452a8b4a263d3122f9c32b6fca93cb45e4df Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 Mar 2026 15:28:41 -0700 Subject: [PATCH 6/7] go all-in on spec test --- test/lit/exec/waitqueue.wast | 31 ------------------------------- test/spec/waitqueue.wast | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 31 deletions(-) delete mode 100644 test/lit/exec/waitqueue.wast diff --git a/test/lit/exec/waitqueue.wast b/test/lit/exec/waitqueue.wast deleted file mode 100644 index 584377e6a84..00000000000 --- a/test/lit/exec/waitqueue.wast +++ /dev/null @@ -1,31 +0,0 @@ -;; NOTE: Assertions have been generated by update_lit_checks.py --output=fuzz-exec and should not be edited. - -;; RUN: wasm-opt %s -all --fuzz-exec-before -q -o /dev/null 2>&1 | filecheck %s - -(module - (type $shared (shared (struct (field (mut waitqueue))))) - - (type $unshared (struct (field (mut waitqueue)))) - - ;; CHECK: [fuzz-exec] export wait-shared - ;; CHECK-NEXT: [fuzz-exec] note result: wait-shared => 2 - (func $wait-shared (export "wait-shared") (result i32) - (struct.wait $shared 0 - (struct.new_default $shared) - (i32.const 0) - (i64.const 0) - ) - ) - - ;; CHECK: [fuzz-exec] export wait-unshared - ;; CHECK-NEXT: [trap cannot struct.wait a non-shared object] - (func $wait-unshared (export "wait-unshared") - (drop - (struct.wait $unshared 0 - (struct.new_default $unshared) - (i32.const 0) - (i64.const 0) - ) - ) - ) -) diff --git a/test/spec/waitqueue.wast b/test/spec/waitqueue.wast index 4ad5e715c0c..154804145a9 100644 --- a/test/spec/waitqueue.wast +++ b/test/spec/waitqueue.wast @@ -117,3 +117,17 @@ (assert_trap (invoke "struct.wait" (i32.const 0) (i64.const 0)) "null ref") (assert_trap (invoke "struct.notify" (i32.const 0)) "null ref") + +;; Waiting on a non-shared struct traps. + +(module + (type $t (struct (field (mut waitqueue)))) + + (global $g (mut (ref null $t)) (struct.new $t (i32.const 0))) + + (func (export "struct.wait") (param $expected i32) (param $timeout i64) (result i32) + (struct.wait $t 0 (global.get $g) (local.get $expected) (local.get $timeout)) + ) +) +(assert_trap (invoke "struct.wait" (i32.const 0) (i64.const 100)) "not shared") + From 00653dd859a9159764fef7e8fd78484c316e568b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 Mar 2026 15:29:03 -0700 Subject: [PATCH 7/7] finish --- test/spec/waitqueue.wast | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/spec/waitqueue.wast b/test/spec/waitqueue.wast index 154804145a9..608ce34944b 100644 --- a/test/spec/waitqueue.wast +++ b/test/spec/waitqueue.wast @@ -118,8 +118,7 @@ (assert_trap (invoke "struct.wait" (i32.const 0) (i64.const 0)) "null ref") (assert_trap (invoke "struct.notify" (i32.const 0)) "null ref") -;; Waiting on a non-shared struct traps. - +;; Waiting on a non-shared struct should trap. (module (type $t (struct (field (mut waitqueue))))