Skip to content

Commit e9c5de8

Browse files
alexcrichtonbongjunj
authored andcommitted
Require a store in catch_unwind_and_record_trap (bytecodealliance#11441)
* Require a store in `catch_unwind_and_record_trap` This commit does some preparatory refactoring for bytecodealliance#11326 to ensure that a store is available when trap information is being processed. Currently this doesn't leverage the new parameter but it should be leverage-able in bytecodealliance#11326. * Review comments
1 parent 177b88a commit e9c5de8

File tree

10 files changed

+182
-243
lines changed

10 files changed

+182
-243
lines changed

crates/wasmtime/src/runtime/component/func/host.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -683,15 +683,12 @@ where
683683
{
684684
let cx = unsafe { VMComponentContext::from_opaque(cx) };
685685
unsafe {
686-
ComponentInstance::from_vmctx(cx, |store, instance| {
686+
ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
687687
let mut store = store.unchecked_context_mut();
688-
689-
crate::runtime::vm::catch_unwind_and_record_trap(|| {
690-
store.0.call_hook(CallHook::CallingHost)?;
691-
let res = func(store.as_context_mut(), instance);
692-
store.0.call_hook(CallHook::ReturningFromHost)?;
693-
res
694-
})
688+
store.0.call_hook(CallHook::CallingHost)?;
689+
let res = func(store.as_context_mut(), instance);
690+
store.0.call_hook(CallHook::ReturningFromHost)?;
691+
res
695692
})
696693
}
697694
}

crates/wasmtime/src/runtime/func.rs

Lines changed: 44 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use crate::prelude::*;
22
use crate::runtime::Uninhabited;
33
use crate::runtime::vm::{
4-
InterpreterRef, SendSyncPtr, StoreBox, VMArrayCallHostFuncContext, VMCommonStackInformation,
5-
VMContext, VMFuncRef, VMFunctionImport, VMOpaqueContext, VMStoreContext,
4+
self, InterpreterRef, SendSyncPtr, StoreBox, VMArrayCallHostFuncContext,
5+
VMCommonStackInformation, VMContext, VMFuncRef, VMFunctionImport, VMOpaqueContext,
6+
VMStoreContext,
67
};
7-
use crate::store::{AutoAssertNoGc, StoreId, StoreOpaque};
8+
use crate::store::{AutoAssertNoGc, InstanceId, StoreId, StoreOpaque};
89
use crate::type_registry::RegisteredType;
910
use crate::{
1011
AsContext, AsContextMut, CallHook, Engine, Extern, FuncType, Instance, ModuleExport, Ref,
@@ -2041,46 +2042,29 @@ impl<T> Caller<'_, T> {
20412042

20422043
/// Executes `f` with an appropriate `Caller`.
20432044
///
2044-
/// This is the entrypoint for host functions in core wasm and converts from
2045-
/// `VMContext` to `Caller`
2046-
///
2047-
/// # Safety
2048-
///
2049-
/// This requires that `caller` is safe to wrap up as a `Caller`,
2050-
/// effectively meaning that we just entered the host from wasm.
2051-
/// Additionally this `Caller`'s `T` parameter must match the actual `T` in
2052-
/// the store of the vmctx of `caller`.
2053-
unsafe fn with<F, R>(caller: NonNull<VMContext>, f: F) -> R
2045+
/// This is the entrypoint for host functions in core wasm. The `store` and
2046+
/// `instance` are created from `Instance::enter_host_from_wasm` and this
2047+
/// will further invoke the host function that `f` refers to.
2048+
fn with<F, R>(mut store: StoreContextMut<T>, caller: InstanceId, f: F) -> R
20542049
where
20552050
F: FnOnce(Caller<'_, T>) -> R,
20562051
{
2057-
// SAFETY: it's a contract of this function itself that `from_vmctx` is
2058-
// safe to call. Additionally it's a contract of this function itself
2059-
// that the `T` of `Caller` matches the store.
2060-
unsafe {
2061-
crate::runtime::vm::InstanceAndStore::from_vmctx(caller, |pair| {
2062-
let (instance, store) = pair.unpack_mut();
2063-
let mut store = store.unchecked_context_mut::<T>();
2064-
let caller = Instance::from_wasmtime(instance.id(), store.0);
2052+
let caller = Instance::from_wasmtime(caller, store.0);
20652053

2066-
let (gc_lifo_scope, ret) = {
2067-
let gc_lifo_scope = store.0.gc_roots().enter_lifo_scope();
2054+
let (gc_lifo_scope, ret) = {
2055+
let gc_lifo_scope = store.0.gc_roots().enter_lifo_scope();
20682056

2069-
let ret = f(Caller {
2070-
store: store.as_context_mut(),
2071-
caller,
2072-
});
2057+
let ret = f(Caller {
2058+
store: store.as_context_mut(),
2059+
caller,
2060+
});
20732061

2074-
(gc_lifo_scope, ret)
2075-
};
2062+
(gc_lifo_scope, ret)
2063+
};
20762064

2077-
// Safe to recreate a mutable borrow of the store because `ret`
2078-
// cannot be borrowing from the store.
2079-
store.0.exit_gc_lifo_scope(gc_lifo_scope);
2065+
store.0.exit_gc_lifo_scope(gc_lifo_scope);
20802066

2081-
ret
2082-
})
2083-
}
2067+
ret
20842068
}
20852069

20862070
fn sub_caller(&mut self) -> Caller<'_, T> {
@@ -2409,10 +2393,14 @@ impl HostContext {
24092393
// closure and then run it as part of `Caller::with`.
24102394
//
24112395
// SAFETY: this is an entrypoint of wasm which requires correct type
2412-
// ascription of `T` itself, meaning that this should be safe to call.
2413-
crate::runtime::vm::catch_unwind_and_record_trap(move || unsafe {
2414-
Caller::with(caller_vmctx, run)
2415-
})
2396+
// ascription of `T` itself, meaning that this should be safe to call
2397+
// both `enter_host_from_wasm` as well as `unchecked_context_mut`.
2398+
unsafe {
2399+
vm::Instance::enter_host_from_wasm(caller_vmctx, |store, instance| {
2400+
let store = store.unchecked_context_mut();
2401+
Caller::with(store, instance.id(), run)
2402+
})
2403+
}
24162404
}
24172405
}
24182406

@@ -2485,20 +2473,23 @@ impl HostFunc {
24852473
T: 'static,
24862474
{
24872475
assert!(ty.comes_from_same_engine(engine));
2488-
// SAFETY: This is only only called in the raw entrypoint of wasm
2489-
// meaning that `caller_vmctx` is appropriate to read, and additionally
2490-
// the later usage of `{,in}to_func` will connect `T` to an actual
2491-
// store's `T` to ensure it's the same.
2492-
let func = move |caller_vmctx, values: &mut [ValRaw]| unsafe {
2493-
Caller::<T>::with(caller_vmctx, |mut caller| {
2494-
caller.store.0.call_hook(CallHook::CallingHost)?;
2495-
let result = func(caller.sub_caller(), values)?;
2496-
caller.store.0.call_hook(CallHook::ReturningFromHost)?;
2497-
Ok(result)
2498-
})
2499-
};
2500-
let ctx = crate::trampoline::create_array_call_function(&ty, func)
2501-
.expect("failed to create function");
2476+
let ctx = crate::trampoline::create_array_call_function(
2477+
&ty,
2478+
move |store, instance, values: &mut [ValRaw]| {
2479+
// SAFETY: the later usage of `{,in}to_func` will connect `T` to
2480+
// an actual store's `T` to ensure it's the same. This means
2481+
// that the store this is invoked with always has `T` as a type
2482+
// parameter which should make this cast safe.
2483+
let store = unsafe { store.unchecked_context_mut::<T>() };
2484+
Caller::with(store, instance, |mut caller| {
2485+
caller.store.0.call_hook(CallHook::CallingHost)?;
2486+
let result = func(caller.sub_caller(), values)?;
2487+
caller.store.0.call_hook(CallHook::ReturningFromHost)?;
2488+
Ok(result)
2489+
})
2490+
},
2491+
)
2492+
.expect("failed to create function");
25022493
HostFunc::_new(engine, ctx.into())
25032494
}
25042495

crates/wasmtime/src/runtime/trampoline/func.rs

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Support for a calling of an imported function.
22
33
use crate::prelude::*;
4-
use crate::runtime::vm::{StoreBox, VMArrayCallHostFuncContext, VMContext, VMOpaqueContext};
4+
use crate::runtime::vm::{
5+
Instance, StoreBox, VMArrayCallHostFuncContext, VMContext, VMOpaqueContext, VMStore,
6+
};
7+
use crate::store::InstanceId;
58
use crate::type_registry::RegisteredType;
69
use crate::{FuncType, ValRaw};
710
use core::ptr::NonNull;
@@ -32,41 +35,45 @@ unsafe extern "C" fn array_call_shim<F>(
3235
values_vec_len: usize,
3336
) -> bool
3437
where
35-
F: Fn(NonNull<VMContext>, &mut [ValRaw]) -> Result<()> + 'static,
38+
F: Fn(&mut dyn VMStore, InstanceId, &mut [ValRaw]) -> Result<()> + 'static,
3639
{
37-
// Be sure to catch Rust panics to manually shepherd them across the wasm
38-
// boundary, and then otherwise delegate as normal.
39-
crate::runtime::vm::catch_unwind_and_record_trap(|| {
40-
// SAFETY: this function itself requires that the `vmctx` is valid to
41-
// use here.
42-
let state = unsafe {
43-
let vmctx = VMArrayCallHostFuncContext::from_opaque(vmctx);
44-
vmctx.as_ref().host_state()
45-
};
40+
// SAFETY: this is an entrypoint of wasm calling a host and our parameters
41+
// should reflect that making `enter_host_from_wasm` suitable. Further
42+
// unsafe operations are commented below.
43+
unsafe {
44+
Instance::enter_host_from_wasm(caller_vmctx, |store, instance| {
45+
let instance = instance.id();
46+
// SAFETY: this function itself requires that the `vmctx` is valid to
47+
// use here.
48+
let state = {
49+
let vmctx = VMArrayCallHostFuncContext::from_opaque(vmctx);
50+
vmctx.as_ref().host_state()
51+
};
4652

47-
// Double-check ourselves in debug mode, but we control
48-
// the `Any` here so an unsafe downcast should also
49-
// work.
50-
//
51-
// SAFETY: this function is only usable with `TrampolineState<F>`.
52-
let state = unsafe {
53-
debug_assert!(state.is::<TrampolineState<F>>());
54-
&*(state as *const _ as *const TrampolineState<F>)
55-
};
56-
let mut values_vec = NonNull::slice_from_raw_parts(values_vec, values_vec_len);
57-
// SAFETY: it's a contract of this function itself that the values
58-
// provided are valid to view as a slice.
59-
let values_vec = unsafe { values_vec.as_mut() };
60-
(state.func)(caller_vmctx, values_vec)
61-
})
53+
// Double-check ourselves in debug mode, but we control
54+
// the `Any` here so an unsafe downcast should also
55+
// work.
56+
//
57+
// SAFETY: this function is only usable with `TrampolineState<F>`.
58+
let state = {
59+
debug_assert!(state.is::<TrampolineState<F>>());
60+
&*(state as *const _ as *const TrampolineState<F>)
61+
};
62+
let mut values_vec = NonNull::slice_from_raw_parts(values_vec, values_vec_len);
63+
// SAFETY: it's a contract of this function itself that the values
64+
// provided are valid to view as a slice.
65+
let values_vec = values_vec.as_mut();
66+
(state.func)(store, instance, values_vec)
67+
})
68+
}
6269
}
6370

6471
pub fn create_array_call_function<F>(
6572
ft: &FuncType,
6673
func: F,
6774
) -> Result<StoreBox<VMArrayCallHostFuncContext>>
6875
where
69-
F: Fn(NonNull<VMContext>, &mut [ValRaw]) -> Result<()> + Send + Sync + 'static,
76+
F: Fn(&mut dyn VMStore, InstanceId, &mut [ValRaw]) -> Result<()> + Send + Sync + 'static,
7077
{
7178
let array_call = array_call_shim::<F>;
7279

crates/wasmtime/src/runtime/vm.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ pub use crate::runtime::vm::gc::*;
9797
pub use crate::runtime::vm::imports::Imports;
9898
pub use crate::runtime::vm::instance::{
9999
GcHeapAllocationIndex, Instance, InstanceAllocationRequest, InstanceAllocator,
100-
InstanceAllocatorImpl, InstanceAndStore, InstanceHandle, MemoryAllocationIndex,
101-
OnDemandInstanceAllocator, StorePtr, TableAllocationIndex, initialize_instance,
100+
InstanceAllocatorImpl, InstanceHandle, MemoryAllocationIndex, OnDemandInstanceAllocator,
101+
StorePtr, TableAllocationIndex, initialize_instance,
102102
};
103103
#[cfg(feature = "pooling-allocator")]
104104
pub use crate::runtime::vm::instance::{

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

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ use crate::runtime::component::ComponentInstanceId;
1111
use crate::runtime::vm::instance::{InstanceLayout, OwnedInstance, OwnedVMContext};
1212
use crate::runtime::vm::vmcontext::VMFunctionBody;
1313
use crate::runtime::vm::{
14-
SendSyncPtr, VMArrayCallFunction, VMFuncRef, VMGlobalDefinition, VMMemoryDefinition,
15-
VMOpaqueContext, VMStore, VMStoreRawPtr, VMTableImport, VMWasmCallFunction, ValRaw, VmPtr,
16-
VmSafe,
14+
HostResult, SendSyncPtr, VMArrayCallFunction, VMFuncRef, VMGlobalDefinition,
15+
VMMemoryDefinition, VMOpaqueContext, VMStore, VMStoreRawPtr, VMTableImport, VMWasmCallFunction,
16+
ValRaw, VmPtr, VmSafe, catch_unwind_and_record_trap,
1717
};
1818
use crate::store::InstanceId;
1919
use alloc::alloc::Layout;
@@ -203,17 +203,24 @@ impl ComponentInstance {
203203
/// Converts the `vmctx` provided into a `ComponentInstance` and runs the
204204
/// provided closure with that instance.
205205
///
206+
/// This function will also catch any failures that `f` produces and returns
207+
/// an appropriate ABI value to return to wasm. This includes normal errors
208+
/// such as traps as well as Rust-side panics which require wasm to unwind.
209+
///
206210
/// # Unsafety
207211
///
208212
/// This is `unsafe` because `vmctx` cannot be guaranteed to be a valid
209213
/// pointer and it cannot be proven statically that it's safe to get a
210214
/// mutable reference at this time to the instance from `vmctx`. Note that
211215
/// it must be also safe to borrow the store mutably, meaning it can't
212216
/// already be in use elsewhere.
213-
pub unsafe fn from_vmctx<R>(
217+
pub unsafe fn enter_host_from_wasm<R>(
214218
vmctx: NonNull<VMComponentContext>,
215219
f: impl FnOnce(&mut dyn VMStore, Instance) -> R,
216-
) -> R {
220+
) -> R::Abi
221+
where
222+
R: HostResult,
223+
{
217224
// SAFETY: it's a contract of this function that `vmctx` is a valid
218225
// allocation which can go backwards to a `ComponentInstance`.
219226
let mut ptr = unsafe {
@@ -230,7 +237,7 @@ impl ComponentInstance {
230237
let store = unsafe { &mut *reference.store.0.as_ptr() };
231238

232239
let instance = Instance::from_wasmtime(store, reference.id);
233-
f(store, instance)
240+
catch_unwind_and_record_trap(store, |store| f(store, instance))
234241
}
235242

236243
/// Returns the `InstanceId` associated with the `vmctx` provided.

crates/wasmtime/src/runtime/vm/component/libcalls.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,11 @@ mod trampolines {
9999
{
100100
$(shims!(@validate_param $pname $param);)*
101101

102-
let ret = crate::runtime::vm::traphandlers::catch_unwind_and_record_trap(|| unsafe {
103-
ComponentInstance::from_vmctx(vmctx, |store, instance| {
102+
let ret = unsafe {
103+
ComponentInstance::enter_host_from_wasm(vmctx, |store, instance| {
104104
shims!(@invoke $name(store, instance,) $($pname)*)
105105
})
106-
});
106+
};
107107
shims!(@convert_ret ret $($pname: $param)*)
108108
}
109109
$(

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

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#![doc(hidden)]
22

3-
use crate::runtime::vm::instance::InstanceAndStore;
4-
use crate::runtime::vm::vmcontext::VMContext;
3+
use crate::runtime::vm::{Instance, VMContext};
54
use core::ptr::NonNull;
65
use wasmtime_environ::{EntityRef, MemoryIndex};
76
use wasmtime_versioned_export_macros::versioned_export;
@@ -19,8 +18,7 @@ pub unsafe extern "C" fn resolve_vmctx_memory_ptr(p: *const u32) -> *const u8 {
1918
VMCTX_AND_MEMORY.0 != NonNull::dangling(),
2019
"must call `__vmctx->set()` before resolving Wasm pointers"
2120
);
22-
InstanceAndStore::from_vmctx(VMCTX_AND_MEMORY.0, |handle| {
23-
let (handle, _) = handle.unpack_mut();
21+
Instance::enter_host_from_wasm(VMCTX_AND_MEMORY.0, |_store, handle| {
2422
assert!(
2523
VMCTX_AND_MEMORY.1 < handle.env_module().memories.len(),
2624
"memory index for debugger is out of bounds"

0 commit comments

Comments
 (0)