Skip to content

Commit 1b805bb

Browse files
authored
wasmparser: require result equivalence for (on $t switch) handler (#2564)
The `resume`/`resume_throw` handler clause `(on $tag switch)` was accepted when the switch tag's result types were merely a *subtype* of the resumed continuation's results. This is unsound, and does not follow the spec: the handler judgment has no subsumption rule, so `(on tu switch) : t*` together with the required `hdl : t2*` forces `t* = t2*`. The two result vectors must be equivalent, not just related by subtyping. V8 and Binaryen already check for equivalence.
1 parent bf2ad79 commit 1b805bb

4 files changed

Lines changed: 46 additions & 7 deletions

File tree

crates/wasmparser/src/validator/operators.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1911,10 +1911,21 @@ where
19111911
}
19121912
Handle::OnSwitch { tag } => {
19131913
let tag_ty = self.tag_at(tag)?;
1914-
if !self.is_func_subtype(
1914+
// The tag's type must be *equivalent* to (not merely a
1915+
// subtype of) `[] -> [old results]`: the handler judgment
1916+
// has no subsumption rule, so `(on tu switch) : t*` together
1917+
// with the required `hdl : t2*` forces `t* = t2*`. Checking
1918+
// subtyping in both directions gives equivalence, and also
1919+
// pins the tag to zero parameters (via the param-length
1920+
// check inside `is_func_subtype`).
1921+
let tag_matches = self.is_func_subtype(
19151922
(tag_ty.params(), tag_ty.results()),
19161923
(&[], old_func_ty.results()),
1917-
) {
1924+
) && self.is_func_subtype(
1925+
(&[], old_func_ty.results()),
1926+
(tag_ty.params(), tag_ty.results()),
1927+
);
1928+
if !tag_matches {
19181929
bail!(
19191930
self.offset,
19201931
"type mismatch: switch tag does not match continuation"

tests/cli/stack-switching/validate-switch-label.wast

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,32 @@
8383
)
8484
"type mismatch: switch tag does not match continuation")
8585

86+
;; The switch tag's results must be *equivalent* to the continuation's results,
87+
;; not merely a subtype. Here the tag results `[nullfuncref]` are a strict
88+
;; subtype of the continuation results `[funcref]`, so this must be rejected.
89+
(assert_invalid
90+
(module
91+
(type $ft (func (result funcref)))
92+
(type $ct (cont $ft))
93+
94+
(type $tag_ft (func (result nullfuncref)))
95+
(tag $t (type $tag_ft))
96+
97+
(func
98+
block $label
99+
(resume $ct (on $t switch) (ref.null $ct))
100+
unreachable
101+
end
102+
)
103+
)
104+
"type mismatch: switch tag does not match continuation")
105+
106+
;; Equivalent reference-type results (both `[funcref]`) are accepted.
86107
(module
87108
(type $ft (func (result funcref)))
88109
(type $ct (cont $ft))
89110

90-
(type $tag_ft (func (result nullfuncref)))
111+
(type $tag_ft (func (result funcref)))
91112
(tag $t (type $tag_ft))
92113

93114
(func

tests/snapshots/cli/stack-switching/validate-switch-label.wast.json

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,16 @@
3636
"text": "type mismatch: switch tag does not match continuation"
3737
},
3838
{
39-
"type": "module",
40-
"line": 86,
39+
"type": "assert_invalid",
40+
"line": 90,
4141
"filename": "validate-switch-label.5.wasm",
42+
"module_type": "binary",
43+
"text": "type mismatch: switch tag does not match continuation"
44+
},
45+
{
46+
"type": "module",
47+
"line": 107,
48+
"filename": "validate-switch-label.6.wasm",
4249
"module_type": "binary"
4350
}
4451
]

tests/snapshots/cli/stack-switching/validate-switch-label.wast/5.print renamed to tests/snapshots/cli/stack-switching/validate-switch-label.wast/6.print

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
(module
22
(type $ft (;0;) (func (result funcref)))
33
(type $ct (;1;) (cont $ft))
4-
(type $tag_ft (;2;) (func (result nullfuncref)))
4+
(type $tag_ft (;2;) (func (result funcref)))
55
(type (;3;) (func))
6-
(tag $t (;0;) (type $tag_ft) (result nullfuncref))
6+
(tag $t (;0;) (type $tag_ft) (result funcref))
77
(func (;0;) (type 3)
88
block $label
99
ref.null $ct

0 commit comments

Comments
 (0)