diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs index 93795cd6da89..7a960de15f98 100644 --- a/crates/cranelift/src/compiler/component.rs +++ b/crates/cranelift/src/compiler/component.rs @@ -119,6 +119,8 @@ impl<'a> TrampolineCompiler<'a> { } fn translate(&mut self, trampoline: &Trampoline) { + self.check_may_leave(trampoline); + match trampoline { Trampoline::Transcoder { op, @@ -1202,23 +1204,7 @@ impl<'a> TrampolineCompiler<'a> { // calling itself. let old_may_block = if let Some(def) = resource_def { if self.types[resource].unwrap_concrete_instance() != def.instance { - let flags = self.builder.ins().load( - ir::types::I32, - trusted, - vmctx, - i32::try_from( - self.offsets - .instance_flags(self.types[resource].unwrap_concrete_instance()), - ) - .unwrap(), - ); - let masked = self - .builder - .ins() - .band_imm(flags, i64::from(FLAG_MAY_LEAVE)); - self.builder - .ins() - .trapz(masked, TRAP_CANNOT_LEAVE_COMPONENT); + self.check_may_leave_instance(self.types[resource].unwrap_concrete_instance()); if self.compiler.tunables.concurrency_support { // Stash the old value of `may_block` and then set it to false. @@ -1471,6 +1457,92 @@ impl<'a> TrampolineCompiler<'a> { self.compiler .call_indirect_host(&mut self.builder, index, host_sig, host_fn, args) } + + fn check_may_leave(&mut self, trampoline: &Trampoline) { + let instance = match trampoline { + // These intrinsics explicitly do not check the may-leave flag. + Trampoline::ResourceRep { .. } + | Trampoline::ThreadIndex + | Trampoline::BackpressureInc { .. } + | Trampoline::BackpressureDec { .. } + | Trampoline::ContextGet { .. } + | Trampoline::ContextSet { .. } => return, + + // Intrinsics used in adapters generated by FACT that aren't called + // directly from guest wasm, so no check is needed. + Trampoline::ResourceTransferOwn + | Trampoline::ResourceTransferBorrow + | Trampoline::ResourceEnterCall + | Trampoline::ResourceExitCall + | Trampoline::PrepareCall { .. } + | Trampoline::SyncStartCall { .. } + | Trampoline::AsyncStartCall { .. } + | Trampoline::FutureTransfer + | Trampoline::StreamTransfer + | Trampoline::ErrorContextTransfer + | Trampoline::Trap + | Trampoline::EnterSyncCall + | Trampoline::ExitSyncCall + | Trampoline::Transcoder { .. } => return, + + Trampoline::LowerImport { options, .. } => self.component.options[*options].instance, + + Trampoline::ResourceNew { instance, .. } + | Trampoline::ResourceDrop { instance, .. } + | Trampoline::TaskReturn { instance, .. } + | Trampoline::TaskCancel { instance } + | Trampoline::WaitableSetNew { instance } + | Trampoline::WaitableSetWait { instance, .. } + | Trampoline::WaitableSetPoll { instance, .. } + | Trampoline::WaitableSetDrop { instance } + | Trampoline::WaitableJoin { instance } + | Trampoline::ThreadYield { instance, .. } + | Trampoline::ThreadSwitchTo { instance, .. } + | Trampoline::ThreadNewIndirect { instance, .. } + | Trampoline::ThreadSuspend { instance, .. } + | Trampoline::ThreadResumeLater { instance } + | Trampoline::ThreadYieldTo { instance, .. } + | Trampoline::SubtaskDrop { instance } + | Trampoline::SubtaskCancel { instance, .. } + | Trampoline::ErrorContextNew { instance, .. } + | Trampoline::ErrorContextDebugMessage { instance, .. } + | Trampoline::ErrorContextDrop { instance, .. } + | Trampoline::StreamNew { instance, .. } + | Trampoline::StreamRead { instance, .. } + | Trampoline::StreamWrite { instance, .. } + | Trampoline::StreamCancelRead { instance, .. } + | Trampoline::StreamCancelWrite { instance, .. } + | Trampoline::StreamDropReadable { instance, .. } + | Trampoline::StreamDropWritable { instance, .. } + | Trampoline::FutureNew { instance, .. } + | Trampoline::FutureRead { instance, .. } + | Trampoline::FutureWrite { instance, .. } + | Trampoline::FutureCancelRead { instance, .. } + | Trampoline::FutureCancelWrite { instance, .. } + | Trampoline::FutureDropReadable { instance, .. } + | Trampoline::FutureDropWritable { instance, .. } => *instance, + }; + + self.check_may_leave_instance(instance) + } + + fn check_may_leave_instance(&mut self, instance: RuntimeComponentInstanceIndex) { + let vmctx = self.builder.func.dfg.block_params(self.block0)[0]; + + let flags = self.builder.ins().load( + ir::types::I32, + ir::MemFlags::trusted(), + vmctx, + i32::try_from(self.offsets.instance_flags(instance)).unwrap(), + ); + let may_leave_bit = self + .builder + .ins() + .band_imm(flags, i64::from(FLAG_MAY_LEAVE)); + self.builder + .ins() + .trapz(may_leave_bit, TRAP_CANNOT_LEAVE_COMPONENT); + } } impl ComponentCompiler for Compiler { diff --git a/crates/wasmtime/src/runtime/component/concurrent.rs b/crates/wasmtime/src/runtime/component/concurrent.rs index 6c4323e4bc8f..bd198d19ba79 100644 --- a/crates/wasmtime/src/runtime/component/concurrent.rs +++ b/crates/wasmtime/src/runtime/component/concurrent.rs @@ -720,8 +720,6 @@ pub(crate) fn poll_and_block( future: impl Future> + Send + 'static, caller_instance: RuntimeInstance, ) -> Result { - store.check_may_leave(caller_instance)?; - let state = store.concurrent_state_mut(); let caller = state.guest_thread.unwrap(); @@ -1388,21 +1386,6 @@ impl StoreContextMut<'_, T> { } impl StoreOpaque { - fn check_may_leave(&mut self, instance: RuntimeInstance) -> Result<()> { - Instance::from_wasmtime(self, instance.instance) - .id() - .get(self) - .check_may_leave(instance.index)?; - - // While we're here, verify that the caller instance matches the most - // recent task pushed onto the task stack: - let state = self.concurrent_state_mut(); - let caller = state.guest_thread.unwrap(); - assert_eq!(state.get_mut(caller.task)?.instance, instance); - - Ok(()) - } - /// Push a `GuestTask` onto the task stack for either a sync-to-sync, /// guest-to-guest call or a sync host-to-guest call. /// @@ -1863,17 +1846,6 @@ impl StoreOpaque { } impl Instance { - fn check_may_leave( - self, - store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, - ) -> Result<()> { - store.check_may_leave(RuntimeInstance { - instance: self.id().instance(), - index: caller, - }) - } - /// Get the next pending event for the specified task and (optional) /// waitable set, along with the waitable handle if applicable. fn get_event( @@ -2355,8 +2327,6 @@ impl Instance { string_encoding: u8, caller_info: CallerInfo, ) -> Result<()> { - self.check_may_leave(store.0, caller_instance)?; - if let (CallerInfo::Sync { .. }, true) = (&caller_info, callee_async) { // A task may only call an async-typed function via a sync lower if // it was created by a call to an async export. Otherwise, we'll @@ -2896,13 +2866,10 @@ impl Instance { pub(crate) fn task_return( self, store: &mut dyn VMStore, - caller: RuntimeComponentInstanceIndex, ty: TypeTupleIndex, options: OptionsIndex, storage: &[ValRaw], ) -> Result<()> { - self.check_may_leave(store, caller)?; - let state = store.concurrent_state_mut(); let guest_thread = state.guest_thread.unwrap(); let lift = state @@ -2948,13 +2915,7 @@ impl Instance { } /// Implements the `task.cancel` intrinsic. - pub(crate) fn task_cancel( - self, - store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, - ) -> Result<()> { - self.check_may_leave(store, caller)?; - + pub(crate) fn task_cancel(self, store: &mut StoreOpaque) -> Result<()> { let state = store.concurrent_state_mut(); let guest_thread = state.guest_thread.unwrap(); let task = state.get_mut(guest_thread.task)?; @@ -3018,8 +2979,6 @@ impl Instance { store: &mut StoreOpaque, caller_instance: RuntimeComponentInstanceIndex, ) -> Result { - self.check_may_leave(store, caller_instance)?; - let set = store.concurrent_state_mut().push(WaitableSet::default())?; let handle = store .handle_table(RuntimeInstance { @@ -3038,8 +2997,6 @@ impl Instance { caller_instance: RuntimeComponentInstanceIndex, set: u32, ) -> Result<()> { - self.check_may_leave(store, caller_instance)?; - let rep = store .handle_table(RuntimeInstance { instance: self.id().instance(), @@ -3068,8 +3025,6 @@ impl Instance { waitable_handle: u32, set_handle: u32, ) -> Result<()> { - self.check_may_leave(store, caller_instance)?; - let mut instance = self.id().get_mut(store); let waitable = Waitable::from_instance(instance.as_mut(), caller_instance, waitable_handle)?; @@ -3098,8 +3053,6 @@ impl Instance { caller_instance: RuntimeComponentInstanceIndex, task_id: u32, ) -> Result<()> { - self.check_may_leave(store, caller_instance)?; - self.waitable_join(store, caller_instance, task_id, 0)?; let (rep, is_host) = store @@ -3162,13 +3115,10 @@ impl Instance { pub(crate) fn waitable_set_wait( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, options: OptionsIndex, set: u32, payload: u32, ) -> Result { - self.check_may_leave(store, caller)?; - if !self.options(store, options).async_ { // The caller may only call `waitable-set.wait` from an async task // (i.e. a task created via a call to an async export). @@ -3204,13 +3154,10 @@ impl Instance { pub(crate) fn waitable_set_poll( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, options: OptionsIndex, set: u32, payload: u32, ) -> Result { - self.check_may_leave(store, caller)?; - let &CanonicalOptions { cancellable, instance: caller_instance, @@ -3256,8 +3203,6 @@ impl Instance { start_func_idx: u32, context: i32, ) -> Result { - self.check_may_leave(store.0, runtime_instance)?; - log::trace!("creating new thread"); let start_func_ty = FuncType::new(store.engine(), [ValType::I32], []); @@ -3392,8 +3337,6 @@ impl Instance { yielding: bool, to_thread: Option, ) -> Result { - self.check_may_leave(store, caller)?; - if to_thread.is_none() { let state = store.concurrent_state_mut(); if yielding { @@ -3543,8 +3486,6 @@ impl Instance { async_: bool, task_id: u32, ) -> Result { - self.check_may_leave(store, caller_instance)?; - if !async_ { // The caller may only sync call `subtask.cancel` from an async task // (i.e. a task created via a call to an async export). Otherwise, @@ -3693,26 +3634,11 @@ impl Instance { } } - pub(crate) fn context_get( - self, - store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, - slot: u32, - ) -> Result { - self.check_may_leave(store, caller)?; - + pub(crate) fn context_get(self, store: &mut StoreOpaque, slot: u32) -> Result { store.concurrent_state_mut().context_get(slot) } - pub(crate) fn context_set( - self, - store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, - slot: u32, - value: u32, - ) -> Result<()> { - self.check_may_leave(store, caller)?; - + pub(crate) fn context_set(self, store: &mut StoreOpaque, slot: u32, value: u32) -> Result<()> { store.concurrent_state_mut().context_set(slot, value) } } @@ -3797,7 +3723,6 @@ pub trait VMComponentAsyncStore { fn future_drop_writable( &mut self, instance: Instance, - caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, writer: u32, ) -> Result<()>; @@ -3860,7 +3785,6 @@ pub trait VMComponentAsyncStore { fn stream_drop_writable( &mut self, instance: Instance, - caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, writer: u32, ) -> Result<()>; @@ -3869,7 +3793,6 @@ pub trait VMComponentAsyncStore { fn error_context_debug_message( &mut self, instance: Instance, - caller: RuntimeComponentInstanceIndex, ty: TypeComponentLocalErrorContextTableIndex, options: OptionsIndex, err_ctx_handle: u32, @@ -4000,8 +3923,6 @@ impl VMComponentAsyncStore for StoreInner { future: u32, address: u32, ) -> Result { - instance.check_may_leave(self, caller)?; - instance .guest_write( StoreContextMut(self), @@ -4025,8 +3946,6 @@ impl VMComponentAsyncStore for StoreInner { future: u32, address: u32, ) -> Result { - instance.check_may_leave(self, caller)?; - instance .guest_read( StoreContextMut(self), @@ -4051,8 +3970,6 @@ impl VMComponentAsyncStore for StoreInner { address: u32, count: u32, ) -> Result { - instance.check_may_leave(self, caller)?; - instance .guest_write( StoreContextMut(self), @@ -4077,8 +3994,6 @@ impl VMComponentAsyncStore for StoreInner { address: u32, count: u32, ) -> Result { - instance.check_may_leave(self, caller)?; - instance .guest_read( StoreContextMut(self), @@ -4096,12 +4011,9 @@ impl VMComponentAsyncStore for StoreInner { fn future_drop_writable( &mut self, instance: Instance, - caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, writer: u32, ) -> Result<()> { - instance.check_may_leave(self, caller)?; - instance.guest_drop_writable(self, TransmitIndex::Future(ty), writer) } @@ -4117,8 +4029,6 @@ impl VMComponentAsyncStore for StoreInner { address: u32, count: u32, ) -> Result { - instance.check_may_leave(self, caller)?; - instance .guest_write( StoreContextMut(self), @@ -4148,8 +4058,6 @@ impl VMComponentAsyncStore for StoreInner { address: u32, count: u32, ) -> Result { - instance.check_may_leave(self, caller)?; - instance .guest_read( StoreContextMut(self), @@ -4170,26 +4078,20 @@ impl VMComponentAsyncStore for StoreInner { fn stream_drop_writable( &mut self, instance: Instance, - caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, writer: u32, ) -> Result<()> { - instance.check_may_leave(self, caller)?; - instance.guest_drop_writable(self, TransmitIndex::Stream(ty), writer) } fn error_context_debug_message( &mut self, instance: Instance, - caller: RuntimeComponentInstanceIndex, ty: TypeComponentLocalErrorContextTableIndex, options: OptionsIndex, err_ctx_handle: u32, debug_msg_address: u32, ) -> Result<()> { - instance.check_may_leave(self, caller)?; - instance.error_context_debug_message( StoreContextMut(self), ty, diff --git a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs index 3bf53db135fa..c4ddc08846d0 100644 --- a/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs +++ b/crates/wasmtime/src/runtime/component/concurrent/futures_and_streams.rs @@ -4002,13 +4002,11 @@ impl Instance { pub(crate) fn error_context_new( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeComponentLocalErrorContextTableIndex, options: OptionsIndex, debug_msg_address: u32, debug_msg_len: u32, ) -> Result { - self.id().get(store).check_may_leave(caller)?; let lift_ctx = &mut LiftContext::new(store, options, self); let debug_msg = String::linear_lift_from_flat( lift_ctx, @@ -4085,12 +4083,10 @@ impl Instance { pub(crate) fn future_cancel_read( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, async_: bool, reader: u32, ) -> Result { - self.id().get(store).check_may_leave(caller)?; self.guest_cancel_read(store, TransmitIndex::Future(ty), async_, reader) .map(|v| v.encode()) } @@ -4099,12 +4095,10 @@ impl Instance { pub(crate) fn future_cancel_write( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, async_: bool, writer: u32, ) -> Result { - self.id().get(store).check_may_leave(caller)?; self.guest_cancel_write(store, TransmitIndex::Future(ty), async_, writer) .map(|v| v.encode()) } @@ -4113,12 +4107,10 @@ impl Instance { pub(crate) fn stream_cancel_read( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, async_: bool, reader: u32, ) -> Result { - self.id().get(store).check_may_leave(caller)?; self.guest_cancel_read(store, TransmitIndex::Stream(ty), async_, reader) .map(|v| v.encode()) } @@ -4127,12 +4119,10 @@ impl Instance { pub(crate) fn stream_cancel_write( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, async_: bool, writer: u32, ) -> Result { - self.id().get(store).check_may_leave(caller)?; self.guest_cancel_write(store, TransmitIndex::Stream(ty), async_, writer) .map(|v| v.encode()) } @@ -4141,11 +4131,9 @@ impl Instance { pub(crate) fn future_drop_readable( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, reader: u32, ) -> Result<()> { - self.id().get(store).check_may_leave(caller)?; self.guest_drop_readable(store, TransmitIndex::Future(ty), reader) } @@ -4153,11 +4141,9 @@ impl Instance { pub(crate) fn stream_drop_readable( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, reader: u32, ) -> Result<()> { - self.id().get(store).check_may_leave(caller)?; self.guest_drop_readable(store, TransmitIndex::Stream(ty), reader) } @@ -4195,12 +4181,10 @@ impl Instance { pub(crate) fn error_context_drop( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeComponentLocalErrorContextTableIndex, error_context: u32, ) -> Result<()> { let instance = self.id().get_mut(store); - instance.check_may_leave(caller)?; let local_handle_table = instance.table_for_error_context(ty); @@ -4265,10 +4249,8 @@ impl Instance { pub(crate) fn future_new( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeFutureTableIndex, ) -> Result { - self.id().get(store).check_may_leave(caller)?; self.guest_new(store, TransmitIndex::Future(ty)) } @@ -4276,10 +4258,8 @@ impl Instance { pub(crate) fn stream_new( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeStreamTableIndex, ) -> Result { - self.id().get(store).check_may_leave(caller)?; self.guest_new(store, TransmitIndex::Stream(ty)) } diff --git a/crates/wasmtime/src/runtime/component/func/host.rs b/crates/wasmtime/src/runtime/component/func/host.rs index 8a05da370579..b746f75c6348 100644 --- a/crates/wasmtime/src/runtime/component/func/host.rs +++ b/crates/wasmtime/src/runtime/component/func/host.rs @@ -371,15 +371,6 @@ where ) -> Result<()> { let vminstance = instance.id().get(store.0); let opts = &vminstance.component().env_component().options[options]; - let caller_instance = opts.instance; - let flags = vminstance.instance_flags(caller_instance); - - // Perform a dynamic check that this instance can indeed be left. - // Exiting the component is disallowed, for example, when the `realloc` - // function calls a canonical import. - if unsafe { !flags.may_leave() } { - return Err(format_err!(crate::Trap::CannotLeaveComponent)); - } if opts.async_ { #[cfg(feature = "component-model-async")] diff --git a/crates/wasmtime/src/runtime/component/instance.rs b/crates/wasmtime/src/runtime/component/instance.rs index 48e2dd0986dd..3c3cdb113138 100644 --- a/crates/wasmtime/src/runtime/component/instance.rs +++ b/crates/wasmtime/src/runtime/component/instance.rs @@ -379,11 +379,9 @@ impl Instance { pub(crate) fn resource_new32( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeResourceTableIndex, rep: u32, ) -> Result { - self.id().get(store).check_may_leave(caller)?; let (calls, _, _, instance) = store.component_resource_state_with_instance(self); resource_tables(calls, instance).resource_new(TypedResource::Component { ty, rep }) } @@ -393,11 +391,9 @@ impl Instance { pub(crate) fn resource_rep32( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeResourceTableIndex, index: u32, ) -> Result { - self.id().get(store).check_may_leave(caller)?; let (calls, _, _, instance) = store.component_resource_state_with_instance(self); resource_tables(calls, instance).resource_rep(TypedResourceIndex::Component { ty, index }) } @@ -406,11 +402,9 @@ impl Instance { pub(crate) fn resource_drop( self, store: &mut StoreOpaque, - caller: RuntimeComponentInstanceIndex, ty: TypeResourceTableIndex, index: u32, ) -> Result> { - self.id().get(store).check_may_leave(caller)?; let (calls, _, _, instance) = store.component_resource_state_with_instance(self); resource_tables(calls, instance).resource_drop(TypedResourceIndex::Component { ty, index }) } diff --git a/crates/wasmtime/src/runtime/vm/component.rs b/crates/wasmtime/src/runtime/vm/component.rs index dfedb1b2fba1..065666e36ce7 100644 --- a/crates/wasmtime/src/runtime/vm/component.rs +++ b/crates/wasmtime/src/runtime/vm/component.rs @@ -947,18 +947,6 @@ impl ComponentInstance { } } - pub(crate) fn check_may_leave( - &self, - instance: RuntimeComponentInstanceIndex, - ) -> crate::Result<()> { - let flags = self.instance_flags(instance); - if unsafe { flags.may_leave() } { - Ok(()) - } else { - Err(crate::format_err!(crate::Trap::CannotLeaveComponent)) - } - } - pub(crate) fn task_may_block(&self) -> NonNull { unsafe { self.vmctx_plus_offset_raw::(self.offsets.task_may_block()) } } diff --git a/crates/wasmtime/src/runtime/vm/component/libcalls.rs b/crates/wasmtime/src/runtime/vm/component/libcalls.rs index e8ee548e192a..fa2808602d7d 100644 --- a/crates/wasmtime/src/runtime/vm/component/libcalls.rs +++ b/crates/wasmtime/src/runtime/vm/component/libcalls.rs @@ -584,42 +584,36 @@ fn inflate_latin1_bytes(dst: &mut [u16], latin1_bytes_so_far: usize) -> &mut [u1 fn resource_new32( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, resource: u32, rep: u32, ) -> Result { - let caller_instance = RuntimeComponentInstanceIndex::from_u32(caller_instance); let resource = TypeResourceTableIndex::from_u32(resource); - instance.resource_new32(store, caller_instance, resource, rep) + instance.resource_new32(store, resource, rep) } fn resource_rep32( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, resource: u32, idx: u32, ) -> Result { - let caller_instance = RuntimeComponentInstanceIndex::from_u32(caller_instance); let resource = TypeResourceTableIndex::from_u32(resource); - instance.resource_rep32(store, caller_instance, resource, idx) + instance.resource_rep32(store, resource, idx) } fn resource_drop( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, resource: u32, idx: u32, ) -> Result { - let caller_instance = RuntimeComponentInstanceIndex::from_u32(caller_instance); let resource = TypeResourceTableIndex::from_u32(resource); - Ok(ResourceDropRet(instance.resource_drop( - store, - caller_instance, - resource, - idx, - )?)) + Ok(ResourceDropRet( + instance.resource_drop(store, resource, idx)?, + )) } struct ResourceDropRet(Option); @@ -725,7 +719,7 @@ fn backpressure_modify( unsafe fn task_return( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, options: u32, storage: *mut u8, @@ -733,7 +727,6 @@ unsafe fn task_return( ) -> Result<()> { instance.task_return( store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeTupleIndex::from_u32(ty), OptionsIndex::from_u32(options), unsafe { core::slice::from_raw_parts(storage.cast(), storage_len) }, @@ -741,11 +734,8 @@ unsafe fn task_return( } #[cfg(feature = "component-model-async")] -fn task_cancel(store: &mut dyn VMStore, instance: Instance, caller_instance: u32) -> Result<()> { - instance.task_cancel( - store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), - ) +fn task_cancel(store: &mut dyn VMStore, instance: Instance, _caller_instance: u32) -> Result<()> { + instance.task_cancel(store) } #[cfg(feature = "component-model-async")] @@ -764,36 +754,24 @@ fn waitable_set_new( fn waitable_set_wait( store: &mut dyn VMStore, instance: Instance, - caller: u32, + _caller: u32, options: u32, set: u32, payload: u32, ) -> Result { - instance.waitable_set_wait( - store, - RuntimeComponentInstanceIndex::from_u32(caller), - OptionsIndex::from_u32(options), - set, - payload, - ) + instance.waitable_set_wait(store, OptionsIndex::from_u32(options), set, payload) } #[cfg(feature = "component-model-async")] fn waitable_set_poll( store: &mut dyn VMStore, instance: Instance, - caller: u32, + _caller: u32, options: u32, set: u32, payload: u32, ) -> Result { - instance.waitable_set_poll( - store, - RuntimeComponentInstanceIndex::from_u32(caller), - OptionsIndex::from_u32(options), - set, - payload, - ) + instance.waitable_set_poll(store, OptionsIndex::from_u32(options), set, payload) } #[cfg(feature = "component-model-async")] @@ -1014,14 +992,10 @@ unsafe impl HostResultHasUnwindSentinel for ResourcePair { fn future_new( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, ) -> Result { - instance.future_new( - store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), - TypeFutureTableIndex::from_u32(ty), - ) + instance.future_new(store, TypeFutureTableIndex::from_u32(ty)) } #[cfg(feature = "component-model-async")] @@ -1068,14 +1042,13 @@ fn future_read( fn future_cancel_write( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, async_: u8, writer: u32, ) -> Result { instance.future_cancel_write( store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeFutureTableIndex::from_u32(ty), async_ != 0, writer, @@ -1086,14 +1059,13 @@ fn future_cancel_write( fn future_cancel_read( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, async_: u8, reader: u32, ) -> Result { instance.future_cancel_read( store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeFutureTableIndex::from_u32(ty), async_ != 0, reader, @@ -1104,13 +1076,12 @@ fn future_cancel_read( fn future_drop_writable( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, writer: u32, ) -> Result<()> { store.component_async_store().future_drop_writable( instance, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeFutureTableIndex::from_u32(ty), writer, ) @@ -1120,30 +1091,21 @@ fn future_drop_writable( fn future_drop_readable( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, reader: u32, ) -> Result<()> { - instance.future_drop_readable( - store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), - TypeFutureTableIndex::from_u32(ty), - reader, - ) + instance.future_drop_readable(store, TypeFutureTableIndex::from_u32(ty), reader) } #[cfg(feature = "component-model-async")] fn stream_new( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, ) -> Result { - instance.stream_new( - store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), - TypeStreamTableIndex::from_u32(ty), - ) + instance.stream_new(store, TypeStreamTableIndex::from_u32(ty)) } #[cfg(feature = "component-model-async")] @@ -1194,14 +1156,13 @@ fn stream_read( fn stream_cancel_write( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, async_: u8, writer: u32, ) -> Result { instance.stream_cancel_write( store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeStreamTableIndex::from_u32(ty), async_ != 0, writer, @@ -1212,14 +1173,13 @@ fn stream_cancel_write( fn stream_cancel_read( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, async_: u8, reader: u32, ) -> Result { instance.stream_cancel_read( store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeStreamTableIndex::from_u32(ty), async_ != 0, reader, @@ -1230,13 +1190,12 @@ fn stream_cancel_read( fn stream_drop_writable( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, writer: u32, ) -> Result<()> { store.component_async_store().stream_drop_writable( instance, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeStreamTableIndex::from_u32(ty), writer, ) @@ -1246,16 +1205,11 @@ fn stream_drop_writable( fn stream_drop_readable( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, reader: u32, ) -> Result<()> { - instance.stream_drop_readable( - store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), - TypeStreamTableIndex::from_u32(ty), - reader, - ) + instance.stream_drop_readable(store, TypeStreamTableIndex::from_u32(ty), reader) } #[cfg(feature = "component-model-async")] @@ -1314,7 +1268,7 @@ fn flat_stream_read( fn error_context_new( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, options: u32, debug_msg_address: u32, @@ -1322,7 +1276,6 @@ fn error_context_new( ) -> Result { instance.error_context_new( store.store_opaque_mut(), - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeComponentLocalErrorContextTableIndex::from_u32(ty), OptionsIndex::from_u32(options), debug_msg_address, @@ -1334,7 +1287,7 @@ fn error_context_new( fn error_context_debug_message( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, options: u32, err_ctx_handle: u32, @@ -1342,7 +1295,6 @@ fn error_context_debug_message( ) -> Result<()> { store.component_async_store().error_context_debug_message( instance, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeComponentLocalErrorContextTableIndex::from_u32(ty), OptionsIndex::from_u32(options), err_ctx_handle, @@ -1354,13 +1306,12 @@ fn error_context_debug_message( fn error_context_drop( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, ty: u32, err_ctx_handle: u32, ) -> Result<()> { instance.error_context_drop( store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), TypeComponentLocalErrorContextTableIndex::from_u32(ty), err_ctx_handle, ) @@ -1370,30 +1321,21 @@ fn error_context_drop( fn context_get( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, slot: u32, ) -> Result { - instance.context_get( - store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), - slot, - ) + instance.context_get(store, slot) } #[cfg(feature = "component-model-async")] fn context_set( store: &mut dyn VMStore, instance: Instance, - caller_instance: u32, + _caller_instance: u32, slot: u32, val: u32, ) -> Result<()> { - instance.context_set( - store, - RuntimeComponentInstanceIndex::from_u32(caller_instance), - slot, - val, - ) + instance.context_set(store, slot, val) } #[cfg(feature = "component-model-async")] diff --git a/tests/disas/riscv64-component-builtins-asm.wat b/tests/disas/riscv64-component-builtins-asm.wat index dd6a465899e7..9bc5c184b4a7 100644 --- a/tests/disas/riscv64-component-builtins-asm.wat +++ b/tests/disas/riscv64-component-builtins-asm.wat @@ -19,24 +19,30 @@ ;; addi sp, sp, -0x10 ;; sd s1, 8(sp) ;; mv s1, a1 -;; mv a3, a2 -;; ld a4, 0x10(a0) -;; mv a5, s0 -;; sd a5, 0x28(a4) -;; ld a5, 8(s0) -;; sd a5, 0x30(a4) -;; ld a1, 8(a0) -;; ld a5, 0x10(a1) +;; mv a7, a2 +;; ld a1, 0x10(a0) +;; mv a2, s0 +;; sd a2, 0x28(a1) +;; ld a2, 8(s0) +;; sd a2, 0x30(a1) +;; lw a2, 0x20(a0) +;; andi a2, a2, 1 +;; bnez a2, 8 +;; .byte 0x00, 0x00, 0x00, 0x00 +;; ╰─╼ trap: CannotLeaveComponent +;; ld a3, 8(a0) +;; ld a5, 0x10(a3) ;; mv a4, zero ;; slli a1, a4, 0x20 ;; srai a1, a1, 0x20 ;; slli a2, a4, 0x20 ;; srai a2, a2, 0x20 +;; mv a3, a7 ;; slli a3, a3, 0x20 ;; srai a3, a3, 0x20 ;; jalr a5 -;; addi a1, zero, -1 -;; beq a0, a1, 0x1c +;; addi a3, zero, -1 +;; beq a0, a3, 0x1c ;; ld s1, 8(sp) ;; addi sp, sp, 0x10 ;; ld ra, 8(sp) @@ -44,8 +50,8 @@ ;; addi sp, sp, 0x10 ;; ret ;; mv a1, s1 -;; ld a2, 0x10(a1) -;; ld a2, 0x198(a2) +;; ld a4, 0x10(a1) +;; ld a4, 0x198(a4) ;; mv a0, a1 -;; jalr a2 +;; jalr a4 ;; .byte 0x00, 0x00, 0x00, 0x00 diff --git a/tests/disas/riscv64-component-builtins.wat b/tests/disas/riscv64-component-builtins.wat index 4a1102fed04a..00d44db61103 100644 --- a/tests/disas/riscv64-component-builtins.wat +++ b/tests/disas/riscv64-component-builtins.wat @@ -20,22 +20,26 @@ ;; store notrap aligned v4, v3+40 ;; v5 = get_return_address.i64 ;; store notrap aligned v5, v3+48 -;; v8 = load.i64 notrap aligned readonly v0+8 -;; v9 = load.i64 notrap aligned readonly v8+16 -;; v6 = iconst.i32 0 -;; v10 = call_indirect sig0, v9(v0, v6, v6, v2) ; v6 = 0, v6 = 0 -;; v11 = iconst.i64 -1 -;; v12 = icmp ne v10, v11 ; v11 = -1 -;; brif v12, block2, block1 +;; v6 = load.i32 notrap aligned v0+32 +;; v17 = iconst.i32 1 +;; v7 = band v6, v17 ; v17 = 1 +;; trapz v7, user25 +;; v10 = load.i64 notrap aligned readonly v0+8 +;; v11 = load.i64 notrap aligned readonly v10+16 +;; v8 = iconst.i32 0 +;; v12 = call_indirect sig0, v11(v0, v8, v8, v2) ; v8 = 0, v8 = 0 +;; v13 = iconst.i64 -1 +;; v14 = icmp ne v12, v13 ; v13 = -1 +;; brif v14, block2, block1 ;; ;; block1 cold: -;; v13 = load.i64 notrap aligned readonly v1+16 -;; v14 = load.i64 notrap aligned readonly v13+408 -;; call_indirect sig1, v14(v1) +;; v15 = load.i64 notrap aligned readonly v1+16 +;; v16 = load.i64 notrap aligned readonly v15+408 +;; call_indirect sig1, v16(v1) ;; trap user1 ;; ;; block2: -;; brif.i64 v10, block3, block4 +;; brif.i64 v12, block3, block4 ;; ;; block3: ;; jump block4 diff --git a/tests/misc_testsuite/component-model-threading/threading-builtins.wast b/tests/misc_testsuite/component-model-threading/threading-builtins.wast index 5a09d41693bf..bf10f60439c3 100644 --- a/tests/misc_testsuite/component-model-threading/threading-builtins.wast +++ b/tests/misc_testsuite/component-model-threading/threading-builtins.wast @@ -100,3 +100,22 @@ (func (export "run") async (result u32) (canon lift (core func $i "run")))) (assert_return (invoke "run") (u32.const 42)) + +;; Test that `thread.index` is exempt from may-leave checks +(component + (core func $thread.index (canon thread.index)) + + (core module $DM + (import "" "thread.index" (func $thread.index (result i32))) + + (func (export "run")) + (func (export "post-return") call $thread.index drop) + ) + (core instance $dm (instantiate $DM (with "" (instance + (export "thread.index" (func $thread.index)) + )))) + (func (export "run") + (canon lift (core func $dm "run") (post-return (func $dm "post-return")))) +) + +(assert_return (invoke "run")) diff --git a/tests/misc_testsuite/component-model-threading/threading-trap-in-post-return.wast b/tests/misc_testsuite/component-model-threading/threading-trap-in-post-return.wast new file mode 100644 index 000000000000..c129a5cbf324 --- /dev/null +++ b/tests/misc_testsuite/component-model-threading/threading-trap-in-post-return.wast @@ -0,0 +1,63 @@ +;;! component_model_threading = true + +(component definition $Tester + (core module $Memory + (table (export "t") 1 funcref) + ) + (core instance $memory (instantiate $Memory)) + + (core type $ft (func (param i32))) + (core func $thread.new-indirect (canon thread.new-indirect $ft (table $memory "t"))) + (core func $thread.switch-to (canon thread.switch-to)) + (core func $thread.suspend (canon thread.suspend)) + (core func $thread.resume-later (canon thread.resume-later)) + (core func $thread.yield-to (canon thread.yield-to)) + + (core module $DM + (import "" "thread.new-indirect" (func $thread.new-indirect (param i32 i32) (result i32))) + (import "" "thread.switch-to" (func $thread.switch-to (param i32) (result i32))) + (import "" "thread.suspend" (func $thread.suspend (result i32))) + (import "" "thread.resume-later" (func $thread.resume-later (param i32))) + (import "" "thread.yield-to" (func $thread.yield-to (param i32) (result i32))) + + (func (export "noop")) + (func (export "trap-calling-thread-new-indirect") (call $thread.new-indirect (i32.const 0) (i32.const 0)) drop) + (func (export "trap-calling-thread-switch-to") (call $thread.switch-to (i32.const 0)) drop) + (func (export "trap-calling-thread-suspend") (call $thread.suspend) drop) + (func (export "trap-calling-thread-resume-later") (call $thread.resume-later (i32.const 0))) + (func (export "trap-calling-thread-yield-to") (call $thread.yield-to (i32.const 0)) drop) + ) + (core instance $dm (instantiate $DM (with "" (instance + (export "thread.new-indirect" (func $thread.new-indirect)) + (export "thread.switch-to" (func $thread.switch-to)) + (export "thread.suspend" (func $thread.suspend)) + (export "thread.resume-later" (func $thread.resume-later)) + (export "thread.yield-to" (func $thread.yield-to)) + )))) + (func (export "trap-calling-thread-new-indirect") + (canon lift (core func $dm "noop") + (post-return (func $dm "trap-calling-thread-new-indirect")))) + (func (export "trap-calling-thread-switch-to") + (canon lift (core func $dm "noop") + (post-return (func $dm "trap-calling-thread-switch-to")))) + (func (export "trap-calling-thread-suspend") + (canon lift (core func $dm "noop") + (post-return (func $dm "trap-calling-thread-suspend")))) + (func (export "trap-calling-thread-resume-later") + (canon lift (core func $dm "noop") + (post-return (func $dm "trap-calling-thread-resume-later")))) + (func (export "trap-calling-thread-yield-to") + (canon lift (core func $dm "noop") + (post-return (func $dm "trap-calling-thread-yield-to")))) +) + +(component instance $i0 $Tester) +(assert_trap (invoke "trap-calling-thread-new-indirect") "cannot leave component instance") +(component instance $i1 $Tester) +(assert_trap (invoke "trap-calling-thread-switch-to") "cannot leave component instance") +(component instance $i2 $Tester) +(assert_trap (invoke "trap-calling-thread-suspend") "cannot leave component instance") +(component instance $i3 $Tester) +(assert_trap (invoke "trap-calling-thread-resume-later") "cannot leave component instance") +(component instance $i4 $Tester) +(assert_trap (invoke "trap-calling-thread-yield-to") "cannot leave component instance") diff --git a/tests/misc_testsuite/component-model/async/task-builtins.wast b/tests/misc_testsuite/component-model/async/task-builtins.wast index 93a74011d8f6..7dcf6e8cb545 100644 --- a/tests/misc_testsuite/component-model/async/task-builtins.wast +++ b/tests/misc_testsuite/component-model/async/task-builtins.wast @@ -75,3 +75,47 @@ (core func $subtask-cancel (canon subtask.cancel)) (core instance $i (instantiate $m (with "" (instance (export "subtask.cancel" (func $subtask-cancel)))))) ) + +;; Test that some intrinsics are exempt from may-leave checks +(component + (core func $backpressure.inc (canon backpressure.inc)) + (core func $backpressure.dec (canon backpressure.dec)) + (core func $context.get (canon context.get i32 0)) + (core func $context.set (canon context.set i32 0)) + + (core module $DM + (import "" "backpressure.inc" (func $backpressure.inc)) + (import "" "backpressure.dec" (func $backpressure.dec)) + (import "" "context.get" (func $context.get (result i32))) + (import "" "context.set" (func $context.set (param i32))) + + (global $g (mut i32) (i32.const 0)) + + (func (export "run") + (call $context.set (i32.const 100)) + ) + (func (export "post-return") + call $backpressure.inc + call $backpressure.dec + + ;; context.get should be what was set in `run` + call $context.get + i32.const 100 + i32.ne + if unreachable end + + i32.const 32 + call $context.set + ) + ) + (core instance $dm (instantiate $DM (with "" (instance + (export "backpressure.inc" (func $backpressure.inc)) + (export "backpressure.dec" (func $backpressure.dec)) + (export "context.get" (func $context.get)) + (export "context.set" (func $context.set)) + )))) + (func (export "run") + (canon lift (core func $dm "run") (post-return (func $dm "post-return")))) +) + +(assert_return (invoke "run")) diff --git a/tests/misc_testsuite/component-model/error-context-trap-in-post-return.wast b/tests/misc_testsuite/component-model/error-context-trap-in-post-return.wast new file mode 100644 index 000000000000..715238041d9e --- /dev/null +++ b/tests/misc_testsuite/component-model/error-context-trap-in-post-return.wast @@ -0,0 +1,50 @@ +;;! component_model_error_context = true + +(component definition $Tester + (core module $Memory + (memory (export "mem") 1) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) unreachable) + ) + (core instance $memory (instantiate $Memory)) + + (core func $error-context.new (canon error-context.new (memory $memory "mem"))) + (core func $error-context.debug-message + (canon error-context.debug-message + (memory $memory "mem") + (realloc (func $memory "realloc")))) + (core func $error-context.drop (canon error-context.drop)) + + (core module $DM + (import "" "mem" (memory 1)) + (import "" "error-context.new" (func $error-context.new (param i32 i32) (result i32))) + (import "" "error-context.debug-message" (func $error-context.debug-message (param i32 i32))) + (import "" "error-context.drop" (func $error-context.drop (param i32))) + + (func (export "noop")) + (func (export "trap-calling-error-context-new") (call $error-context.new (i32.const 0) (i32.const 0)) drop) + (func (export "trap-calling-error-context-debug-message") (call $error-context.debug-message (i32.const 0) (i32.const 0))) + (func (export "trap-calling-error-context-drop") (call $error-context.drop (i32.const 0))) + ) + (core instance $dm (instantiate $DM (with "" (instance + (export "mem" (memory $memory "mem")) + (export "error-context.new" (func $error-context.new)) + (export "error-context.debug-message" (func $error-context.debug-message)) + (export "error-context.drop" (func $error-context.drop)) + )))) + (func (export "trap-calling-error-context-new") + (canon lift (core func $dm "noop") + (post-return (func $dm "trap-calling-error-context-new")))) + (func (export "trap-calling-error-context-debug-message") + (canon lift (core func $dm "noop") + (post-return (func $dm "trap-calling-error-context-debug-message")))) + (func (export "trap-calling-error-context-drop") + (canon lift (core func $dm "noop") + (post-return (func $dm "trap-calling-error-context-drop")))) +) + +(component instance $i0 $Tester) +(assert_trap (invoke "trap-calling-error-context-new") "cannot leave component instance") +(component instance $i1 $Tester) +(assert_trap (invoke "trap-calling-error-context-debug-message") "cannot leave component instance") +(component instance $i2 $Tester) +(assert_trap (invoke "trap-calling-error-context-drop") "cannot leave component instance") diff --git a/tests/misc_testsuite/component-model/resources.wast b/tests/misc_testsuite/component-model/resources.wast index 5302e7eb904a..baa633e8580a 100644 --- a/tests/misc_testsuite/component-model/resources.wast +++ b/tests/misc_testsuite/component-model/resources.wast @@ -1091,3 +1091,36 @@ (assert_trap (invoke "drop-r1-as-r2") "handle index 2 used with the wrong type, expected guest-defined resource but found a different guest-defined resource") (component instance $C1 $C) (assert_trap (invoke "return-r1-as-r2") "handle index 2 used with the wrong type, expected guest-defined resource but found a different guest-defined resource") + +;; Test that `resource.rep` is exempt from may-leave checks +(component + (type $r (resource (rep i32))) + (core func $resource.new (canon resource.new $r)) + (core func $resource.rep (canon resource.rep $r)) + + (core module $DM + (import "" "resource.new" (func $resource.new (param i32) (result i32))) + (import "" "resource.rep" (func $resource.rep (param i32) (result i32))) + + (global $g (mut i32) (i32.const 0)) + + (func (export "run") + (global.set $g (call $resource.new (i32.const 42))) + ) + (func (export "post-return") + (i32.eq + (call $resource.rep (global.get $g)) + (i32.const 42)) + if return end + unreachable + ) + ) + (core instance $dm (instantiate $DM (with "" (instance + (export "resource.new" (func $resource.new)) + (export "resource.rep" (func $resource.rep)) + )))) + (func (export "run") + (canon lift (core func $dm "run") (post-return (func $dm "post-return")))) +) + +(assert_return (invoke "run"))