Skip to content

Commit 60cc9c5

Browse files
authored
Allow null-checking exact casts without custom descriptors (#7520)
We previously allowed trivial exact casts (i.e. those where the input and target types are the same) to validate even without custom descriptors enabled to ensure that finalization always produces valid casts. But validation can also produce casts where the cast target is a non-nullable exact reference and the input type is a nullable exact reference to the same heap type. Update validation to allow these casts. This is safe because erasing the exactness of the input and cast types does not change the results of these casts. They succeed iff the input is a non-null value.
1 parent 19e887e commit 60cc9c5

File tree

2 files changed

+164
-17
lines changed

2 files changed

+164
-17
lines changed

src/wasm/wasm-validator.cpp

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2904,13 +2904,14 @@ void FunctionValidator::visitRefTest(RefTest* curr) {
29042904
curr,
29052905
"ref.test target type and ref type must have a common supertype");
29062906

2907-
// If custom descriptors is not enabled, only trivial exact casts are allowed,
2908-
// i.e. those where the operand has the same exact type. The result of these
2909-
// trivial exact casts does not change when the types are made inexact during
2910-
// binary writing.
2907+
// If custom descriptors is not enabled, only trivial and null-checking exact
2908+
// casts are allowed, i.e. those where the operand is also exact and has the
2909+
// same heap type, but may differ in nullability. The result of these trivial
2910+
// exact casts does not change when the types are made inexact during binary
2911+
// writing.
29112912
if (!getModule()->features.hasCustomDescriptors()) {
2912-
shouldBeTrue(curr->castType.isInexact() ||
2913-
curr->castType == curr->ref->type,
2913+
shouldBeTrue(curr->castType.isInexact() || curr->castType.with(Nullable) ==
2914+
curr->ref->type.with(Nullable),
29142915
curr,
29152916
"ref.test of exact type requires custom descriptors "
29162917
"[--enable-custom-descriptors]");
@@ -2956,7 +2957,8 @@ void FunctionValidator::visitRefCast(RefCast* curr) {
29562957

29572958
// See comment about exactness on visitRefTest.
29582959
if (!getModule()->features.hasCustomDescriptors()) {
2959-
shouldBeTrue(curr->type.isInexact() || curr->type == curr->ref->type,
2960+
shouldBeTrue(curr->type.isInexact() ||
2961+
curr->type.with(Nullable) == curr->ref->type.with(Nullable),
29602962
curr,
29612963
"ref.cast to exact type requires custom descriptors "
29622964
"[--enable-custom-descriptors]");
@@ -2993,7 +2995,8 @@ void FunctionValidator::visitBrOn(BrOn* curr) {
29932995
// See comment about exactness on visitRefTest.
29942996
if (!getModule()->features.hasCustomDescriptors()) {
29952997
shouldBeTrue(curr->castType.isInexact() ||
2996-
curr->castType == curr->ref->type,
2998+
curr->castType.with(Nullable) ==
2999+
curr->ref->type.with(Nullable),
29973000
curr,
29983001
"br_on_cast* to exact type requires custom descriptors "
29993002
"[--enable-custom-descriptors]");

test/lit/validation/exact-casts-trivial.wast

Lines changed: 153 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,71 +9,215 @@
99
;; CHECK: (type $foo (struct))
1010
(type $foo (struct))
1111

12-
;; CHECK: (func $ref.cast (type $1) (param $0 (ref null (exact $foo)))
12+
;; CHECK: (func $ref.cast (type $1) (param $0 (ref (exact $foo))) (param $1 (ref null (exact $foo)))
1313
;; CHECK-NEXT: (drop
14-
;; CHECK-NEXT: (ref.cast (ref null (exact $foo))
14+
;; CHECK-NEXT: (ref.cast (ref (exact $foo))
1515
;; CHECK-NEXT: (local.get $0)
1616
;; CHECK-NEXT: )
1717
;; CHECK-NEXT: )
18+
;; CHECK-NEXT: (drop
19+
;; CHECK-NEXT: (ref.cast (ref (exact $foo))
20+
;; CHECK-NEXT: (local.get $0)
21+
;; CHECK-NEXT: )
22+
;; CHECK-NEXT: )
23+
;; CHECK-NEXT: (drop
24+
;; CHECK-NEXT: (ref.cast (ref null (exact $foo))
25+
;; CHECK-NEXT: (local.get $1)
26+
;; CHECK-NEXT: )
27+
;; CHECK-NEXT: )
28+
;; CHECK-NEXT: (drop
29+
;; CHECK-NEXT: (ref.cast (ref (exact $foo))
30+
;; CHECK-NEXT: (local.get $1)
31+
;; CHECK-NEXT: )
32+
;; CHECK-NEXT: )
1833
;; CHECK-NEXT: )
19-
(func $ref.cast (param (ref null (exact $foo)))
34+
(func $ref.cast (param (ref (exact $foo)) (ref null (exact $foo)))
2035
(drop
2136
(ref.cast anyref
2237
(local.get 0)
2338
)
2439
)
40+
(drop
41+
(ref.cast (ref any)
42+
(local.get 0)
43+
)
44+
)
45+
(drop
46+
(ref.cast anyref
47+
(local.get 1)
48+
)
49+
)
50+
(drop
51+
(ref.cast (ref any)
52+
(local.get 1)
53+
)
54+
)
2555
)
2656

27-
;; CHECK: (func $ref.test (type $2) (param $0 (ref (exact $foo)))
57+
;; CHECK: (func $ref.test (type $1) (param $0 (ref (exact $foo))) (param $1 (ref null (exact $foo)))
2858
;; CHECK-NEXT: (drop
2959
;; CHECK-NEXT: (ref.test (ref (exact $foo))
3060
;; CHECK-NEXT: (local.get $0)
3161
;; CHECK-NEXT: )
3262
;; CHECK-NEXT: )
63+
;; CHECK-NEXT: (drop
64+
;; CHECK-NEXT: (ref.test (ref (exact $foo))
65+
;; CHECK-NEXT: (local.get $0)
66+
;; CHECK-NEXT: )
67+
;; CHECK-NEXT: )
68+
;; CHECK-NEXT: (drop
69+
;; CHECK-NEXT: (ref.test (ref null (exact $foo))
70+
;; CHECK-NEXT: (local.get $1)
71+
;; CHECK-NEXT: )
72+
;; CHECK-NEXT: )
73+
;; CHECK-NEXT: (drop
74+
;; CHECK-NEXT: (ref.test (ref (exact $foo))
75+
;; CHECK-NEXT: (local.get $1)
76+
;; CHECK-NEXT: )
77+
;; CHECK-NEXT: )
3378
;; CHECK-NEXT: )
34-
(func $ref.test (param (ref (exact $foo)))
79+
(func $ref.test (param (ref (exact $foo)) (ref null (exact $foo)))
3580
(drop
3681
(ref.test anyref
3782
(local.get 0)
3883
)
3984
)
85+
(drop
86+
(ref.test (ref any)
87+
(local.get 0)
88+
)
89+
)
90+
(drop
91+
(ref.test anyref
92+
(local.get 1)
93+
)
94+
)
95+
(drop
96+
(ref.test (ref any)
97+
(local.get 1)
98+
)
99+
)
40100
)
41101

42-
;; CHECK: (func $br_on_cast (type $1) (param $0 (ref null (exact $foo)))
102+
;; CHECK: (func $br_on_cast (type $2) (param $0 (ref null (exact $foo))) (param $1 (ref null (exact $foo)))
43103
;; CHECK-NEXT: (drop
44104
;; CHECK-NEXT: (block $block (result anyref)
45105
;; CHECK-NEXT: (br_on_cast $block (ref null (exact $foo)) (ref null (exact $foo))
46106
;; CHECK-NEXT: (local.get $0)
47107
;; CHECK-NEXT: )
48108
;; CHECK-NEXT: )
49109
;; CHECK-NEXT: )
110+
;; CHECK-NEXT: (drop
111+
;; CHECK-NEXT: (block $block1 (result anyref)
112+
;; CHECK-NEXT: (br_on_cast $block1 (ref null (exact $foo)) (ref (exact $foo))
113+
;; CHECK-NEXT: (local.get $0)
114+
;; CHECK-NEXT: )
115+
;; CHECK-NEXT: )
116+
;; CHECK-NEXT: )
117+
;; CHECK-NEXT: (drop
118+
;; CHECK-NEXT: (block $block2 (result anyref)
119+
;; CHECK-NEXT: (br_on_cast $block2 (ref null (exact $foo)) (ref null (exact $foo))
120+
;; CHECK-NEXT: (local.get $1)
121+
;; CHECK-NEXT: )
122+
;; CHECK-NEXT: )
123+
;; CHECK-NEXT: )
124+
;; CHECK-NEXT: (drop
125+
;; CHECK-NEXT: (block $block3 (result anyref)
126+
;; CHECK-NEXT: (br_on_cast $block3 (ref null (exact $foo)) (ref (exact $foo))
127+
;; CHECK-NEXT: (local.get $1)
128+
;; CHECK-NEXT: )
129+
;; CHECK-NEXT: )
130+
;; CHECK-NEXT: )
50131
;; CHECK-NEXT: )
51-
(func $br_on_cast (param (ref null (exact $foo)))
132+
(func $br_on_cast (param (ref null (exact $foo)) (ref null (exact $foo)))
52133
(drop
53134
(block (result anyref)
54135
(br_on_cast 0 anyref anyref
55136
(local.get 0)
56137
)
57138
)
58139
)
140+
(drop
141+
(block (result anyref)
142+
(br_on_cast 0 anyref (ref any)
143+
(local.get 0)
144+
)
145+
)
146+
)
147+
(drop
148+
(block (result anyref)
149+
(br_on_cast 0 anyref anyref
150+
(local.get 1)
151+
)
152+
)
153+
)
154+
(drop
155+
(block (result anyref)
156+
(br_on_cast 0 anyref (ref any)
157+
(local.get 1)
158+
)
159+
)
160+
)
59161
)
60162

61-
;; CHECK: (func $br_on_cast_fail (type $2) (param $0 (ref (exact $foo)))
163+
;; CHECK: (func $br_on_cast_fail (type $1) (param $0 (ref (exact $foo))) (param $1 (ref null (exact $foo)))
62164
;; CHECK-NEXT: (drop
63165
;; CHECK-NEXT: (block $block (result anyref)
64166
;; CHECK-NEXT: (br_on_cast_fail $block (ref (exact $foo)) (ref (exact $foo))
65167
;; CHECK-NEXT: (local.get $0)
66168
;; CHECK-NEXT: )
67169
;; CHECK-NEXT: )
68170
;; CHECK-NEXT: )
171+
;; CHECK-NEXT: (drop
172+
;; CHECK-NEXT: (block $block1 (result anyref)
173+
;; CHECK-NEXT: (br_on_cast_fail $block1 (ref (exact $foo)) (ref (exact $foo))
174+
;; CHECK-NEXT: (local.get $0)
175+
;; CHECK-NEXT: )
176+
;; CHECK-NEXT: )
177+
;; CHECK-NEXT: )
178+
;; CHECK-NEXT: (drop
179+
;; CHECK-NEXT: (block $block2 (result anyref)
180+
;; CHECK-NEXT: (br_on_cast_fail $block2 (ref null (exact $foo)) (ref null (exact $foo))
181+
;; CHECK-NEXT: (local.get $1)
182+
;; CHECK-NEXT: )
183+
;; CHECK-NEXT: )
184+
;; CHECK-NEXT: )
185+
;; CHECK-NEXT: (drop
186+
;; CHECK-NEXT: (block $block3 (result anyref)
187+
;; CHECK-NEXT: (br_on_cast_fail $block3 (ref null (exact $foo)) (ref (exact $foo))
188+
;; CHECK-NEXT: (local.get $1)
189+
;; CHECK-NEXT: )
190+
;; CHECK-NEXT: )
191+
;; CHECK-NEXT: )
69192
;; CHECK-NEXT: )
70-
(func $br_on_cast_fail (param (ref (exact $foo)))
193+
(func $br_on_cast_fail (param (ref (exact $foo)) (ref null (exact $foo)))
71194
(drop
72195
(block (result anyref)
73196
(br_on_cast_fail 0 anyref anyref
74197
(local.get 0)
75198
)
76199
)
77200
)
201+
(drop
202+
(block (result anyref)
203+
(br_on_cast_fail 0 anyref (ref any)
204+
(local.get 0)
205+
)
206+
)
207+
)
208+
(drop
209+
(block (result anyref)
210+
(br_on_cast_fail 0 anyref anyref
211+
(local.get 1)
212+
)
213+
)
214+
)
215+
(drop
216+
(block (result anyref)
217+
(br_on_cast_fail 0 anyref (ref any)
218+
(local.get 1)
219+
)
220+
)
221+
)
78222
)
79223
)

0 commit comments

Comments
 (0)