diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs index e6a6ef3a61039..ed8b63f53bd31 100644 --- a/client/executor/wasmi/src/lib.rs +++ b/client/executor/wasmi/src/lib.rs @@ -46,6 +46,7 @@ struct FunctionExecutor<'a> { host_functions: &'a [&'static dyn Function], allow_missing_func_imports: bool, missing_functions: &'a [String], + instance_panicked: Option, } impl<'a> FunctionExecutor<'a> { @@ -65,8 +66,13 @@ impl<'a> FunctionExecutor<'a> { host_functions, allow_missing_func_imports, missing_functions, + instance_panicked: None, }) } + + fn panicked(&mut self) -> Option { + self.instance_panicked.take() + } } impl<'a> sandbox::SandboxCapabilities for FunctionExecutor<'a> { @@ -124,6 +130,10 @@ impl<'a> FunctionContext for FunctionExecutor<'a> { fn sandbox(&mut self) -> &mut dyn Sandbox { self } + + fn instance_panicked(&mut self, message: &str) { + self.instance_panicked = Some(Error::RuntimePanicked(message.into())); + } } impl<'a> Sandbox for FunctionExecutor<'a> { @@ -503,7 +513,11 @@ fn call_in_wasm_module( "Failed to execute code with {} pages", memory.current_size().0, ); - Err(e.into()) + + match function_executor.panicked() { + Some(e) => Err(e), + None => Err(e.into()), + } }, _ => Err(Error::InvalidReturn), } diff --git a/client/executor/wasmtime/src/host.rs b/client/executor/wasmtime/src/host.rs index c1eb77ff81f34..02041ac5e35b2 100644 --- a/client/executor/wasmtime/src/host.rs +++ b/client/executor/wasmtime/src/host.rs @@ -54,6 +54,7 @@ pub struct HostState { sandbox_store: RefCell>, allocator: RefCell, instance: Rc, + instance_panicked: RefCell>, } impl HostState { @@ -63,6 +64,7 @@ impl HostState { sandbox_store: RefCell::new(sandbox::Store::new()), allocator: RefCell::new(allocator), instance, + instance_panicked: RefCell::new(None), } } @@ -70,6 +72,11 @@ impl HostState { pub fn materialize<'a>(&'a self) -> HostContext<'a> { HostContext(self) } + + /// Returns an error if the wasm instance informed about us about itself panicking. + fn panicked(&mut self) -> Option { + self.instance_panicked.borrow_mut().take() + } } /// A `HostContext` implements `FunctionContext` for making host calls from a Wasmtime @@ -156,6 +163,10 @@ impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> { fn sandbox(&mut self) -> &mut dyn Sandbox { self } + + fn instance_panicked(&mut self, message: &str) { + *self.instance_panicked.borrow_mut() = Some(Error::RuntimePanicked(message.into())); + } } impl<'a> Sandbox for HostContext<'a> { diff --git a/client/executor/wasmtime/src/instance_wrapper.rs b/client/executor/wasmtime/src/instance_wrapper.rs index 2103ab9b7b98c..f16cc772a206d 100644 --- a/client/executor/wasmtime/src/instance_wrapper.rs +++ b/client/executor/wasmtime/src/instance_wrapper.rs @@ -98,7 +98,7 @@ impl EntryPoint { let data_ptr = u32::from(data_ptr) as i32; let data_len = u32::from(data_len) as i32; - (match self.call_type { + match self.call_type { EntryPointType::Direct => { self.func.call(&[ wasmtime::Val::I32(data_ptr), @@ -112,15 +112,14 @@ impl EntryPoint { wasmtime::Val::I32(data_len), ]) }, - }) - .map(|results| - // the signature is checked to have i64 return type - results[0].unwrap_i64() as u64 - ) - .map_err(|err| Error::from(format!( - "Wasm execution trapped: {}", - err - ))) + }.map(|results| + // the signature is checked to have i64 return type + results[0].unwrap_i64() as u64 + ) + .map_err(|err| Error::from(format!( + "Wasm execution trapped: {}", + err, + ))) } pub fn direct(func: wasmtime::Func) -> std::result::Result { diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index a17a034918db7..1f85368123a77 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -153,10 +153,15 @@ fn perform_call( ) -> Result> { let (data_ptr, data_len) = inject_input_data(&instance_wrapper, &mut allocator, data)?; - let host_state = HostState::new(allocator, instance_wrapper.clone()); - let ret = state_holder::with_initialized_state(&host_state, || -> Result<_> { - Ok(unpack_ptr_and_len(entrypoint.call(data_ptr, data_len)?)) - }); + let mut host_state = HostState::new(allocator, instance_wrapper.clone()); + let ret = state_holder::with_initialized_state(&host_state, || + entrypoint.call(data_ptr, data_len).map(unpack_ptr_and_len) + ); + + if let Some(err) = host_state.panicked() { + return Err(err); + } + let (output_ptr, output_len) = ret?; let output = extract_output_data(&instance_wrapper, output_ptr, output_len)?; diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 397dd3c21712a..62ba783583bf8 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -1007,6 +1007,17 @@ trait Allocator { } } +/// Wasm only panic handler. +#[runtime_interface(wasm_only)] +trait PanicHandler { + /// Should be called when the wasm instance is panicking. + /// + /// The given `message` should correspond to the reason the instance panicked. + fn panicking(&mut self, message: &str) { + self.instance_panicked(message); + } +} + /// Interface that provides functions for logging from within the runtime. #[runtime_interface] pub trait Logging { diff --git a/primitives/wasm-interface/src/lib.rs b/primitives/wasm-interface/src/lib.rs index fd200268473b0..cfbf32788d2ad 100644 --- a/primitives/wasm-interface/src/lib.rs +++ b/primitives/wasm-interface/src/lib.rs @@ -272,7 +272,9 @@ impl PartialEq for dyn Function { } } -/// Context used by `Function` to interact with the allocator and the memory of the wasm instance. +/// Context used by [`Function`] to interact with the executing context. +/// +/// This includes access to the memory and the sandbox. pub trait FunctionContext { /// Read memory from `address` into a vector. fn read_memory(&self, address: Pointer, size: WordSize) -> Result> { @@ -290,6 +292,8 @@ pub trait FunctionContext { fn deallocate_memory(&mut self, ptr: Pointer) -> Result<()>; /// Provides access to the sandbox. fn sandbox(&mut self) -> &mut dyn Sandbox; + /// The wasm instance panicked with the given `message`. + fn instance_panicked(&mut self, message: &str); } /// Sandbox memory identifier.