Skip to content

Commit 325d71f

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 325d71f

File tree

14 files changed

+257
-174
lines changed

14 files changed

+257
-174
lines changed

crates/wasmparser/src/validator/core.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ 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(&global.init_expr, global.ty.content_type, global.ty.shared, features, types)?;
137137
self.module.assert_mut().globals.push(global.ty);
138138
Ok(())
139139
}
@@ -162,7 +162,7 @@ impl ModuleState {
162162
the function-references proposal"
163163
);
164164
}
165-
self.check_const_expr(expr, table.ty.element_type.into(), features, types)?;
165+
self.check_const_expr(expr, table.ty.element_type.into(), table.ty.shared, features, types)?;
166166
}
167167
}
168168
self.module.assert_mut().tables.push(table.ty);
@@ -182,8 +182,8 @@ impl ModuleState {
182182
memory_index,
183183
offset_expr,
184184
} => {
185-
let ty = self.module.memory_at(memory_index, offset)?.index_type();
186-
self.check_const_expr(&offset_expr, ty, features, types)
185+
let ty = self.module.memory_at(memory_index, offset)?;
186+
self.check_const_expr(&offset_expr, ty.index_type(), ty.shared, features, types)
187187
}
188188
}
189189
}
@@ -195,8 +195,8 @@ impl ModuleState {
195195
types: &TypeList,
196196
offset: usize,
197197
) -> Result<()> {
198-
// the `funcref` value type is allowed all the way back to the MVP, so
199-
// don't check it here
198+
// The `funcref` value type is allowed all the way back to the MVP, so
199+
// don't check it here.
200200
let element_ty = match &mut e.items {
201201
crate::ElementItems::Functions(_) => RefType::FUNC,
202202
crate::ElementItems::Expressions(ty, _) => {
@@ -221,8 +221,7 @@ impl ModuleState {
221221
offset,
222222
));
223223
}
224-
225-
self.check_const_expr(&offset_expr, table.index_type(), features, types)?;
224+
self.check_const_expr(&offset_expr, table.index_type(), table.shared, features, types)?;
226225
}
227226
ElementKind::Passive | ElementKind::Declared => {
228227
if !features.bulk_memory() {
@@ -256,8 +255,18 @@ impl ModuleState {
256255
}
257256
crate::ElementItems::Expressions(ty, reader) => {
258257
validate_count(reader.count())?;
258+
let shared = match ty.heap_type() {
259+
HeapType::Abstract { shared, .. } => shared,
260+
HeapType::Concrete(unpacked_index) => {
261+
if let Some(id) = unpacked_index.as_core_type_id() {
262+
types[id].composite_type.shared
263+
} else {
264+
todo!()
265+
}
266+
}
267+
};
259268
for expr in reader {
260-
self.check_const_expr(&expr?, ValType::Ref(ty), features, types)?;
269+
self.check_const_expr(&expr?, ValType::Ref(ty), shared, features, types)?;
261270
}
262271
}
263272
}
@@ -269,6 +278,7 @@ impl ModuleState {
269278
&mut self,
270279
expr: &ConstExpr<'_>,
271280
expected_ty: ValType,
281+
shared: bool,
272282
features: &WasmFeatures,
273283
types: &TypeList,
274284
) -> Result<()> {
@@ -286,6 +296,7 @@ impl ModuleState {
286296
module: &mut self.module,
287297
},
288298
features,
299+
shared,
289300
};
290301

291302
let mut ops = expr.get_operators_reader();
@@ -309,6 +320,7 @@ impl ModuleState {
309320
resources: OperatorValidatorResources<'a>,
310321
order: Order,
311322
features: &'a WasmFeatures,
323+
shared: bool,
312324
}
313325

314326
impl VisitConstOperator<'_> {
@@ -374,6 +386,12 @@ impl ModuleState {
374386
self.offset,
375387
));
376388
}
389+
if self.shared && !global.shared {
390+
return Err(BinaryReaderError::new(
391+
"invalid type: constant expression must be shared",
392+
self.offset,
393+
));
394+
}
377395
Ok(())
378396
}
379397

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)