Skip to content

Commit d99b51d

Browse files
committed
threads: add validation for global constant expressions
Previously, it was possible to use an unshared global to initialize a shared global, table element. This change makes that invalid by propagating sharedness in to the constant expression validator.
1 parent 3e0dda0 commit d99b51d

File tree

14 files changed

+275
-174
lines changed

14 files changed

+275
-174
lines changed

crates/wasmparser/src/validator/core.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,13 @@ impl ModuleState {
133133
) -> Result<()> {
134134
self.module
135135
.check_global_type(&mut global.ty, features, types, offset)?;
136-
self.check_const_expr(&global.init_expr, global.ty.content_type, features, types)?;
136+
self.check_const_expr(
137+
&global.init_expr,
138+
global.ty.content_type,
139+
global.ty.shared,
140+
features,
141+
types,
142+
)?;
137143
self.module.assert_mut().globals.push(global.ty);
138144
Ok(())
139145
}
@@ -162,7 +168,13 @@ impl ModuleState {
162168
the function-references proposal"
163169
);
164170
}
165-
self.check_const_expr(expr, table.ty.element_type.into(), features, types)?;
171+
self.check_const_expr(
172+
expr,
173+
table.ty.element_type.into(),
174+
table.ty.shared,
175+
features,
176+
types,
177+
)?;
166178
}
167179
}
168180
self.module.assert_mut().tables.push(table.ty);
@@ -182,8 +194,8 @@ impl ModuleState {
182194
memory_index,
183195
offset_expr,
184196
} => {
185-
let ty = self.module.memory_at(memory_index, offset)?.index_type();
186-
self.check_const_expr(&offset_expr, ty, features, types)
197+
let ty = self.module.memory_at(memory_index, offset)?;
198+
self.check_const_expr(&offset_expr, ty.index_type(), ty.shared, features, types)
187199
}
188200
}
189201
}
@@ -195,8 +207,8 @@ impl ModuleState {
195207
types: &TypeList,
196208
offset: usize,
197209
) -> Result<()> {
198-
// the `funcref` value type is allowed all the way back to the MVP, so
199-
// don't check it here
210+
// The `funcref` value type is allowed all the way back to the MVP, so
211+
// don't check it here.
200212
let element_ty = match &mut e.items {
201213
crate::ElementItems::Functions(_) => RefType::FUNC,
202214
crate::ElementItems::Expressions(ty, _) => {
@@ -221,8 +233,13 @@ impl ModuleState {
221233
offset,
222234
));
223235
}
224-
225-
self.check_const_expr(&offset_expr, table.index_type(), features, types)?;
236+
self.check_const_expr(
237+
&offset_expr,
238+
table.index_type(),
239+
table.shared,
240+
features,
241+
types,
242+
)?;
226243
}
227244
ElementKind::Passive | ElementKind::Declared => {
228245
if !features.bulk_memory() {
@@ -256,8 +273,18 @@ impl ModuleState {
256273
}
257274
crate::ElementItems::Expressions(ty, reader) => {
258275
validate_count(reader.count())?;
276+
let shared = match ty.heap_type() {
277+
HeapType::Abstract { shared, .. } => shared,
278+
HeapType::Concrete(unpacked_index) => {
279+
if let Some(id) = unpacked_index.as_core_type_id() {
280+
types[id].composite_type.shared
281+
} else {
282+
todo!()
283+
}
284+
}
285+
};
259286
for expr in reader {
260-
self.check_const_expr(&expr?, ValType::Ref(ty), features, types)?;
287+
self.check_const_expr(&expr?, ValType::Ref(ty), shared, features, types)?;
261288
}
262289
}
263290
}
@@ -269,6 +296,7 @@ impl ModuleState {
269296
&mut self,
270297
expr: &ConstExpr<'_>,
271298
expected_ty: ValType,
299+
shared: bool,
272300
features: &WasmFeatures,
273301
types: &TypeList,
274302
) -> Result<()> {
@@ -286,6 +314,7 @@ impl ModuleState {
286314
module: &mut self.module,
287315
},
288316
features,
317+
shared,
289318
};
290319

291320
let mut ops = expr.get_operators_reader();
@@ -309,6 +338,7 @@ impl ModuleState {
309338
resources: OperatorValidatorResources<'a>,
310339
order: Order,
311340
features: &'a WasmFeatures,
341+
shared: bool,
312342
}
313343

314344
impl VisitConstOperator<'_> {
@@ -374,6 +404,12 @@ impl ModuleState {
374404
self.offset,
375405
));
376406
}
407+
if self.shared && !global.shared {
408+
return Err(BinaryReaderError::new(
409+
"invalid type: constant expression must be shared",
410+
self.offset,
411+
));
412+
}
377413
Ok(())
378414
}
379415

tests/local/shared-everything-threads/globals.wast

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@
2626
(global (shared mut v128) (v128.const i64x2 0 0))
2727
)
2828

29+
;; Check that we can only use shared globals to initialize other shared globals.
30+
(module
31+
(global $a (shared i32) (i32.const 0))
32+
(global $b (shared i32) (global.get $a))
33+
)
34+
(assert_invalid
35+
(module
36+
(global $a i32 (i32.const 0))
37+
(global $b (shared i32) (global.get $a))
38+
)
39+
"invalid type")
40+
2941
(assert_malformed
3042
(module quote "(global (mut shared i64) (i64.const -1))")
3143
"unexpected token")

tests/local/shared-everything-threads/tables.wast

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@
4242
(table shared 0 (ref $t)))
4343
"shared tables must have a shared element type")
4444

45+
;; A shared table must be initialized from shared objects.
46+
(module
47+
(type $f (shared (func)))
48+
(global $g (shared (ref null $f)) (ref.null $f))
49+
(table $t shared 1 (ref null $f))
50+
(elem (ref null $f) (global.get $g))
51+
)
52+
(assert_invalid
53+
(module
54+
(type $f (shared (func)))
55+
(global $g (ref null $f) (ref.null $f))
56+
(table $t shared 1 (ref null $f))
57+
;; When we initialize a shared element, everything must be shared, including
58+
;; the used global.
59+
(elem (ref null $f) (global.get $g)))
60+
"invalid type")
61+
4562
;; Check `table.atomic.*` instructions.
4663
(module (;eq;)
4764
(table $a (import "spectest" "table_eq") shared 1 (ref null (shared eq)))

0 commit comments

Comments
 (0)