diff --git a/crates/wasmi/src/engine/executor/instrs/call.rs b/crates/wasmi/src/engine/executor/instrs/call.rs index f073e97696..4fae2d8372 100644 --- a/crates/wasmi/src/engine/executor/instrs/call.rs +++ b/crates/wasmi/src/engine/executor/instrs/call.rs @@ -10,7 +10,7 @@ use crate::{ }, func::{FuncEntity, HostFuncEntity}, ir::{index, Op, Slot, SlotSpan}, - store::{CallHooks, PrunedStore, StoreInner}, + store::{CallHooks, PrunedStore, StoreError, StoreInner}, Error, Func, Instance, @@ -42,16 +42,21 @@ pub fn dispatch_host_func( usize::from(len_params), usize::from(len_results), ); - store - .call_host_func(&host_func, instance, params_results, call_hooks) - .inspect_err(|_error| { + match store.call_host_func(&host_func, instance, params_results, call_hooks) { + Err(StoreError::Internal(error)) => { + panic!("`call.host`: internal interpreter error: {error}") + } + Err(StoreError::External(error)) => { // Note: We drop the values that have been temporarily added to // the stack to act as parameter and result buffer for the // called host function. Since the host function failed we // need to clean up the temporary buffer values here. // This is required for resumable calls to work properly. value_stack.drop(usize::from(max_inout)); - })?; + return Err(error); + } + _ => {} + } Ok((len_params, len_results)) } diff --git a/crates/wasmi/src/engine/executor/instrs/memory.rs b/crates/wasmi/src/engine/executor/instrs/memory.rs index 4470e88dbb..5cbf1b7b7d 100644 --- a/crates/wasmi/src/engine/executor/instrs/memory.rs +++ b/crates/wasmi/src/engine/executor/instrs/memory.rs @@ -7,7 +7,7 @@ use crate::{ Op, Slot, }, - store::{PrunedStore, StoreInner}, + store::{PrunedStore, StoreError, StoreInner}, Error, TrapCode, }; @@ -46,7 +46,7 @@ impl Executor<'_> { /// Executes an [`Op::DataDrop`]. pub fn execute_data_drop(&mut self, store: &mut StoreInner, segment_index: Data) { let segment = self.get_data_segment(segment_index); - store.resolve_data_segment_mut(&segment).drop_bytes(); + store.resolve_data_mut(&segment).drop_bytes(); self.next_instr(); } @@ -88,20 +88,24 @@ impl Executor<'_> { unsafe { self.cache.update_memory(store.inner_mut()) }; return_value } - Err(MemoryError::OutOfBoundsGrowth | MemoryError::OutOfSystemMemory) => { + Err(StoreError::External( + MemoryError::OutOfBoundsGrowth | MemoryError::OutOfSystemMemory, + )) => { let memory_ty = store.inner().resolve_memory(&memory).ty(); match memory_ty.is_64() { true => u64::MAX, false => u64::from(u32::MAX), } } - Err(MemoryError::OutOfFuel { required_fuel }) => { + Err(StoreError::External(MemoryError::OutOfFuel { required_fuel })) => { return Err(Error::from(ResumableOutOfFuelError::new(required_fuel))) } - Err(MemoryError::ResourceLimiterDeniedAllocation) => { + Err(StoreError::External(MemoryError::ResourceLimiterDeniedAllocation)) => { return Err(Error::from(TrapCode::GrowthOperationLimited)) } - Err(error) => panic!("encountered an unexpected error: {error}"), + Err(error) => { + panic!("`table.grow`: internal interpreter error: {error}") + } }; self.set_stack_slot(result, return_value); self.try_next_instr_at(2) diff --git a/crates/wasmi/src/engine/executor/instrs/table.rs b/crates/wasmi/src/engine/executor/instrs/table.rs index 165672c8f5..a4a2c8f980 100644 --- a/crates/wasmi/src/engine/executor/instrs/table.rs +++ b/crates/wasmi/src/engine/executor/instrs/table.rs @@ -9,7 +9,7 @@ use crate::{ Op, Slot, }, - store::{PrunedStore, StoreInner}, + store::{PrunedStore, StoreError, StoreInner}, Error, TrapCode, }; @@ -224,20 +224,24 @@ impl Executor<'_> { let value = self.get_stack_slot(value); let return_value = match store.grow_table(&table, delta, value) { Ok(return_value) => return_value, - Err(TableError::GrowOutOfBounds | TableError::OutOfSystemMemory) => { + Err(StoreError::External( + TableError::GrowOutOfBounds | TableError::OutOfSystemMemory, + )) => { let table_ty = store.inner().resolve_table(&table).ty(); match table_ty.is_64() { true => u64::MAX, false => u64::from(u32::MAX), } } - Err(TableError::OutOfFuel { required_fuel }) => { + Err(StoreError::External(TableError::OutOfFuel { required_fuel })) => { return Err(Error::from(ResumableOutOfFuelError::new(required_fuel))) } - Err(TableError::ResourceLimiterDeniedAllocation) => { + Err(StoreError::External(TableError::ResourceLimiterDeniedAllocation)) => { return Err(Error::from(TrapCode::GrowthOperationLimited)) } - Err(error) => panic!("encountered unexpected error: {error}"), + Err(error) => { + panic!("`memory.grow`: internal interpreter error: {error}") + } }; self.set_stack_slot(result, return_value); self.try_next_instr_at(2) @@ -246,7 +250,7 @@ impl Executor<'_> { /// Executes an [`Op::ElemDrop`]. pub fn execute_element_drop(&mut self, store: &mut StoreInner, segment_index: Elem) { let segment = self.get_element_segment(segment_index); - store.resolve_element_segment_mut(&segment).drop_items(); + store.resolve_element_mut(&segment).drop_items(); self.next_instr(); } } diff --git a/crates/wasmi/src/global.rs b/crates/wasmi/src/global.rs index 226c819755..2c722be532 100644 --- a/crates/wasmi/src/global.rs +++ b/crates/wasmi/src/global.rs @@ -56,6 +56,10 @@ impl Global { } /// Returns the [`GlobalType`] of the global variable. + /// + /// # Panics + /// + /// Panics if `ctx` does not own this [`Global`]. pub fn ty(&self, ctx: impl AsContext) -> GlobalType { ctx.as_context().store.inner.resolve_global(self).ty() } diff --git a/crates/wasmi/src/reftype.rs b/crates/wasmi/src/reftype.rs index 18db070fb3..8c4bf41f24 100644 --- a/crates/wasmi/src/reftype.rs +++ b/crates/wasmi/src/reftype.rs @@ -126,7 +126,7 @@ impl ExternRef { /// /// Panics if `ctx` does not own this [`ExternRef`]. pub fn data<'a, T: 'a>(&self, ctx: impl Into>) -> &'a dyn Any { - ctx.into().store.inner.resolve_external_object(self).data() + ctx.into().store.inner.resolve_externref(self).data() } } diff --git a/crates/wasmi/src/store/error.rs b/crates/wasmi/src/store/error.rs new file mode 100644 index 0000000000..9ab9e7f090 --- /dev/null +++ b/crates/wasmi/src/store/error.rs @@ -0,0 +1,119 @@ +use core::{any::type_name, fmt}; + +#[cfg(doc)] +use super::{PrunedStore, Store, StoreInner}; + +/// An error occurred on [`Store`] or [`StoreInner`] methods. +#[derive(Debug, Copy, Clone)] +pub enum StoreError { + /// An error representing an internal error, a.k.a. a bug or invalid behavior within Wasmi. + Internal(InternalStoreError), + /// An external error forwarded by the [`Store`] or [`StoreInner`]. + External(E), +} + +impl fmt::Display for StoreError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + StoreError::Internal(error) => fmt::Display::fmt(error, f), + StoreError::External(error) => fmt::Display::fmt(error, f), + } + } +} + +/// A [`Store`] or [`StoreInner`] internal error, a.k.a. bug or invalid behavior within Wasmi. +#[derive(Debug, Copy, Clone)] +pub struct InternalStoreError { + kind: InternalStoreErrorKind, +} + +impl InternalStoreError { + /// Creates a new [`InternalStoreError`]. + fn new(kind: InternalStoreErrorKind) -> Self { + Self { kind } + } + + /// An error indicating that a [`Store`] resource could not be found. + #[cold] + #[inline] + pub fn not_found() -> Self { + Self::new(InternalStoreErrorKind::EntityNotFound) + } + + /// An error indicating that a [`Store`] resource does not originate from the store. + #[cold] + #[inline] + pub fn store_mismatch() -> Self { + Self::new(InternalStoreErrorKind::StoreMismatch) + } + + /// An error indicating that restoring a [`PrunedStore`] to a [`Store`] mismatched `T`. + #[cold] + #[inline] + pub fn restore_type_mismatch() -> Self { + Self::new(InternalStoreErrorKind::RestoreTypeMismatch( + RestoreTypeMismatchError::new::(), + )) + } +} + +impl fmt::Display for InternalStoreError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let message = match &self.kind { + InternalStoreErrorKind::RestoreTypeMismatch(error) => { + return fmt::Display::fmt(error, f) + } + InternalStoreErrorKind::StoreMismatch => "store owner mismatch", + InternalStoreErrorKind::EntityNotFound => "entity not found", + }; + write!(f, "failed to resolve entity: {message}") + } +} + +#[derive(Debug, Copy, Clone)] +enum InternalStoreErrorKind { + /// An error when restoring a [`PrunedStore`] with an incorrect `T` for [`Store`]. + RestoreTypeMismatch(RestoreTypeMismatchError), + /// An error indicating that a store resource does not originate from the given store. + StoreMismatch, + /// An error indicating that a store resource was not found. + EntityNotFound, +} + +impl StoreError { + /// Create a new [`StoreError`] from the external `error`. + pub fn external(error: E) -> Self { + Self::External(error) + } +} + +impl From for StoreError { + fn from(error: InternalStoreError) -> Self { + Self::Internal(error) + } +} + +/// Error occurred when restoring a [`PrunedStore`] to a [`Store`] with an mismatching `T`. +#[derive(Debug, Copy, Clone)] +struct RestoreTypeMismatchError { + type_name: fn() -> &'static str, +} + +impl RestoreTypeMismatchError { + /// Create a new [`RestoreTypeMismatchError`]. + pub fn new() -> Self { + Self { + type_name: type_name::, + } + } +} + +impl fmt::Display for RestoreTypeMismatchError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "failed to unprune store due to type mismatch: {}", + (self.type_name)() + ) + } +} diff --git a/crates/wasmi/src/store/inner.rs b/crates/wasmi/src/store/inner.rs index 4d4f494912..30d55c1b7e 100644 --- a/crates/wasmi/src/store/inner.rs +++ b/crates/wasmi/src/store/inner.rs @@ -4,6 +4,7 @@ use crate::{ engine::DedupFuncType, memory::DataSegment, reftype::{ExternRef, ExternRefEntity, ExternRefIdx}, + store::error::InternalStoreError, DataSegmentEntity, DataSegmentIdx, ElementSegment, @@ -181,19 +182,17 @@ impl StoreInner { /// Unwraps the given [`Stored`] reference and returns the `Idx`. /// - /// # Panics + /// # Errors /// /// If the [`Stored`] does not originate from this [`StoreInner`]. - pub(super) fn unwrap_stored(&self, stored: &Stored) -> Idx + pub(super) fn unwrap_stored(&self, stored: &Stored) -> Result where Idx: ArenaIndex + Debug, { - stored.entity_index(self.store_idx).unwrap_or_else(|| { - panic!( - "entity reference ({:?}) does not belong to store {:?}", - stored, self.store_idx, - ) - }) + match stored.entity_index(self.store_idx) { + Some(index) => Ok(index), + None => Err(InternalStoreError::store_mismatch()), + } } /// Allocates a new [`CoreGlobal`] and returns a [`Global`] reference to it. @@ -263,7 +262,10 @@ impl StoreInner { init.is_initialized(), "encountered an uninitialized new instance entity: {init:?}", ); - let idx = self.unwrap_stored(instance.as_inner()); + let idx = match self.unwrap_stored(instance.as_inner()) { + Ok(idx) => idx, + Err(error) => panic!("failed to unwrap stored entity: {error}"), + }; let uninit = self .instances .get_mut(idx) @@ -277,7 +279,7 @@ impl StoreInner { /// Returns a shared reference to the entity indexed by the given `idx`. /// - /// # Panics + /// # Errors /// /// - If the indexed entity does not originate from this [`StoreInner`]. /// - If the entity index cannot be resolved to its entity. @@ -285,14 +287,15 @@ impl StoreInner { &self, idx: &Stored, entities: &'a Arena, - ) -> &'a Entity + ) -> Result<&'a Entity, InternalStoreError> where Idx: ArenaIndex + Debug, { - let idx = self.unwrap_stored(idx); - entities - .get(idx) - .unwrap_or_else(|| panic!("failed to resolve stored entity: {idx:?}")) + let idx = self.unwrap_stored(idx)?; + match entities.get(idx) { + Some(entity) => Ok(entity), + None => Err(InternalStoreError::not_found()), + } } /// Returns an exclusive reference to the entity indexed by the given `idx`. @@ -302,16 +305,20 @@ impl StoreInner { /// Due to borrow checking issues this method takes an already unwrapped /// `Idx` unlike the [`StoreInner::resolve`] method. /// - /// # Panics + /// # Errors /// - /// - If the entity index cannot be resolved to its entity. - fn resolve_mut(idx: Idx, entities: &mut Arena) -> &mut Entity + /// If the entity index cannot be resolved to its entity. + fn resolve_mut( + idx: Idx, + entities: &mut Arena, + ) -> Result<&mut Entity, InternalStoreError> where Idx: ArenaIndex + Debug, { - entities - .get_mut(idx) - .unwrap_or_else(|| panic!("failed to resolve stored entity: {idx:?}")) + match entities.get_mut(idx) { + Some(entity) => Ok(entity), + None => Err(InternalStoreError::not_found()), + } } /// Returns the [`FuncType`] associated to the given [`DedupFuncType`]. @@ -340,64 +347,70 @@ impl StoreInner { /// Returns a shared reference to the [`CoreGlobal`] associated to the given [`Global`]. /// - /// # Panics + /// # Errors /// /// - If the [`Global`] does not originate from this [`StoreInner`]. /// - If the [`Global`] cannot be resolved to its entity. - pub fn resolve_global(&self, global: &Global) -> &CoreGlobal { + pub fn try_resolve_global(&self, global: &Global) -> Result<&CoreGlobal, InternalStoreError> { self.resolve(global.as_inner(), &self.globals) } /// Returns an exclusive reference to the [`CoreGlobal`] associated to the given [`Global`]. /// - /// # Panics + /// # Errors /// /// - If the [`Global`] does not originate from this [`StoreInner`]. /// - If the [`Global`] cannot be resolved to its entity. - pub fn resolve_global_mut(&mut self, global: &Global) -> &mut CoreGlobal { - let idx = self.unwrap_stored(global.as_inner()); + pub fn try_resolve_global_mut( + &mut self, + global: &Global, + ) -> Result<&mut CoreGlobal, InternalStoreError> { + let idx = self.unwrap_stored(global.as_inner())?; Self::resolve_mut(idx, &mut self.globals) } /// Returns a shared reference to the [`CoreTable`] associated to the given [`Table`]. /// - /// # Panics + /// # Errors /// /// - If the [`Table`] does not originate from this [`StoreInner`]. /// - If the [`Table`] cannot be resolved to its entity. - pub fn resolve_table(&self, table: &Table) -> &CoreTable { + pub fn try_resolve_table(&self, table: &Table) -> Result<&CoreTable, InternalStoreError> { self.resolve(table.as_inner(), &self.tables) } /// Returns an exclusive reference to the [`CoreTable`] associated to the given [`Table`]. /// - /// # Panics + /// # Errors /// /// - If the [`Table`] does not originate from this [`StoreInner`]. /// - If the [`Table`] cannot be resolved to its entity. - pub fn resolve_table_mut(&mut self, table: &Table) -> &mut CoreTable { - let idx = self.unwrap_stored(table.as_inner()); + pub fn try_resolve_table_mut( + &mut self, + table: &Table, + ) -> Result<&mut CoreTable, InternalStoreError> { + let idx = self.unwrap_stored(table.as_inner())?; Self::resolve_mut(idx, &mut self.tables) } /// Returns an exclusive reference to the [`CoreTable`] and [`CoreElementSegment`] associated to `table` and `elem`. /// - /// # Panics + /// # Errors /// /// - If the [`Table`] does not originate from this [`StoreInner`]. /// - If the [`Table`] cannot be resolved to its entity. /// - If the [`ElementSegment`] does not originate from this [`StoreInner`]. /// - If the [`ElementSegment`] cannot be resolved to its entity. - pub fn resolve_table_and_element_mut( + pub fn try_resolve_table_and_element_mut( &mut self, table: &Table, elem: &ElementSegment, - ) -> (&mut CoreTable, &mut CoreElementSegment) { - let table_idx = self.unwrap_stored(table.as_inner()); - let elem_idx = self.unwrap_stored(elem.as_inner()); - let table = Self::resolve_mut(table_idx, &mut self.tables); - let elem = Self::resolve_mut(elem_idx, &mut self.elems); - (table, elem) + ) -> Result<(&mut CoreTable, &mut CoreElementSegment), InternalStoreError> { + let table_idx = self.unwrap_stored(table.as_inner())?; + let elem_idx = self.unwrap_stored(elem.as_inner())?; + let table = Self::resolve_mut(table_idx, &mut self.tables)?; + let elem = Self::resolve_mut(elem_idx, &mut self.elems)?; + Ok((table, elem)) } /// Returns both @@ -405,35 +418,38 @@ impl StoreInner { /// - an exclusive reference to the [`CoreTable`] associated to the given [`Table`] /// - an exclusive reference to the [`Fuel`] of the [`StoreInner`]. /// - /// # Panics + /// # Errors /// /// - If the [`Table`] does not originate from this [`StoreInner`]. /// - If the [`Table`] cannot be resolved to its entity. - pub fn resolve_table_and_fuel_mut(&mut self, table: &Table) -> (&mut CoreTable, &mut Fuel) { - let idx = self.unwrap_stored(table.as_inner()); - let table = Self::resolve_mut(idx, &mut self.tables); + pub fn try_resolve_table_and_fuel_mut( + &mut self, + table: &Table, + ) -> Result<(&mut CoreTable, &mut Fuel), InternalStoreError> { + let idx = self.unwrap_stored(table.as_inner())?; + let table = Self::resolve_mut(idx, &mut self.tables)?; let fuel = &mut self.fuel; - (table, fuel) + Ok((table, fuel)) } /// Returns an exclusive reference to the [`CoreTable`] associated to the given [`Table`]. /// - /// # Panics + /// # Errors /// /// - If the [`Table`] does not originate from this [`StoreInner`]. /// - If the [`Table`] cannot be resolved to its entity. - pub fn resolve_table_pair_and_fuel( + pub fn try_resolve_table_pair_and_fuel( &mut self, fst: &Table, snd: &Table, - ) -> (&mut CoreTable, &mut CoreTable, &mut Fuel) { - let fst = self.unwrap_stored(fst.as_inner()); - let snd = self.unwrap_stored(snd.as_inner()); + ) -> Result<(&mut CoreTable, &mut CoreTable, &mut Fuel), InternalStoreError> { + let fst = self.unwrap_stored(fst.as_inner())?; + let snd = self.unwrap_stored(snd.as_inner())?; let (fst, snd) = self.tables.get_pair_mut(fst, snd).unwrap_or_else(|| { panic!("failed to resolve stored pair of entities: {fst:?} and {snd:?}") }); let fuel = &mut self.fuel; - (fst, snd, fuel) + Ok((fst, snd, fuel)) } /// Returns the following data: @@ -448,7 +464,7 @@ impl StoreInner { /// This method exists to properly handle use cases where /// otherwise the Rust borrow-checker would not accept. /// - /// # Panics + /// # Errors /// /// - If the [`Instance`] does not originate from this [`StoreInner`]. /// - If the [`Instance`] cannot be resolved to its entity. @@ -456,75 +472,87 @@ impl StoreInner { /// - If the [`Table`] cannot be resolved to its entity. /// - If the [`ElementSegment`] does not originate from this [`StoreInner`]. /// - If the [`ElementSegment`] cannot be resolved to its entity. - pub fn resolve_table_init_params( + pub fn try_resolve_table_init_params( &mut self, table: &Table, segment: &ElementSegment, - ) -> (&mut CoreTable, &CoreElementSegment, &mut Fuel) { - let mem_idx = self.unwrap_stored(table.as_inner()); + ) -> Result<(&mut CoreTable, &CoreElementSegment, &mut Fuel), InternalStoreError> { + let mem_idx = self.unwrap_stored(table.as_inner())?; let elem_idx = segment.as_inner(); - let elem = self.resolve(elem_idx, &self.elems); - let mem = Self::resolve_mut(mem_idx, &mut self.tables); + let elem = self.resolve(elem_idx, &self.elems)?; + let mem = Self::resolve_mut(mem_idx, &mut self.tables)?; let fuel = &mut self.fuel; - (mem, elem, fuel) + Ok((mem, elem, fuel)) } /// Returns a shared reference to the [`CoreElementSegment`] associated to the given [`ElementSegment`]. /// - /// # Panics + /// # Errors /// /// - If the [`ElementSegment`] does not originate from this [`StoreInner`]. /// - If the [`ElementSegment`] cannot be resolved to its entity. - pub fn resolve_element_segment(&self, segment: &ElementSegment) -> &CoreElementSegment { + pub fn try_resolve_element( + &self, + segment: &ElementSegment, + ) -> Result<&CoreElementSegment, InternalStoreError> { self.resolve(segment.as_inner(), &self.elems) } /// Returns an exclusive reference to the [`CoreElementSegment`] associated to the given [`ElementSegment`]. /// - /// # Panics + /// # Errors /// /// - If the [`ElementSegment`] does not originate from this [`StoreInner`]. /// - If the [`ElementSegment`] cannot be resolved to its entity. - pub fn resolve_element_segment_mut( + pub fn try_resolve_element_mut( &mut self, segment: &ElementSegment, - ) -> &mut CoreElementSegment { - let idx = self.unwrap_stored(segment.as_inner()); + ) -> Result<&mut CoreElementSegment, InternalStoreError> { + let idx = self.unwrap_stored(segment.as_inner())?; Self::resolve_mut(idx, &mut self.elems) } /// Returns a shared reference to the [`CoreMemory`] associated to the given [`Memory`]. /// - /// # Panics + /// # Errors /// /// - If the [`Memory`] does not originate from this [`StoreInner`]. /// - If the [`Memory`] cannot be resolved to its entity. - pub fn resolve_memory<'a>(&'a self, memory: &Memory) -> &'a CoreMemory { + pub fn try_resolve_memory<'a>( + &'a self, + memory: &Memory, + ) -> Result<&'a CoreMemory, InternalStoreError> { self.resolve(memory.as_inner(), &self.memories) } /// Returns an exclusive reference to the [`CoreMemory`] associated to the given [`Memory`]. /// - /// # Panics + /// # Errors /// /// - If the [`Memory`] does not originate from this [`StoreInner`]. /// - If the [`Memory`] cannot be resolved to its entity. - pub fn resolve_memory_mut<'a>(&'a mut self, memory: &Memory) -> &'a mut CoreMemory { - let idx = self.unwrap_stored(memory.as_inner()); + pub fn try_resolve_memory_mut<'a>( + &'a mut self, + memory: &Memory, + ) -> Result<&'a mut CoreMemory, InternalStoreError> { + let idx = self.unwrap_stored(memory.as_inner())?; Self::resolve_mut(idx, &mut self.memories) } /// Returns an exclusive reference to the [`CoreMemory`] associated to the given [`Memory`]. /// - /// # Panics + /// # Errors /// /// - If the [`Memory`] does not originate from this [`StoreInner`]. /// - If the [`Memory`] cannot be resolved to its entity. - pub fn resolve_memory_and_fuel_mut(&mut self, memory: &Memory) -> (&mut CoreMemory, &mut Fuel) { - let idx = self.unwrap_stored(memory.as_inner()); - let memory = Self::resolve_mut(idx, &mut self.memories); + pub fn try_resolve_memory_and_fuel_mut( + &mut self, + memory: &Memory, + ) -> Result<(&mut CoreMemory, &mut Fuel), InternalStoreError> { + let idx = self.unwrap_stored(memory.as_inner())?; + let memory = Self::resolve_mut(idx, &mut self.memories)?; let fuel = &mut self.fuel; - (memory, fuel) + Ok((memory, fuel)) } /// Returns the following data: @@ -538,73 +566,82 @@ impl StoreInner { /// This method exists to properly handle use cases where /// otherwise the Rust borrow-checker would not accept. /// - /// # Panics + /// # Errors /// /// - If the [`Memory`] does not originate from this [`StoreInner`]. /// - If the [`Memory`] cannot be resolved to its entity. /// - If the [`DataSegment`] does not originate from this [`StoreInner`]. /// - If the [`DataSegment`] cannot be resolved to its entity. - pub fn resolve_memory_init_params( + pub fn try_resolve_memory_init_params( &mut self, memory: &Memory, segment: &DataSegment, - ) -> (&mut CoreMemory, &DataSegmentEntity, &mut Fuel) { - let mem_idx = self.unwrap_stored(memory.as_inner()); + ) -> Result<(&mut CoreMemory, &DataSegmentEntity, &mut Fuel), InternalStoreError> { + let mem_idx = self.unwrap_stored(memory.as_inner())?; let data_idx = segment.as_inner(); - let data = self.resolve(data_idx, &self.datas); - let mem = Self::resolve_mut(mem_idx, &mut self.memories); + let data = self.resolve(data_idx, &self.datas)?; + let mem = Self::resolve_mut(mem_idx, &mut self.memories)?; let fuel = &mut self.fuel; - (mem, data, fuel) + Ok((mem, data, fuel)) } /// Returns an exclusive pair of references to the [`CoreMemory`] associated to the given [`Memory`]s. /// - /// # Panics + /// # Errors /// /// - If the [`Memory`] does not originate from this [`StoreInner`]. /// - If the [`Memory`] cannot be resolved to its entity. - pub fn resolve_memory_pair_and_fuel( + pub fn try_resolve_memory_pair_and_fuel( &mut self, fst: &Memory, snd: &Memory, - ) -> (&mut CoreMemory, &mut CoreMemory, &mut Fuel) { - let fst = self.unwrap_stored(fst.as_inner()); - let snd = self.unwrap_stored(snd.as_inner()); + ) -> Result<(&mut CoreMemory, &mut CoreMemory, &mut Fuel), InternalStoreError> { + let fst = self.unwrap_stored(fst.as_inner())?; + let snd = self.unwrap_stored(snd.as_inner())?; let (fst, snd) = self.memories.get_pair_mut(fst, snd).unwrap_or_else(|| { panic!("failed to resolve stored pair of entities: {fst:?} and {snd:?}") }); let fuel = &mut self.fuel; - (fst, snd, fuel) + Ok((fst, snd, fuel)) } /// Returns an exclusive reference to the [`DataSegmentEntity`] associated to the given [`DataSegment`]. /// - /// # Panics + /// # Errors /// /// - If the [`DataSegment`] does not originate from this [`StoreInner`]. /// - If the [`DataSegment`] cannot be resolved to its entity. - pub fn resolve_data_segment_mut(&mut self, segment: &DataSegment) -> &mut DataSegmentEntity { - let idx = self.unwrap_stored(segment.as_inner()); + pub fn try_resolve_data_mut( + &mut self, + segment: &DataSegment, + ) -> Result<&mut DataSegmentEntity, InternalStoreError> { + let idx = self.unwrap_stored(segment.as_inner())?; Self::resolve_mut(idx, &mut self.datas) } /// Returns a shared reference to the [`InstanceEntity`] associated to the given [`Instance`]. /// - /// # Panics + /// # Errors /// /// - If the [`Instance`] does not originate from this [`StoreInner`]. /// - If the [`Instance`] cannot be resolved to its entity. - pub fn resolve_instance(&self, instance: &Instance) -> &InstanceEntity { + pub fn try_resolve_instance( + &self, + instance: &Instance, + ) -> Result<&InstanceEntity, InternalStoreError> { self.resolve(instance.as_inner(), &self.instances) } /// Returns a shared reference to the [`ExternRefEntity`] associated to the given [`ExternRef`]. /// - /// # Panics + /// # Errors /// /// - If the [`ExternRef`] does not originate from this [`StoreInner`]. /// - If the [`ExternRef`] cannot be resolved to its entity. - pub fn resolve_external_object(&self, object: &ExternRef) -> &ExternRefEntity { + pub fn try_resolve_externref( + &self, + object: &ExternRef, + ) -> Result<&ExternRefEntity, InternalStoreError> { self.resolve(object.as_inner(), &self.extern_objects) } @@ -616,14 +653,102 @@ impl StoreInner { /// Returns a shared reference to the associated entity of the Wasm or host function. /// - /// # Panics + /// # Errors /// /// - If the [`Func`] does not originate from this [`StoreInner`]. /// - If the [`Func`] cannot be resolved to its entity. - pub fn resolve_func(&self, func: &Func) -> &FuncEntity { - let entity_index = self.unwrap_stored(func.as_inner()); - self.funcs.get(entity_index).unwrap_or_else(|| { - panic!("failed to resolve stored Wasm or host function: {entity_index:?}") - }) + pub fn try_resolve_func(&self, func: &Func) -> Result<&FuncEntity, InternalStoreError> { + self.resolve(func.as_inner(), &self.funcs) + } +} + +macro_rules! define_panicking_getters { + ( + $( + pub fn $getter:ident($receiver:ty, $( $param_name:ident: $param_ty:ty ),* $(,)? ) -> $ret_ty:ty = $try_getter:expr + );* + $(;)? + ) => { + $( + #[doc = ::core::concat!( + "Resolves `", + ::core::stringify!($ret_ty), + "` via [`", + ::core::stringify!($try_getter), + "`] panicking upon error." + )] + pub fn $getter(self: $receiver, $( $param_name: $param_ty ),*) -> $ret_ty { + match $try_getter(self, $($param_name),*) { + ::core::result::Result::Ok(value) => value, + ::core::result::Result::Err(error) => ::core::panic!( + ::core::concat!( + "failed to resolve stored", + $( " ", ::core::stringify!($param_name), )* + ": {}" + ), + error, + ) + } + } + )* + }; +} +impl StoreInner { + define_panicking_getters! { + pub fn resolve_global(&Self, global: &Global) -> &CoreGlobal = Self::try_resolve_global; + pub fn resolve_global_mut(&mut Self, global: &Global) -> &mut CoreGlobal = Self::try_resolve_global_mut; + + pub fn resolve_memory(&Self, memory: &Memory) -> &CoreMemory = Self::try_resolve_memory; + pub fn resolve_memory_mut(&mut Self, memory: &Memory) -> &mut CoreMemory = Self::try_resolve_memory_mut; + + pub fn resolve_table(&Self, table: &Table) -> &CoreTable = Self::try_resolve_table; + pub fn resolve_table_mut(&mut Self, table: &Table) -> &mut CoreTable = Self::try_resolve_table_mut; + + pub fn resolve_element(&Self, elem: &ElementSegment) -> &CoreElementSegment = Self::try_resolve_element; + pub fn resolve_element_mut(&mut Self, elem: &ElementSegment) -> &mut CoreElementSegment = Self::try_resolve_element_mut; + + pub fn resolve_func(&Self, func: &Func) -> &FuncEntity = Self::try_resolve_func; + pub fn resolve_data_mut(&mut Self, data: &DataSegment) -> &mut DataSegmentEntity = Self::try_resolve_data_mut; + pub fn resolve_instance(&Self, instance: &Instance) -> &InstanceEntity = Self::try_resolve_instance; + pub fn resolve_externref(&Self, data: &ExternRef) -> &ExternRefEntity = Self::try_resolve_externref; + + pub fn resolve_table_and_element_mut( + &mut Self, + table: &Table, elem: &ElementSegment, + ) -> (&mut CoreTable, &mut CoreElementSegment) = Self::try_resolve_table_and_element_mut; + + pub fn resolve_table_and_fuel_mut( + &mut Self, + table: &Table, + ) -> (&mut CoreTable, &mut Fuel) = Self::try_resolve_table_and_fuel_mut; + + pub fn resolve_table_pair_and_fuel( + &mut Self, + fst: &Table, + snd: &Table, + ) -> (&mut CoreTable, &mut CoreTable, &mut Fuel) = Self::try_resolve_table_pair_and_fuel; + + pub fn resolve_table_init_params( + &mut Self, + table: &Table, + elem: &ElementSegment, + ) -> (&mut CoreTable, &CoreElementSegment, &mut Fuel) = Self::try_resolve_table_init_params; + + pub fn resolve_memory_and_fuel_mut( + &mut Self, + memory: &Memory, + ) -> (&mut CoreMemory, &mut Fuel) = Self::try_resolve_memory_and_fuel_mut; + + pub fn resolve_memory_init_params( + &mut Self, + memory: &Memory, + segment: &DataSegment, + ) -> (&mut CoreMemory, &DataSegmentEntity, &mut Fuel) = Self::try_resolve_memory_init_params; + + pub fn resolve_memory_pair_and_fuel( + &mut Self, + fst: &Memory, + snd: &Memory, + ) -> (&mut CoreMemory, &mut CoreMemory, &mut Fuel) = Self::try_resolve_memory_pair_and_fuel; } } diff --git a/crates/wasmi/src/store/mod.rs b/crates/wasmi/src/store/mod.rs index 639ddc8192..bda67cd5ff 100644 --- a/crates/wasmi/src/store/mod.rs +++ b/crates/wasmi/src/store/mod.rs @@ -1,4 +1,5 @@ mod context; +mod error; mod inner; mod pruned; mod typeid; @@ -6,6 +7,7 @@ mod typeid; use self::pruned::PrunedStoreVTable; pub use self::{ context::{AsContext, AsContextMut, StoreContext, StoreContextMut}, + error::{InternalStoreError, StoreError}, inner::{StoreInner, Stored}, pruned::PrunedStore, }; @@ -109,9 +111,11 @@ impl Store { func: &HostFuncEntity, instance: Option<&Instance>, params_results: FuncInOut, - ) -> Result<(), Error> { - let trampoline = self.resolve_trampoline(func.trampoline()).clone(); - trampoline.call(self, instance, params_results)?; + ) -> Result<(), StoreError> { + let trampoline = self.resolve_trampoline(func.trampoline())?.clone(); + trampoline + .call(self, instance, params_results) + .map_err(StoreError::external)?; Ok(()) } @@ -223,12 +227,15 @@ impl Store { /// /// - If the [`Trampoline`] does not originate from this [`Store`]. /// - If the [`Trampoline`] cannot be resolved to its entity. - fn resolve_trampoline(&self, func: &Trampoline) -> &TrampolineEntity { - let entity_index = self.inner.unwrap_stored(func.as_inner()); - self.typed - .trampolines - .get(entity_index) - .unwrap_or_else(|| panic!("failed to resolve stored host function: {entity_index:?}")) + fn resolve_trampoline( + &self, + func: &Trampoline, + ) -> Result<&TrampolineEntity, InternalStoreError> { + let entity_index = self.inner.unwrap_stored(func.as_inner())?; + let Some(trampoline) = self.typed.trampolines.get(entity_index) else { + return Err(InternalStoreError::not_found()); + }; + Ok(trampoline) } /// Sets a callback function that is executed whenever a WebAssembly diff --git a/crates/wasmi/src/store/pruned.rs b/crates/wasmi/src/store/pruned.rs index c5b492f286..10b38f140f 100644 --- a/crates/wasmi/src/store/pruned.rs +++ b/crates/wasmi/src/store/pruned.rs @@ -2,6 +2,7 @@ use super::{typeid, CallHooks, FuncInOut, HostFuncEntity, StoreInner}; use crate::{ core::{hint, UntypedVal}, errors::{MemoryError, TableError}, + store::error::{InternalStoreError, StoreError}, CallHook, Error, Instance, @@ -10,7 +11,6 @@ use crate::{ Table, }; use core::{ - any::type_name, fmt::{self, Debug}, mem, }; @@ -36,16 +36,17 @@ pub struct PrunedStoreVTable { instance: Option<&Instance>, params_results: FuncInOut, call_hooks: CallHooks, - ) -> Result<(), Error>, + ) -> Result<(), StoreError>, /// Grows `memory` by `delta` pages. - grow_memory: fn(&mut PrunedStore, memory: &Memory, delta: u64) -> Result, + grow_memory: + fn(&mut PrunedStore, memory: &Memory, delta: u64) -> Result>, /// Grows `table` by `delta` items filling with `init`. grow_table: fn( &mut PrunedStore, table: &Table, delta: u64, init: UntypedVal, - ) -> Result, + ) -> Result>, } impl PrunedStoreVTable { pub fn new() -> Self { @@ -55,35 +56,43 @@ impl PrunedStoreVTable { instance: Option<&Instance>, params_results: FuncInOut, call_hooks: CallHooks| - -> Result<(), Error> { - let store: &mut Store = pruned.restore_or_panic(); + -> Result<(), StoreError> { + let store: &mut Store = pruned.restore()?; if matches!(call_hooks, CallHooks::Call) { - store.invoke_call_hook(CallHook::CallingHost)?; + store + .invoke_call_hook(CallHook::CallingHost) + .map_err(StoreError::external)?; } store.call_host_func(func, instance, params_results)?; if matches!(call_hooks, CallHooks::Call) { - store.invoke_call_hook(CallHook::ReturningFromHost)?; + store + .invoke_call_hook(CallHook::ReturningFromHost) + .map_err(StoreError::external)?; } Ok(()) }, grow_memory: |pruned: &mut PrunedStore, memory: &Memory, delta: u64| - -> Result { - let store: &mut Store = pruned.restore_or_panic(); + -> Result> { + let store: &mut Store = pruned.restore()?; let (store, mut resource_limiter) = store.store_inner_and_resource_limiter_ref(); - let (memory, fuel) = store.resolve_memory_and_fuel_mut(memory); - memory.grow(delta, Some(fuel), &mut resource_limiter) + let (memory, fuel) = store.try_resolve_memory_and_fuel_mut(memory)?; + memory + .grow(delta, Some(fuel), &mut resource_limiter) + .map_err(StoreError::external) }, grow_table: |pruned: &mut PrunedStore, table: &Table, delta: u64, init: UntypedVal| - -> Result { - let store: &mut Store = pruned.restore_or_panic(); + -> Result> { + let store: &mut Store = pruned.restore()?; let (store, mut resource_limiter) = store.store_inner_and_resource_limiter_ref(); - let (table, fuel) = store.resolve_table_and_fuel_mut(table); - table.grow_untyped(delta, init, Some(fuel), &mut resource_limiter) + let (table, fuel) = store.try_resolve_table_and_fuel_mut(table)?; + table + .grow_untyped(delta, init, Some(fuel), &mut resource_limiter) + .map_err(StoreError::external) }, } } @@ -97,7 +106,7 @@ impl PrunedStoreVTable { instance: Option<&Instance>, params_results: FuncInOut, call_hooks: CallHooks, - ) -> Result<(), Error> { + ) -> Result<(), StoreError> { (self.call_host_func)(pruned, func, instance, params_results, call_hooks) } @@ -107,7 +116,7 @@ impl PrunedStoreVTable { pruned: &mut PrunedStore, memory: &Memory, delta: u64, - ) -> Result { + ) -> Result> { (self.grow_memory)(pruned, memory, delta) } @@ -118,7 +127,7 @@ impl PrunedStoreVTable { table: &Table, delta: u64, init: UntypedVal, - ) -> Result { + ) -> Result> { (self.grow_table)(pruned, table, delta, init) } } @@ -187,7 +196,7 @@ impl PrunedStore { instance: Option<&Instance>, params_results: FuncInOut, call_hooks: CallHooks, - ) -> Result<(), Error> { + ) -> Result<(), StoreError> { self.pruned.restore_pruned.clone().call_host_func( self, func, @@ -199,7 +208,11 @@ impl PrunedStore { /// Grows the [`Memory`] by `delta` pages and returns the result. #[inline] - pub fn grow_memory(&mut self, memory: &Memory, delta: u64) -> Result { + pub fn grow_memory( + &mut self, + memory: &Memory, + delta: u64, + ) -> Result> { self.pruned .restore_pruned .clone() @@ -213,37 +226,22 @@ impl PrunedStore { table: &Table, delta: u64, init: UntypedVal, - ) -> Result { + ) -> Result> { self.pruned .restore_pruned .clone() .grow_table(self, table, delta, init) } - /// Restores `self` to a proper [`Store`] if possible. - /// - /// # Panics - /// - /// If the `T` of the resulting [`Store`] does not match the given `T`. - fn restore_or_panic(&mut self) -> &mut Store { - let Ok(store) = self.restore() else { - panic!( - "failed to convert `PrunedStore` back into `Store<{}>`", - type_name::(), - ); - }; - store - } - /// Restores `self` to a proper [`Store`] if possible. /// /// # Errors /// /// If the `T` of the resulting [`Store`] does not match the given `T`. #[inline] - fn restore(&mut self) -> Result<&mut Store, PrunedStoreError> { + fn restore(&mut self) -> Result<&mut Store, InternalStoreError> { if hint::unlikely(typeid::of::() != self.pruned.id) { - return Err(PrunedStoreError); + return Err(InternalStoreError::restore_type_mismatch::()); } let store = { // Safety: we restore the original `Store` from the pruned `Store`. @@ -259,10 +257,6 @@ impl PrunedStore { } } -/// Returned when [`PrunedStore::restore`] failed. -#[derive(Debug)] -pub struct PrunedStoreError; - #[test] fn pruning_works() { let engine = Engine::default(); diff --git a/crates/wasmi/src/table/element.rs b/crates/wasmi/src/table/element.rs index bf96be67f4..5cfc9d91eb 100644 --- a/crates/wasmi/src/table/element.rs +++ b/crates/wasmi/src/table/element.rs @@ -78,10 +78,6 @@ impl ElementSegment { /// Returns the number of items in the [`ElementSegment`]. pub fn size(&self, ctx: impl AsContext) -> u32 { - ctx.as_context() - .store - .inner - .resolve_element_segment(self) - .size() + ctx.as_context().store.inner.resolve_element(self).size() } }