Skip to content

Commit 8b8809d

Browse files
authored
Fix Wasm GC validation/generation of field types' mutability and subtyping (#1934)
* wasmparser: Fix Wasm GC field validation with regards to mutability We previously incorrectly treated the following invalid cases as valid: 1. mismatched mutable and immutable fields (mutability must match exactly) 2. mutable references to subtypes (mutable reference fields must be invariant) * wasm-smith: fix generation of matching field types Ensure that the mutability always matches and that mutable fields are invariant with respect to the field's storage type.
1 parent fc0b918 commit 8b8809d

File tree

9 files changed

+307
-162
lines changed

9 files changed

+307
-162
lines changed

crates/wasm-smith/src/core.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -756,10 +756,14 @@ impl Module {
756756
u: &mut Unstructured,
757757
ty: FieldType,
758758
) -> Result<FieldType> {
759-
Ok(FieldType {
760-
element_type: self.arbitrary_matching_storage_type(u, ty.element_type)?,
761-
mutable: if ty.mutable { u.arbitrary()? } else { false },
762-
})
759+
if ty.mutable {
760+
Ok(ty)
761+
} else {
762+
Ok(FieldType {
763+
element_type: self.arbitrary_matching_storage_type(u, ty.element_type)?,
764+
mutable: false,
765+
})
766+
}
763767
}
764768

