Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 41765de

Browse files
authored
Merge pull request #213 from bytecodealliance/miri-fiber
address CM async miri violations
2 parents 0327ee5 + 518bf1b commit 41765de

6 files changed

Lines changed: 290 additions & 44 deletions

File tree

ci/miri-provenance-test.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@
88
set -ex
99

1010
compile() {
11-
cargo run --no-default-features --features compile,pulley,wat,gc-drc,component-model \
11+
cargo run --no-default-features --features compile,pulley,wat,gc-drc,component-model,component-model-async \
1212
compile --target pulley64 $1 \
1313
-o ${1%.wat}.cwasm \
1414
-O memory-reservation=$((1 << 20)) \
1515
-O memory-guard-size=0 \
1616
-O signals-based-traps=n \
17-
-W function-references
17+
-W function-references,component-model-async,component-model-async-stackful,component-model-async-builtins,component-model-error-context
1818
}
1919

2020
compile ./tests/all/pulley_provenance_test.wat
2121
compile ./tests/all/pulley_provenance_test_component.wat
22+
compile ./tests/all/pulley_provenance_test_async_component.wat
2223

2324
MIRIFLAGS="$MIRIFLAGS -Zmiri-disable-isolation -Zmiri-permissive-provenance" \
2425
cargo miri test --test all -- \

