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

Commit 6e501c1

Browse files
authored
Merge pull request #180 from bytecodealliance/dicej/safer-instances-part1
eliminate `ComponentInstance` unsafe code in futures_and_streams.rs
2 parents 00a22a8 + daac0be commit 6e501c1

11 files changed

Lines changed: 472 additions & 589 deletions

File tree

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

Lines changed: 59 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ where
288288
token: StoreToken<T>,
289289
get: fn() -> *mut dyn VMStore,
290290
get_data: fn(&mut T) -> D::Data<'_>,
291-
instance: Option<Instance>,
291+
instance: Instance,
292292
}
293293

294294
impl<T> Accessor<T> {
@@ -309,7 +309,7 @@ impl<T> Accessor<T> {
309309
/// intended scope. If it returns, the caller must be granted exclusive
310310
/// access to that store until the call to `Future::poll` for the current
311311
/// host task returns.
312-
unsafe fn new(token: StoreToken<T>, instance: Option<Instance>) -> Self {
312+
unsafe fn new(token: StoreToken<T>, instance: Instance) -> Self {
313313
Self {
314314
token,
315315
get: get_store,
@@ -377,7 +377,7 @@ where
377377
where
378378
T: 'static,
379379
{
380-
let instance = self.instance.unwrap();
380+
let instance = self.instance;
381381
let accessor = Self {
382382
token: self.token,
383383
get: self.get,
@@ -391,11 +391,6 @@ where
391391

392392
/// Retrieve the component instance of the caller.
393393
pub fn instance(&self) -> Instance {
394-
self.instance.unwrap()
395-
}
396-
397-
#[doc(hidden)]
398-
pub fn maybe_instance(&self) -> Option<Instance> {
399394
self.instance
400395
}
401396
}
@@ -515,16 +510,15 @@ enum InstanceThreadLocalState {
515510
/// they can be mutably referenced without either one aliasing the other.
516511
Detached {
517512
instance: SendSyncPtr<ComponentInstance>,
513+
handle: Instance,
518514
store: VMStoreRawPtr,
519515
},
520516
/// The specified instance's event loop is currently polling a future, and
521-
/// the instance handle is available via the `instance` field.
517+
/// the instance handle is available via the `handle` field.
522518
///
523519
/// In this case, the store and instance are in an "attached" state, meaning
524520
/// care must be taken to avoid creating mutable reference aliases.
525-
Attached {
526-
instance: SendSyncPtr<ComponentInstance>,
527-
},
521+
Attached { handle: Instance },
528522
}
529523

530524
impl fmt::Debug for InstanceThreadLocalState {
@@ -560,7 +554,10 @@ fn with_local_instance<R>(fun: impl FnOnce(&mut dyn VMStore, &mut ComponentInsta
560554
INSTANCE_STATE.with(|v| v.replace(InstanceThreadLocalState::Polling)),
561555
);
562556

563-
let InstanceThreadLocalState::Detached { instance, store } = state.0 else {
557+
let InstanceThreadLocalState::Detached {
558+
instance, store, ..
559+
} = state.0
560+
else {
564561
unreachable!("expected `Detached`; got `{:?}`", state.0)
565562
};
566563
let (store, instance) = unsafe { (&mut *store.0.as_ptr(), &mut *instance.as_ptr()) };
@@ -581,6 +578,7 @@ fn poll_with_local_instance<F: Future + Send + ?Sized>(
581578
let state = ResetInstanceThreadLocalState(INSTANCE_STATE.with(|v| {
582579
v.replace(InstanceThreadLocalState::Detached {
583580
instance: SendSyncPtr::new(instance.into()),
581+
handle: instance.instance,
584582
store: VMStoreRawPtr(store.into()),
585583
})
586584
}));
@@ -819,24 +817,7 @@ impl fmt::Debug for WorkItem {
819817
}
820818

821819
impl<T> StoreContextMut<'_, T> {
822-
/// Temporarily split the specified store and instance into a "detached"
823-
/// state (i.e. clearing the pointers they have to each other) so that they
824-
/// can both safely be exclusively referenced without accidentally aliasing
825-
/// one via the other.
826-
///
827-
/// This will take the specified instance out of the store, null out the
828-
/// pointer in the instance which points back to the store, call the
829-
/// specified function with exclusive references to both, and then reset
830-
/// everything back to the way it was before returning a result.
831-
///
832-
/// This is appropriate for use in code which needs safe, exclusive access
833-
/// to both the store and the instance simultaneously. Note that you'll
834-
/// need to temporarily re-attach the instance to the store using
835-
/// `with_attached_instance` when calling guest code or calling
836-
/// application-defined host functions, since they both expect the instance
837-
/// to be in an "attached" state.
838-
///
839-
/// See also `with_detached_instance_async` for async closures.
820+
/// Temporary code which will go away soon. Nothing to see here, folks.
840821
fn with_detached_instance<R>(
841822
&mut self,
842823
instance: &Instance,
@@ -849,18 +830,22 @@ impl<T> StoreContextMut<'_, T> {
849830
}
850831
_ => unreachable!(),
851832
}));
852-
let ptr = self.0.hide_instance(*instance);
853-
// SAFETY: We've taken the instance out of the store, so now we own
854-
// it and can take an exclusive reference to it.
855-
let reference = unsafe { &mut *ptr };
856-
reference.set_store(None);
857-
let result = fun(self.as_context_mut(), reference);
858-
reference.set_store(Some(VMStoreRawPtr(self.traitobj())));
859-
self.0.unhide_instance(*instance);
860-
result
833+
// SAFETY: This isn't safe at all, since `fun` will easily be able to
834+
// create aliases of `instance` using
835+
// e.g. `StoreOpaque::component_instance_mut`. As of this writing,
836+
// we're relying on `fun` never doing that except within a
837+
// `with_attached_instance` closure where it's sound.
838+
//
839+
// We're planning to remove `with_{a,de}tached_instance[_async]` soon
840+
// anyway and migrate the code which uses it to deal in `Instance`
841+
// parameters rather than `&mut ComponentInstance` parameters,
842+
// retrieving the latter only as necessary, with soundness enforced by
843+
// the borrow checker, at which point this will go away.
844+
let instance = unsafe { &mut *self.0[instance.0].as_mut().unwrap().instance_ptr() };
845+
fun(self.as_context_mut(), instance)
861846
}
862847

863-
/// Same as `with_detached_instance`, but for async closures.
848+
/// Temporary code which will go away soon. Nothing to see here, folks.
864849
async fn with_detached_instance_async<R>(
865850
&mut self,
866851
instance: &Instance,
@@ -873,55 +858,47 @@ impl<T> StoreContextMut<'_, T> {
873858
}
874859
_ => unreachable!(),
875860
}));
876-
let ptr = self.0.hide_instance(*instance);
877-
// SAFETY: We've taken the instance out of the store, so now we own
878-
// it and can take an exclusive reference to it.
879-
let reference = unsafe { &mut *ptr };
880-
reference.set_store(None);
881-
let result = fun(self.as_context_mut(), reference).await;
882-
reference.set_store(Some(VMStoreRawPtr(self.traitobj())));
883-
self.0.unhide_instance(*instance);
884-
result
861+
// SAFETY: See corresponding comment in `with_detached_instance`
862+
let instance = unsafe { &mut *self.0[instance.0].as_mut().unwrap().instance_ptr() };
863+
fun(self.as_context_mut(), instance).await
885864
}
886865

887-
/// Temporarily the operation performed by `with_detached_instance{_async}`.
888-
/// See the docs for those functions for details.
866+
/// Temporary code which will go away soon. Nothing to see here, folks.
889867
pub(crate) fn with_attached_instance<R>(
890868
&mut self,
891869
instance: &mut ComponentInstance,
892-
fun: impl FnOnce(StoreContextMut<'_, T>, Option<Instance>) -> R,
870+
fun: impl FnOnce(StoreContextMut<'_, T>, Instance) -> R,
893871
) -> R {
894872
let _state = ResetInstanceThreadLocalState(INSTANCE_STATE.with(|v| match v.get() {
895873
state @ InstanceThreadLocalState::None => state,
896-
state @ InstanceThreadLocalState::Polling => {
897-
if instance.instance.is_some() {
898-
v.replace(InstanceThreadLocalState::Attached {
899-
instance: SendSyncPtr::new(instance.into()),
900-
})
901-
} else {
902-
state
903-
}
904-
}
874+
InstanceThreadLocalState::Polling => v.replace(InstanceThreadLocalState::Attached {
875+
handle: instance.instance,
876+
}),
905877
_ => unreachable!(),
906878
}));
907-
if let Some(handle) = instance.instance {
908-
self.0.unhide_instance(handle);
909-
}
910-
instance.set_store(Some(VMStoreRawPtr(self.traitobj())));
911-
let result = fun(self.as_context_mut(), instance.instance);
912-
instance.set_store(None);
913-
if let Some(handle) = instance.instance {
914-
self.0.hide_instance(handle);
915-
}
916-
result
879+
fun(self.as_context_mut(), instance.instance)
917880
}
918881
}
919882

920-
impl ComponentInstance {
921-
pub(crate) fn instance(&self) -> Option<Instance> {
922-
self.instance
883+
impl StoreOpaque {
884+
/// Temporary code which will go away soon. Nothing to see here, folks.
885+
pub(crate) fn with_attached_instance<R>(
886+
&mut self,
887+
instance: &mut ComponentInstance,
888+
fun: impl FnOnce(&mut StoreOpaque, Instance) -> R,
889+
) -> R {
890+
let _state = ResetInstanceThreadLocalState(INSTANCE_STATE.with(|v| match v.get() {
891+
state @ InstanceThreadLocalState::None => state,
892+
InstanceThreadLocalState::Polling => v.replace(InstanceThreadLocalState::Attached {
893+
handle: instance.instance,
894+
}),
895+
_ => unreachable!(),
896+
}));
897+
fun(self, instance.instance)
923898
}
899+
}
924900

901+
impl ComponentInstance {
925902
fn instance_state(&mut self, instance: RuntimeComponentInstanceIndex) -> &mut InstanceState {
926903
self.concurrent_state
927904
.instance_states
@@ -1887,7 +1864,7 @@ impl ComponentInstance {
18871864
// thread-local variable which `poll_with_state` will populate and reset
18881865
// with valid pointers to the store data and the store itself each time
18891866
// the returned future is polled, respectively.
1890-
let mut accessor = unsafe { Accessor::new(token, self.instance()) };
1867+
let mut accessor = unsafe { Accessor::new(token, self.instance) };
18911868
let mut future = Box::pin(async move { closure(&mut accessor, params).await });
18921869
Box::pin(future::poll_fn(move |cx| {
18931870
poll_with_state(token, cx, future.as_mut())
@@ -3358,7 +3335,7 @@ impl Instance {
33583335
.with_detached_instance(self, |store, instance| {
33593336
let token = StoreToken::new(store);
33603337
// SAFETY: See corresponding comment in `ComponentInstance::wrap_call`.
3361-
let mut accessor = unsafe { Accessor::new(token, instance.instance()) };
3338+
let mut accessor = unsafe { Accessor::new(token, instance.instance) };
33623339
let mut future = Box::pin(async move { fun(&mut accessor).await });
33633340
Box::pin(future::poll_fn(move |cx| {
33643341
poll_with_state(token, cx, future.as_mut())
@@ -3382,8 +3359,7 @@ impl Instance {
33823359
) -> AbortHandle {
33833360
let mut store = store.as_context_mut();
33843361
// SAFETY: TODO
3385-
let accessor =
3386-
unsafe { Accessor::new(StoreToken::new(store.as_context_mut()), Some(*self)) };
3362+
let accessor = unsafe { Accessor::new(StoreToken::new(store.as_context_mut()), *self) };
33873363
self.spawn_with_accessor(store, accessor, task)
33883364
}
33893365

@@ -4751,7 +4727,7 @@ fn for_any_lift<
47514727
///
47524728
/// See `Instance::run` for details.
47534729
fn checked<F: Future + Send + 'static>(
4754-
instance: SendSyncPtr<ComponentInstance>,
4730+
instance: Instance,
47554731
fut: F,
47564732
) -> impl Future<Output = F::Output> + Send + 'static {
47574733
let mut fut = Box::pin(fut);
@@ -4764,12 +4740,8 @@ fn checked<F: Future + Send + 'static>(
47644740
";
47654741
INSTANCE_STATE.with(|v| {
47664742
let matched = match v.get() {
4767-
InstanceThreadLocalState::Detached {
4768-
instance: local, ..
4769-
}
4770-
| InstanceThreadLocalState::Attached { instance: local } => {
4771-
local.as_ptr() == instance.as_ptr()
4772-
}
4743+
InstanceThreadLocalState::Detached { handle, .. }
4744+
| InstanceThreadLocalState::Attached { handle } => handle.0 == instance.0,
47734745
InstanceThreadLocalState::None => false,
47744746
InstanceThreadLocalState::Polling => unreachable!(),
47754747
};
@@ -5299,12 +5271,8 @@ pub(crate) fn queue_call<T: 'static, R: Send + 'static>(
52995271

53005272
queue_call0(store.as_context_mut(), handle, task, param_count)?;
53015273

5302-
let func_data = &store[handle.0];
5303-
let instance = func_data.instance;
5304-
let instance_ptr = store.0[instance.0].as_ref().unwrap().instance_ptr();
5305-
53065274
Ok(checked(
5307-
SendSyncPtr::new(NonNull::new(instance_ptr).unwrap()),
5275+
store[handle.0].instance,
53085276
rx.map(|result| {
53095277
result
53105278
.map(|v| *v.downcast().unwrap())

0 commit comments

Comments
 (0)