Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Catch panics on the FFI boundary between the runtime and the host for wasmtime #11189

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion client/executor/runtime-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ sp_core::wasm_export_functions! {
}
}

fn test_exhaust_heap() -> Vec<u8> { Vec::with_capacity(16777216) }
fn test_allocate_vec(size: u32) -> Vec<u8> {
Vec::with_capacity(size as usize)
}

fn test_fp_f32add(a: [u8; 4], b: [u8; 4]) -> [u8; 4] {
let a = f32::from_le_bytes(a);
Expand Down
31 changes: 29 additions & 2 deletions client/executor/src/integration_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,8 +466,8 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) {
RuntimeBlob::uncompress_if_needed(wasm_binary_unwrap()).unwrap(),
&mut ext.ext(),
true,
"test_exhaust_heap",
&[0],
"test_allocate_vec",
&16777216_u32.encode(),
)
.map_err(|e| e.to_string())
.unwrap_err();
Expand Down Expand Up @@ -803,3 +803,30 @@ fn unreachable_intrinsic(wasm_method: WasmExecutionMethod) {
error => panic!("unexpected error: {:?}", error),
}
}

test_wasm_execution!(panic_in_host_function);
fn panic_in_host_function(wasm_method: WasmExecutionMethod) {
koute marked this conversation as resolved.
Show resolved Hide resolved
let mut ext = TestExternalities::default();
let mut ext = ext.ext();

// We call the `test_allocate_vec` here since that's a convenient way of triggering
// a panic in a host function.
match call_in_wasm("test_allocate_vec", &0x0FFFFFFF_u32.encode(), wasm_method, &mut ext)
.unwrap_err()
{
#[cfg(feature = "wasmtime")]
Error::AbortedDueToTrap(error) if wasm_method == WasmExecutionMethod::Compiled => {
assert_eq!(
error.message,
r#"host code panicked while being called by the runtime: Failed to allocate memory: "Requested allocation size is too large""#
);
},
Error::RuntimePanicked(error) if wasm_method == WasmExecutionMethod::Interpreted => {
assert_eq!(
error,
r#"Failed to allocate memory: "Requested allocation size is too large""#
);
},
error => panic!("unexpected error: {:?}", error),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -374,11 +374,27 @@ fn generate_host_function_implementation(
-> std::result::Result<#ffi_return_ty, #crate_::sp_wasm_interface::wasmtime::Trap>
{
T::with_function_context(caller, move |__function_context__| {
#struct_name::call(
__function_context__,
#(#ffi_names,)*
)
}).map_err(#crate_::sp_wasm_interface::wasmtime::Trap::new)
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
#struct_name::call(
__function_context__,
#(#ffi_names,)*
).map_err(#crate_::sp_wasm_interface::wasmtime::Trap::new)
}));
match result {
Ok(result) => result,
Err(panic) => {
let message =
if let Some(message) = panic.downcast_ref::<String>() {
format!("host code panicked while being called by the runtime: {}", message)
} else if let Some(message) = panic.downcast_ref::<&'static str>() {
format!("host code panicked while being called by the runtime: {}", message)
} else {
"host code panicked while being called by the runtime".to_owned()
};
return Err(#crate_::sp_wasm_interface::wasmtime::Trap::new(message));
}
}
})
}
)?;
};
Expand Down