765769
fn arbitrary_matching_storage_type(

crates/wasmparser/src/readers/core/types/matches.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,28 @@ impl<'a> Matches for WithRecGroup<&'a StructType> {
233233

234234
impl Matches for WithRecGroup<FieldType> {
235235
fn matches(types: &TypeList, a: Self, b: Self) -> bool {
236-
(b.mutable || !a.mutable)
236+
// `a`'s storage type must match `b`'s storage type.
237+
if !Matches::matches(
238+
types,
239+
WithRecGroup::map(a, |a| a.element_type),
240+
WithRecGroup::map(b, |b| b.element_type),
241+
) {
242+
return false;
243+
}
244+
245+
// And either both fields are immutable...
246+
if !a.mutable && !b.mutable {
247+
return true;
248+
}
249+
250+
// Or both fields are mutable and `b`'s storage type must match `a`'s
251+
// storage type, a.k.a. they must be the exact same storage type.
252+
a.mutable
253+
&& b.mutable
237254
&& Matches::matches(
238255
types,
239-
WithRecGroup::map(a, |a| a.element_type),
240256
WithRecGroup::map(b, |b| b.element_type),
257+
WithRecGroup::map(a, |a| a.element_type),
241258
)
242259
}
243260
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
;; --enable-gc
2+
3+
(module
4+
;; When fields are mutable, a subtype's reference fields must be the exact
5+
;; same the supertype's fields (i.e. are invariant).
6+
(type $a (sub (struct (field (mut (ref null any))))))
7+
(type $b (sub $a (struct (field (mut (ref null any))))))
8+
9+
;; When fields are non-mutable, a subtype's reference fields can be subtypes
10+
;; of the supertype's fields (i.e. are covariant).
11+
(type $c (sub (struct (field (ref null any)))))
12+
(type $d (sub $c (struct (field (ref null none)))))
13+
)

tests/local/gc/gc-subtypes.wat

+98-83
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,120 @@
11
;; --enable-gc
22

33
(module
4-
(type $a (sub (func)))
5-
(type $b (sub $a (func)))
6-
(type $c (sub $b (func)))
7-
(type $b1 (sub final $a (func)))
4+
;; nullability matching for field types
5+
(type $non_null (sub (array (ref null any))))
6+
(type $nullable (sub $non_null (array (ref any))))
87

9-
;; struct, ref types, mutability, nullability
10-
(type $d (sub (struct)))
11-
(type $e (sub $d (struct (field (mut (ref null $d)))))) ;; width
12-
(type $f (sub final $e (struct (field (ref $e))))) ;; depth
8+
;; struct width and depth subtyping
9+
(type $struct_base (sub (struct)))
10+
(type $struct_width (sub $struct_base (struct (field (ref null $struct_base)))))
11+
(type $struct_depth (sub $struct_width (struct (field (ref $struct_base)))))
1312

14-
;; func
15-
(type $g (sub (func (param (ref $e)) (result (ref $e)))))
16-
(type $h (sub $g (func (param (ref $d)) (result (ref $f)))))
17-
18-
(type $j (sub (func (param (ref $b)) (result (ref $b)))))
19-
(type $k (sub $j (func (param (ref $a)) (result (ref $c)))))
20-
21-
;; valid: expanded param/result types are equal to those of the parent
22-
(type $l (sub $j (func (param (ref $b)) (result (ref $b)))))
23-
24-
;; array, val types, ref types, mutability, nullability
25-
(type $m (sub (array (mut i32))))
26-
(type $n (sub $m (array i32)))
27-
28-
(type $o (sub (array i32)))
29-
(type $p (sub $o (array i32)))
30-
31-
(type $o1 (sub (array i64)))
32-
(type $p1 (sub $o1 (array i64)))
13+
;; function param and result subtyping
14+
(type $func_base (sub (func (param eqref) (result eqref))))
15+
(type $func_contravariant_params (sub $func_base (func (param anyref) (result eqref))))
16+
(type $func_covariant_results (sub $func_base (func (param eqref) (result nullref))))
3317

3418
;; any refs
35-
(type $q (sub (array (mut anyref))))
36-
(type $q0 (sub $q (array (ref any))))
19+
(type $any (sub (array anyref)))
20+
(type $any_2 (sub $any (array anyref)))
21+
(type $any_mut (sub (array (mut anyref))))
22+
(type $any_mut_2 (sub $any_mut (array (mut anyref))))
3723

3824
;; eq refs
39-
(type $q1 (sub $q (array (mut eqref))))
40-
(type $q2 (sub $q1 (array (mut (ref eq)))))
41-
(type $q3 (sub $q2 (array (ref eq))))
25+
(type $eq (sub $any (array eqref)))
26+
(type $eq_2 (sub $eq (array eqref)))
27+
(type $eq_mut (sub (array (mut eqref))))
28+
(type $eq_mut_2 (sub $eq_mut (array (mut eqref))))
4229

4330
;; i31 refs
44-
(type $r (sub $q (array i31ref)))
45-
(type $r1 (sub $q1 (array i31ref)))
46-
(type $s (sub $r (array (ref i31))))
47-
(type $s1 (sub $q1 (array (ref i31))))
48-
(type $s2 (sub $q2 (array (ref i31))))
31+
(type $i31 (sub $eq (array i31ref)))
32+
(type $i31_2 (sub $i31 (array i31ref)))
33+
(type $i31_mut (sub (array (mut i31ref))))
34+
(type $i31_mut_2 (sub $i31_mut (array (mut i31ref))))
4935

5036
;; array refs
51-
(type $rr (sub $q (array arrayref)))
52-
(type $rr1 (sub $q1 (array arrayref)))
53-
(type $ss (sub $rr (array (ref array))))
54-
(type $ss0 (sub $ss (array (ref $rr))))
55-
(type $ss1 (sub $q1 (array (ref array))))
56-
(type (sub $q1 (array (ref $rr))))
57-
(type $ss2 (sub $q2 (array (ref array))))
58-
(type (sub $q2 (array (ref $rr))))
37+
(type $array (sub $eq (array arrayref)))
38+
(type $array_2 (sub $array (array arrayref)))
39+
(type $array_mut (sub (array (mut arrayref))))
40+
(type $array_mut_2 (sub $array_mut (array (mut arrayref))))
41+
42+
;; concrete array refs
43+
(type $concrete_array (sub $array (array (ref null $array))))
44+
(type $concrete_array_2 (sub $concrete_array (array (ref null $array))))
45+
(type $concrete_array_mut (sub (array (ref null $array_mut))))
46+
(type $concrete_array_mut_2 (sub $concrete_array_mut (array (ref null $array_mut))))
5947

6048
;; struct refs
61-
(type $rrr (sub $q (array structref)))
62-
(type $rrr1 (sub $q1 (array structref)))
63-
(type $sss (sub $rrr (array (ref struct))))
64-
(type $sss0 (sub $rrr (array (ref null $d))))
65-
(type $sss1 (sub $q1 (array (ref struct))))
66-
(type (sub $q1 (array (ref $d))))
67-
(type $sss2 (sub $q2 (array (ref struct))))
68-
(type (sub $q2 (array (ref $d))))
49+
(type $struct (sub $eq (array structref)))
50+
(type $struct_2 (sub $struct (array structref)))
51+
(type $struct_mut (sub (array (mut structref))))
52+
(type $struct_mut_2 (sub $struct_mut (array (mut structref))))
53+
54+
;; concrete struct refs
55+
(type $my_struct (struct))
56+
(type $concrete_struct (sub $struct (array (ref null $my_struct))))
57+
(type $concrete_struct_2 (sub $concrete_struct (array (ref null $my_struct))))
58+
(type $concrete_struct_mut (sub (array (mut (ref null $my_struct)))))
59+
(type $concrete_struct_mut_2 (sub $concrete_struct_mut (array (mut (ref null $my_struct)))))
6960

7061
;; none refs
71-
(type $z1 (sub $q (array (mut nullref))))
72-
(type $z2 (sub $q0 (array (ref none))))
73-
(type $z3 (sub $z1 (array (mut (ref none)))))
74-
(type $z4 (sub $z1 (array nullref)))
75-
(type (sub $q1 (array nullref)))
76-
(type (sub $r (array nullref)))
77-
(type (sub $rr (array nullref)))
78-
(type (sub $rrr (array nullref)))
79-
(type (sub $q1 (array (ref none))))
80-
(type (sub $r (array (ref none))))
81-
(type (sub $rr (array (ref none))))
82-
(type (sub $rrr (array (ref none))))
62+
(type $none (sub (array nullref)))
63+
(type $none_2 (sub $none (array nullref)))
64+
(type $none_any (sub $any (array nullref)))
65+
(type $none_any_2 (sub $none_any (array nullref)))
66+
(type $none_eq (sub $eq (array nullref)))
67+
(type $none_eq_2 (sub $none_eq (array nullref)))
68+
(type $none_i31 (sub $i31 (array nullref)))
69+
(type $none_i31_2 (sub $none_i31 (array nullref)))
70+
(type $none_array (sub $array (array nullref)))
71+
(type $none_array_2 (sub $none_array (array nullref)))
72+
(type $none_concrete_array (sub $concrete_array (array nullref)))
73+
(type $none_concrete_array_2 (sub $none_concrete_array (array nullref)))
74+
(type $none_struct (sub $struct (array nullref)))
75+
(type $none_struct_2 (sub $none_struct (array nullref)))
76+
(type $none_concrete_struct (sub $concrete_struct (array nullref)))
77+
(type $none_concrete_struct_2 (sub $none_concrete_struct (array nullref)))
78+
(type $none_mut (sub (array (mut nullref))))
79+
(type $none_mut_2 (sub $none_mut (array (mut nullref))))
8380

8481
;; func refs
85-
(type $t (sub (array (mut funcref))))
86-
(type $u (sub $t (array (ref null func))))
87-
(type $v (sub $u (array (ref func))))
88-
(type $w (sub $v (array (ref $a))))
89-
(type $x (sub $t (array (ref null $a))))
90-
(type $y (sub $w (array (ref nofunc))))
91-
(type $z (sub $x (array nullfuncref)))
82+
(type $func (sub (array funcref)))
83+
(type $func_2 (sub $func (array funcref)))
84+
(type $func_mut (sub (array (mut funcref))))
85+
(type $func_mut_2 (sub $func_mut (array (mut funcref))))
86+
87+
;; concrete func refs
88+
(type $my_func (func))
89+
(type $concrete_func (sub $func (array (ref null $my_func))))
90+
(type $concrete_func_2 (sub $concrete_func (array (ref null $my_func))))
91+
(type $concrete_func_mut (sub (array (mut (ref null $my_func)))))
92+
(type $concrete_func_mut_2 (sub $concrete_func_mut (array (mut (ref null $my_func)))))
93+
94+
;; nofunc
95+
(type $nofunc (sub $concrete_func (array nullfuncref)))
96+
(type $nofunc_2 (sub $nofunc (array nullfuncref)))
97+
(type $nofunc_mut (sub (array (mut nullfuncref))))
98+
(type $nofunc_mut_2 (sub $nofunc_mut (array (mut nullfuncref))))
9299

93100
;; extern refs
94-
(type $t0 (sub (array (mut externref))))
95-
(type $u0 (sub $t0 (array (ref null extern))))
96-
(type $v0 (sub $u0 (array (ref extern))))
97-
(type $y0 (sub $v0 (array (ref noextern))))
98-
(type $y01 (sub $u0 (array (ref noextern))))
99-
(type $z0 (sub $u0 (array nullexternref)))
100-
101-
(type $A (sub (struct (field $vt (mut i32)))))
102-
(type $B (sub $A (struct (field $vt (mut i32)))))
103-
(type (sub $A (struct (field $tv (mut i32))))) ;; same field, different name
104-
(type (sub $A (struct (field (mut i32)) (field $vt (mut i64))))) ;; different field, same name
101+
(type $extern (sub (array externref)))
102+
(type $extern_2 (sub $extern (array externref)))
103+
(type $extern_mut (sub (array (mut externref))))
104+
(type $extern_mut_2 (sub $extern_mut (array (mut externref))))
105+
106+
;; noextern
107+
(type $noextern (sub $extern (array nullexternref)))
108+
(type $noextern_2 (sub $noextern (array nullexternref)))
109+
(type $noextern_mut (sub (array (mut nullexternref))))
110+
(type $noextern_mut_2 (sub $noextern_mut (array (mut nullexternref))))
111+
112+
;; field names
113+
(type $struct_with_named_field (sub (struct (field $field (mut i32)))))
114+
;; same field, same name
115+
(type (sub $struct_with_named_field (struct (field $field (mut i32)))))
116+
;; same field, different name
117+
(type (sub $struct_with_named_field (struct (field $different (mut i32)))))
118+
;; different field, same name
119+
(type (sub $struct_with_named_field (struct (field (mut i32)) (field $field (mut i64)))))
105120
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
;; --enable-gc
2+
3+
(assert_invalid
4+
(module
5+
;; When fields are mutable, a subtype's reference fields cannot be subtypes of
6+
;; the supertype's fields, they must match exactly.
7+
(type $a (sub (struct (field (mut (ref null any))))))
8+
(type $b (sub $a (struct (field (mut (ref null none))))))
9+
)
10+
"sub type must match super type"
11+
)
12+
13+
(assert_invalid
14+
(module
15+
;; When fields are const, a subtype's reference fields cannot be supertypes of
16+
;; the supertype's fields, they must be subtypes.
17+
(type $a (sub (struct (field (mut (ref null none))))))
18+
(type $b (sub $a (struct (field (mut (ref null any))))))
19+
)
20+
"sub type must match super type"
21+
)
22+
23+
(assert_invalid
24+
(module
25+
;; The mutability of fields must be the same.
26+
(type $c (sub (struct (field (mut (ref null any))))))
27+
(type $d (sub $c (struct (field (ref null none)))))
28+
)
29+
"sub type must match super type"
30+
)
31+
32+
(assert_invalid
33+
(module
34+
;; The mutability of fields must be the same.
35+
(type $c (sub (struct (field (ref null any)))))
36+
(type $d (sub $c (struct (field (mut (ref null none))))))
37+
)
38+
"sub type must match super type"
39+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"source_filename": "tests/local/gc/gc-field-subtyping.wast",
3+
"commands": [
4+
{
5+
"type": "module",
6+
"line": 3,
7+
"filename": "gc-field-subtyping.0.wasm",
8+
"module_type": "binary"
9+
}
10+
]
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(module
2+
(type $a (;0;) (sub (struct (field (mut anyref)))))
3+
(type $b (;1;) (sub $a (struct (field (mut anyref)))))
4+
(type $c (;2;) (sub (struct (field anyref))))
5+
(type $d (;3;) (sub $c (struct (field nullref))))
6+
)

0 commit comments

Comments
 (0)