mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 12:17:58 +00:00
Add a new host function for reporting fatal errors; make WASM backtraces readable when printing out errors (#10741)
* Add a new host function for reporting fatal errors * Fix one of the wasmtime executor tests * Have `#[runtime_interface(wasm_only)]` actually mean WASM-only, and not no_std-only * Print out errors through `Display` instead of `Debug` * Switch one more trait to require `Error` for its error instead of only `Debug` * Align to review comments
This commit is contained in:
@@ -45,6 +45,7 @@ unsafe impl Send for SandboxStore {}
|
||||
pub struct HostState {
|
||||
sandbox_store: SandboxStore,
|
||||
allocator: FreeingBumpHeapAllocator,
|
||||
panic_message: Option<String>,
|
||||
}
|
||||
|
||||
impl HostState {
|
||||
@@ -55,8 +56,14 @@ impl HostState {
|
||||
sandbox::SandboxBackend::TryWasmer,
|
||||
)))),
|
||||
allocator,
|
||||
panic_message: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes the error message out of the host state, leaving a `None` in its place.
|
||||
pub fn take_panic_message(&mut self) -> Option<String> {
|
||||
self.panic_message.take()
|
||||
}
|
||||
}
|
||||
|
||||
/// A `HostContext` implements `FunctionContext` for making host calls from a Wasmtime
|
||||
@@ -134,6 +141,14 @@ impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> {
|
||||
fn sandbox(&mut self) -> &mut dyn Sandbox {
|
||||
self
|
||||
}
|
||||
|
||||
fn register_panic_error_message(&mut self, message: &str) {
|
||||
self.caller
|
||||
.data_mut()
|
||||
.host_state_mut()
|
||||
.expect("host state is not empty when calling a function in wasm; qed")
|
||||
.panic_message = Some(message.to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sandbox for HostContext<'a> {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
use crate::runtime::{Store, StoreData};
|
||||
use sc_executor_common::{
|
||||
error::{Error, Result},
|
||||
error::{Backtrace, Error, MessageWithBacktrace, Result},
|
||||
wasm_runtime::InvokeMethod,
|
||||
};
|
||||
use sp_wasm_interface::{HostFunctions, Pointer, Value, WordSize};
|
||||
@@ -53,25 +53,51 @@ pub struct EntryPoint {
|
||||
|
||||
impl EntryPoint {
|
||||
/// Call this entry point.
|
||||
pub fn call(
|
||||
pub(crate) fn call(
|
||||
&self,
|
||||
ctx: impl AsContextMut,
|
||||
store: &mut Store,
|
||||
data_ptr: Pointer<u8>,
|
||||
data_len: WordSize,
|
||||
) -> Result<u64> {
|
||||
let data_ptr = u32::from(data_ptr);
|
||||
let data_len = u32::from(data_len);
|
||||
|
||||
fn handle_trap(err: wasmtime::Trap) -> Error {
|
||||
Error::from(format!("Wasm execution trapped: {}", err))
|
||||
}
|
||||
|
||||
match self.call_type {
|
||||
EntryPointType::Direct { ref entrypoint } =>
|
||||
entrypoint.call(ctx, (data_ptr, data_len)).map_err(handle_trap),
|
||||
entrypoint.call(&mut *store, (data_ptr, data_len)),
|
||||
EntryPointType::Wrapped { func, ref dispatcher } =>
|
||||
dispatcher.call(ctx, (func, data_ptr, data_len)).map_err(handle_trap),
|
||||
dispatcher.call(&mut *store, (func, data_ptr, data_len)),
|
||||
}
|
||||
.map_err(|trap| {
|
||||
let host_state = store
|
||||
.data_mut()
|
||||
.host_state
|
||||
.as_mut()
|
||||
.expect("host state cannot be empty while a function is being called; qed");
|
||||
|
||||
// The logic to print out a backtrace is somewhat complicated,
|
||||
// so let's get wasmtime to print it out for us.
|
||||
let mut backtrace_string = trap.to_string();
|
||||
let suffix = "\nwasm backtrace:";
|
||||
if let Some(index) = backtrace_string.find(suffix) {
|
||||
// Get rid of the error message and just grab the backtrace,
|
||||
// since we're storing the error message ourselves separately.
|
||||
backtrace_string.replace_range(0..index + suffix.len(), "");
|
||||
}
|
||||
|
||||
let backtrace = Backtrace { backtrace_string };
|
||||
if let Some(error) = host_state.take_panic_message() {
|
||||
Error::AbortedDueToPanic(MessageWithBacktrace {
|
||||
message: error,
|
||||
backtrace: Some(backtrace),
|
||||
})
|
||||
} else {
|
||||
Error::AbortedDueToTrap(MessageWithBacktrace {
|
||||
message: trap.display_reason().to_string(),
|
||||
backtrace: Some(backtrace),
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn direct(
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::{Decode as _, Encode as _};
|
||||
use sc_executor_common::{runtime_blob::RuntimeBlob, wasm_runtime::WasmModule};
|
||||
use sc_executor_common::{error::Error, runtime_blob::RuntimeBlob, wasm_runtime::WasmModule};
|
||||
use sc_runtime_test::wasm_binary_unwrap;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -158,11 +158,13 @@ fn test_stack_depth_reaching() {
|
||||
};
|
||||
let mut instance = runtime.new_instance().expect("failed to instantiate a runtime");
|
||||
|
||||
let err = instance.call_export("test-many-locals", &[]).unwrap_err();
|
||||
|
||||
assert!(format!("{:?}", err).starts_with(
|
||||
"Other(\"Wasm execution trapped: wasm trap: wasm `unreachable` instruction executed"
|
||||
));
|
||||
match instance.call_export("test-many-locals", &[]).unwrap_err() {
|
||||
Error::AbortedDueToTrap(error) => {
|
||||
let expected = "wasm trap: wasm `unreachable` instruction executed";
|
||||
assert_eq!(error.message, expected);
|
||||
},
|
||||
error => panic!("unexpected error: {:?}", error),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user