Skip to content

Commit 2564177

Browse files
authored
More Pulley Provenance Tests (bytecodealliance#10139)
In the spirit of covering more libcalls and exercising more wasm this adds execution of table/memory intrinsics. This helped uncover some stacked-borrows unsoundness in how our `table.copy` intrinsic is implemented which was fixed with a new carefully typed method on `PrimaryMap`.
1 parent 6dae7eb commit 2564177

File tree

4 files changed

+76
-12
lines changed

4 files changed

+76
-12
lines changed

cranelift/entity/src/primary.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,25 @@ where
214214
.map(|i| K::new(i))
215215
.map_err(|i| K::new(i))
216216
}
217+
218+
/// Analog of `get_raw` except that a raw pointer is returned rather than a
219+
/// mutable reference.
220+
///
221+
/// The default accessors of items in [`PrimaryMap`] will invalidate all
222+
/// previous borrows obtained from the map according to miri. This function
223+
/// can be used to acquire a pointer and then subsequently acquire a second
224+
/// pointer later on without invalidating the first one. In other words
225+
/// this is only here to help borrow two elements simultaneously with miri.
226+
pub fn get_raw_mut(&mut self, k: K) -> Option<*mut V> {
227+
if k.index() < self.elems.len() {
228+
// SAFETY: the `add` function requires that the index is in-bounds
229+
// with respect to the allocation which is satisfied here due to
230+
// the bounds-check above.
231+
unsafe { Some(self.elems.as_mut_ptr().add(k.index())) }
232+
} else {
233+
None
234+
}
235+
}
217236
}
218237

219238
#[derive(Debug, PartialEq, Eq, Clone, Copy)]

crates/wasmtime/src/runtime/vm/instance.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,9 @@ impl Instance {
10821082

10831083
/// Get a locally-defined memory.
10841084
pub fn get_defined_memory(&mut self, index: DefinedMemoryIndex) -> *mut Memory {
1085-
&raw mut self.memories[index].1
1085+
// SAFETY: the `unsafe` here is projecting from `*mut (A, B)` to
1086+
// `*mut A`, which should be a safe operation to do.
1087+
unsafe { &raw mut (*self.memories.get_raw_mut(index).unwrap()).1 }
10861088
}
10871089

10881090
/// Do a `memory.copy`
@@ -1302,20 +1304,26 @@ impl Instance {
13021304
}
13031305
}
13041306

1305-
&raw mut self.tables[idx].1
1307+
// SAFETY: the `unsafe` here is projecting from `*mut (A, B)` to
1308+
// `*mut A`, which should be a safe operation to do.
1309+
unsafe { &raw mut (*self.tables.get_raw_mut(idx).unwrap()).1 }
13061310
}
13071311

13081312
/// Get a table by index regardless of whether it is locally-defined or an
13091313
/// imported, foreign table.
13101314
pub(crate) fn get_table(&mut self, table_index: TableIndex) -> *mut Table {
1311-
self.with_defined_table_index_and_instance(table_index, |idx, instance| {
1312-
&raw mut instance.tables[idx].1
1315+
self.with_defined_table_index_and_instance(table_index, |idx, instance| unsafe {
1316+
// SAFETY: the `unsafe` here is projecting from `*mut (A, B)` to
1317+
// `*mut A`, which should be a safe operation to do.
1318+
&raw mut (*instance.tables.get_raw_mut(idx).unwrap()).1
13131319
})
13141320
}
13151321

13161322
/// Get a locally-defined table.
13171323
pub(crate) fn get_defined_table(&mut self, index: DefinedTableIndex) -> *mut Table {
1318-
&raw mut self.tables[index].1
1324+
// SAFETY: the `unsafe` here is projecting from `*mut (A, B)` to
1325+
// `*mut A`, which should be a safe operation to do.
1326+
unsafe { &raw mut (*self.tables.get_raw_mut(index).unwrap()).1 }
13191327
}
13201328

13211329
pub(crate) fn with_defined_table_index_and_instance<R>(

tests/all/pulley.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,28 +144,31 @@ fn pulley_provenance_test() -> Result<()> {
144144
let funcref = instance.get_func(&mut store, "call-wasm").unwrap();
145145
for func in ["call_ref-wasm", "return_call_ref-wasm"] {
146146
println!("testing func {func:?}");
147-
let func = instance
148-
.get_typed_func::<Func, (i32, i32, i32)>(&mut store, func)
149-
.unwrap();
147+
let func = instance.get_typed_func::<Func, (i32, i32, i32)>(&mut store, func)?;
150148
let results = func.call(&mut store, funcref)?;
151149
assert_eq!(results, (1, 2, 3));
152150
}
153151

154152
let trap = instance
155-
.get_typed_func::<(), ()>(&mut store, "unreachable")
156-
.unwrap()
153+
.get_typed_func::<(), ()>(&mut store, "unreachable")?
157154
.call(&mut store, ())
158155
.unwrap_err()
159156
.downcast::<Trap>()?;
160157
assert_eq!(trap, Trap::UnreachableCodeReached);
161158

162159
let trap = instance
163-
.get_typed_func::<(), i32>(&mut store, "divide-by-zero")
164-
.unwrap()
160+
.get_typed_func::<(), i32>(&mut store, "divide-by-zero")?
165161
.call(&mut store, ())
166162
.unwrap_err()
167163
.downcast::<Trap>()?;
168164
assert_eq!(trap, Trap::IntegerDivisionByZero);
169165

166+
instance
167+
.get_typed_func::<(), ()>(&mut store, "memory-intrinsics")?
168+
.call(&mut store, ())?;
169+
instance
170+
.get_typed_func::<(), ()>(&mut store, "table-intrinsics")?
171+
.call(&mut store, ())?;
172+
170173
Ok(())
171174
}

tests/all/pulley_provenance_test.wat

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,38 @@
4949
i32.const 100
5050
i32.const 0
5151
i32.div_s)
52+
53+
(memory 1)
54+
(func (export "memory-intrinsics")
55+
(drop (i32.load (i32.const 0)))
56+
(i32.store (i32.const 0) (i32.const 0))
57+
(drop (memory.grow (i32.const 1)))
58+
(drop (i32.load (i32.const 0)))
59+
(i32.store (i32.const 0) (i32.const 0))
60+
(drop (memory.size))
61+
62+
(memory.copy (i32.const 0) (i32.const 1) (i32.const 10))
63+
(memory.init $d (i32.const 0) (i32.const 1) (i32.const 3))
64+
(memory.fill (i32.const 0) (i32.const 10) (i32.const 10))
65+
66+
(data.drop $d)
67+
)
68+
(data $d "abcd")
69+
70+
(table 1 funcref)
71+
(func (export "table-intrinsics")
72+
(drop (table.get (i32.const 0)))
73+
(table.set (i32.const 0) (table.get (i32.const 0)))
74+
75+
(drop (table.grow (ref.null func) (i32.const 100)))
76+
77+
(drop (table.get (i32.const 1)))
78+
(table.set (i32.const 1) (table.get (i32.const 1)))
79+
80+
(table.copy (i32.const 0) (i32.const 1) (i32.const 10))
81+
(table.init $e (i32.const 0) (i32.const 1) (i32.const 3))
82+
(table.fill (i32.const 0) (ref.func $empty) (i32.const 10))
83+
)
84+
(elem $e func $empty $empty $empty $empty)
85+
(func $empty)
5286
)

0 commit comments

Comments
 (0)