crates/wasmtime/src/runtime/component/concurrent.rs

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ use {
5454
ComponentInstanceId, HasData, HasSelf, Instance,
5555
func::{self, Func, Options},
5656
},
57-
store::{StoreInner, StoreOpaque, StoreToken},
57+
store::{Executor, StoreInner, StoreOpaque, StoreToken},
5858
vm::{
59-
AsyncWasmCallState, PreviousAsyncWasmCallState, SendSyncPtr, VMFuncRef,
59+
AsyncWasmCallState, Interpreter, PreviousAsyncWasmCallState, SendSyncPtr, VMFuncRef,
6060
VMMemoryDefinition, VMStore, VMStoreRawPtr,
6161
component::{CallContext, ComponentInstance, InstanceFlags, ResourceTables},
6262
mpk::{self, ProtectionMask},
@@ -94,7 +94,7 @@ use {
9494
},
9595
table::{Table, TableDebug, TableError, TableId},
9696
wasmtime_environ::{
97-
PrimaryMap,
97+
PrimaryMap, TripleExt,
9898
component::{
9999
MAX_FLAT_PARAMS, MAX_FLAT_RESULTS, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
100100
RuntimeComponentInstanceIndex, StringEncoding,
@@ -3223,11 +3223,13 @@ impl Instance {
32233223

32243224
impl Drop for Reset<'_> {
32253225
fn drop(&mut self) {
3226-
self.0.concurrent_async_state().current_instance = self.1;
3226+
unsafe {
3227+
(*self.0.concurrent_async_state()).current_instance = self.1;
3228+
}
32273229
}
32283230
}
32293231
let prev = mem::replace(
3230-
&mut store.concurrent_async_state().current_instance,
3232+
unsafe { &mut (*store.concurrent_async_state()).current_instance },
32313233
Some(self.id().instance()),
32323234
);
32333235
let reset = Reset(store, prev);
@@ -3253,12 +3255,9 @@ impl Instance {
32533255
let result = future.poll(cx);
32543256

32553257
tls::get(|store| {
3256-
let spawned_tasks = mem::take(
3257-
&mut store
3258-
.store_opaque_mut()
3259-
.concurrent_async_state()
3260-
.spawned_tasks,
3261-
);
3258+
let spawned_tasks = mem::take(unsafe {
3259+
&mut (*store.store_opaque_mut().concurrent_async_state()).spawned_tasks
3260+
});
32623261
let instance = &mut store[self.id()];
32633262
for spawned in spawned_tasks {
32643263
instance.push_future(spawned);
@@ -4267,15 +4266,16 @@ pub(crate) struct AsyncState {
42674266
/// The `Suspend` for the current fiber (or null if no such fiber is running).
42684267
///
42694268
/// See `StoreFiber` for an explanation of the signature types we use here.
4270-
current_suspend: UnsafeCell<
4271-
*mut Suspend<
4272-
(Option<*mut dyn VMStore>, Result<()>),
4273-
Option<*mut dyn VMStore>,
4274-
(Option<*mut dyn VMStore>, Result<()>),
4275-
>,
4269+
current_suspend: *mut Suspend<
4270+
(Option<*mut dyn VMStore>, Result<()>),
4271+
Option<*mut dyn VMStore>,
4272+
(Option<*mut dyn VMStore>, Result<()>),
42764273
>,
4274+
4275+
pub(crate) current_executor: *mut Executor,
4276+
42774277
/// See `PollContext`
4278-
current_poll_cx: UnsafeCell<PollContext>,
4278+
current_poll_cx: PollContext,
42794279

42804280
/// List of spawned tasks built up during a polling operation. This is
42814281
/// drained after the poll in `poll_with_state`.
@@ -4290,8 +4290,9 @@ pub(crate) struct AsyncState {
42904290
impl Default for AsyncState {
42914291
fn default() -> Self {
42924292
Self {
4293-
current_suspend: UnsafeCell::new(ptr::null_mut()),
4294-
current_poll_cx: UnsafeCell::new(PollContext::default()),
4293+
current_suspend: ptr::null_mut(),
4294+
current_executor: ptr::null_mut(),
4295+
current_poll_cx: PollContext::default(),
42954296
spawned_tasks: Vec::new(),
42964297
current_instance: None,
42974298
}
@@ -4300,7 +4301,7 @@ impl Default for AsyncState {
43004301

43014302
impl AsyncState {
43024303
pub(crate) fn async_guard_range(&self) -> Range<*mut u8> {
4303-
let context = unsafe { *self.current_poll_cx.get() };
4304+
let context = self.current_poll_cx;
43044305
context.guard_range_start..context.guard_range_end
43054306
}
43064307
}
@@ -4335,12 +4336,14 @@ impl AsyncCx {
43354336
/// This will return `None` if called outside the scope of a `self::poll_fn`
43364337
/// call.
43374338
pub(crate) fn try_new(store: &mut StoreOpaque) -> Option<Self> {
4338-
let current_poll_cx = store.concurrent_async_state().current_poll_cx.get();
4339+
let current_poll_cx = unsafe { &raw mut (*store.concurrent_async_state()).current_poll_cx };
43394340
if unsafe { (*current_poll_cx).future_context.is_null() } {
43404341
None
43414342
} else {
43424343
Some(Self {
4343-
current_suspend: store.concurrent_async_state().current_suspend.get(),
4344+
current_suspend: unsafe {
4345+
&raw mut (*store.concurrent_async_state()).current_suspend
4346+
},
43444347
current_stack_limit: store.vm_store_context().stack_limit.get(),
43454348
current_poll_cx,
43464349
track_pkey_context_switch: store.has_pkey(),
@@ -4578,8 +4581,8 @@ fn checked<F: Future + Send + 'static>(
45784581
tls::try_get(|store| {
45794582
let matched = match store {
45804583
tls::TryGet::Some(store) => {
4581-
store.concurrent_async_state().current_instance
4582-
== Some(instance.id().instance())
4584+
let a = unsafe { (*store.concurrent_async_state()).current_instance };
4585+
a == Some(instance.id().instance())
45834586
}
45844587
tls::TryGet::Taken | tls::TryGet::None => false,
45854588
};
@@ -4705,6 +4708,8 @@ struct StoreFiber<'a> {
47054708
>,
47064709
/// The stack limit used for handling traps from guest code.
47074710
stack_limit: *mut usize,
4711+
executor_ptr: *mut *mut Executor,
4712+
executor: Executor,
47084713
}
47094714

47104715
impl StoreFiber<'_> {
@@ -4753,6 +4758,17 @@ fn make_fiber<'a>(
47534758
fun: impl FnOnce(&mut dyn VMStore) -> Result<()> + 'a,
47544759
) -> Result<StoreFiber<'a>> {
47554760
let engine = store.engine().clone();
4761+
#[cfg(has_host_compiler_backend)]
4762+
let executor = if cfg!(feature = "pulley") && engine.target().is_pulley() {
4763+
Executor::Interpreter(Interpreter::new(&engine))
4764+
} else {
4765+
Executor::Native
4766+
};
4767+
#[cfg(not(has_host_compiler_backend))]
4768+
let executor = {
4769+
debug_assert!(engine.target().is_pulley());
4770+
Executor::Interpreter(Interpreter::new(&engine))
4771+
};
47564772
let stack = engine.allocator().allocate_fiber_stack()?;
47574773
Ok(StoreFiber {
47584774
fiber: Some(Fiber::new(
@@ -4765,11 +4781,10 @@ fn make_fiber<'a>(
47654781
// exclusive access to the store until we exit or yield it
47664782
// back to the resumer.
47674783
let store_ref = unsafe { &mut *store.unwrap() };
4768-
let suspend_ptr = store_ref
4769-
.store_opaque_mut()
4770-
.concurrent_async_state()
4771-
.current_suspend
4772-
.get();
4784+
let suspend_ptr = unsafe {
4785+
&raw mut (*store_ref.store_opaque_mut().concurrent_async_state())
4786+
.current_suspend
4787+
};
47734788
// SAFETY: The resumer is responsible for setting
47744789
// `current_suspend` to a valid pointer.
47754790
let _reset = Reset(suspend_ptr, unsafe { *suspend_ptr });
@@ -4780,12 +4795,14 @@ fn make_fiber<'a>(
47804795
)?),
47814796
state: Some(AsyncWasmCallState::new()),
47824797
engine,
4783-
suspend: store
4784-
.store_opaque_mut()
4785-
.concurrent_async_state()
4786-
.current_suspend
4787-
.get(),
4798+
suspend: unsafe {
4799+
&raw mut (*store.store_opaque_mut().concurrent_async_state()).current_suspend
4800+
},
47884801
stack_limit: store.vm_store_context().stack_limit.get(),
4802+
executor_ptr: unsafe {
4803+
&raw mut (*store.store_opaque_mut().concurrent_async_state()).current_executor
4804+
},
4805+
executor,
47894806
})
47904807
}
47914808

@@ -4817,6 +4834,8 @@ unsafe fn resume_fiber_raw<'a>(
48174834
}
48184835
}
48194836

4837+
let _reset_executor = Reset((*fiber).executor_ptr, *(*fiber).executor_ptr);
4838+
*(*fiber).executor_ptr = &raw mut (*fiber).executor;
48204839
let _reset_suspend = Reset((*fiber).suspend, *(*fiber).suspend);
48214840
let _reset_stack_limit = Reset((*fiber).stack_limit, *(*fiber).stack_limit);
48224841
let state = Some((*fiber).state.take().unwrap().push());
@@ -5165,7 +5184,7 @@ async fn poll_fn<R>(
51655184

51665185
unsafe impl Send for PollCx {}
51675186

5168-
let poll_cx = PollCx(store.concurrent_async_state().current_poll_cx.get());
5187+
let poll_cx = PollCx(unsafe { &raw mut (*store.concurrent_async_state()).current_poll_cx });
51695188
future::poll_fn({
51705189
let mut store = Some(VMStoreRawPtr(store.traitobj()));
51715190

crates/wasmtime/src/runtime/store.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ pub struct StoreOpaque {
410410
///
411411
/// Effectively stores Pulley interpreter state and handles conditional support
412412
/// for Cranelift at compile time.
413-
enum Executor {
413+
pub(crate) enum Executor {
414414
Interpreter(Interpreter),
415415
#[cfg(has_host_compiler_backend)]
416416
Native,
@@ -600,6 +600,11 @@ impl<T: 'static> Store<T> {
600600
data: ManuallyDrop::new(data),
601601
});
602602

603+
#[cfg(feature = "component-model-async")]
604+
{
605+
inner.concurrent_async_state.current_executor = &raw mut inner.executor;
606+
}
607+
603608
inner.traitobj = StorePtr::new(NonNull::from(&mut *inner));
604609

605610
// Wasmtime uses the callee argument to host functions to learn about
@@ -1931,8 +1936,8 @@ at https://bytecodealliance.org/security.
19311936
}
19321937

19331938
#[cfg(feature = "component-model-async")]
1934-
pub(crate) fn concurrent_async_state(&mut self) -> &mut concurrent::AsyncState {
1935-
&mut self.concurrent_async_state
1939+
pub(crate) fn concurrent_async_state(&mut self) -> *mut concurrent::AsyncState {
1940+
&raw mut self.concurrent_async_state
19361941
}
19371942

19381943
#[cfg(feature = "component-model-async")]
@@ -1946,15 +1951,31 @@ at https://bytecodealliance.org/security.
19461951
}
19471952

19481953
pub(crate) fn executor(&mut self) -> ExecutorRef<'_> {
1949-
match &mut self.executor {
1954+
#[cfg(feature = "component-model-async")]
1955+
let executor = unsafe {
1956+
assert!(!self.concurrent_async_state.current_executor.is_null());
1957+
&mut *self.concurrent_async_state.current_executor
1958+
};
1959+
#[cfg(not(feature = "component-model-async"))]
1960+
let executor = &mut self.executor;
1961+
1962+
match executor {
19501963
Executor::Interpreter(i) => ExecutorRef::Interpreter(i.as_interpreter_ref()),
19511964
#[cfg(has_host_compiler_backend)]
19521965
Executor::Native => ExecutorRef::Native,
19531966
}
19541967
}
19551968

19561969
pub(crate) fn unwinder(&self) -> &'static dyn Unwind {
1957-
match &self.executor {
1970+
#[cfg(feature = "component-model-async")]
1971+
let executor = unsafe {
1972+
assert!(!self.concurrent_async_state.current_executor.is_null());
1973+
&*self.concurrent_async_state.current_executor
1974+
};
1975+
#[cfg(not(feature = "component-model-async"))]
1976+
let executor = &self.executor;
1977+
1978+
match executor {
19581979
Executor::Interpreter(i) => i.unwinder(),
19591980
#[cfg(has_host_compiler_backend)]
19601981
Executor::Native => &vm::UnwindHost,

crates/wasmtime/src/runtime/store/async_.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,7 @@ impl StoreOpaque {
750750
pub(crate) fn async_guard_range(&mut self) -> Range<*mut u8> {
751751
#[cfg(feature = "component-model-async")]
752752
{
753-
self.concurrent_async_state().async_guard_range()
753+
unsafe { (*self.concurrent_async_state()).async_guard_range() }
754754
}
755755
#[cfg(not(feature = "component-model-async"))]
756756
unsafe {

0 commit comments

Comments
 (0)