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:
Koute
2022-02-09 18:12:55 +09:00
committed by GitHub
parent bd261d57c4
commit 9a31b2c341
68 changed files with 554 additions and 249 deletions
@@ -22,7 +22,7 @@ mod sandbox;
use codec::{Decode, Encode};
use hex_literal::hex;
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 sp_core::{
blake2_128, blake2_256, ed25519, map,
@@ -122,7 +122,7 @@ fn call_in_wasm<E: Externalities>(
call_data: &[u8],
execution_method: WasmExecutionMethod,
ext: &mut E,
) -> Result<Vec<u8>, String> {
) -> Result<Vec<u8>, Error> {
let executor =
crate::WasmExecutor::<HostFunctions>::new(execution_method, Some(1024), 8, None, 2);
executor.uncached_call(
@@ -148,25 +148,16 @@ fn call_not_existing_function(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
match call_in_wasm(
"test_calling_missing_external",
&[],
wasm_method,
&mut ext,
) {
Ok(_) => panic!("was expected an `Err`"),
Err(e) => {
match wasm_method {
WasmExecutionMethod::Interpreted => assert_eq!(
&format!("{:?}", e),
"\"Trap: Trap { kind: Host(Other(\\\"Function `missing_external` is only a stub. Calling a stub is not allowed.\\\")) }\""
),
match call_in_wasm("test_calling_missing_external", &[], wasm_method, &mut ext).unwrap_err() {
Error::AbortedDueToTrap(error) => {
let expected = match wasm_method {
WasmExecutionMethod::Interpreted => "Trap: Host(Other(\"Function `missing_external` is only a stub. Calling a stub is not allowed.\"))",
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled => assert!(
format!("{:?}", e).contains("Wasm execution trapped: call to a missing function env:missing_external")
),
}
}
WasmExecutionMethod::Compiled => "call to a missing function env:missing_external"
};
assert_eq!(error.message, expected);
},
error => panic!("unexpected error: {:?}", error),
}
}
@@ -175,25 +166,18 @@ fn call_yet_another_not_existing_function(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
match call_in_wasm(
"test_calling_yet_another_missing_external",
&[],
wasm_method,
&mut ext,
) {
Ok(_) => panic!("was expected an `Err`"),
Err(e) => {
match wasm_method {
WasmExecutionMethod::Interpreted => assert_eq!(
&format!("{:?}", e),
"\"Trap: Trap { kind: Host(Other(\\\"Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.\\\")) }\""
),
match call_in_wasm("test_calling_yet_another_missing_external", &[], wasm_method, &mut ext)
.unwrap_err()
{
Error::AbortedDueToTrap(error) => {
let expected = match wasm_method {
WasmExecutionMethod::Interpreted => "Trap: Host(Other(\"Function `yet_another_missing_external` is only a stub. Calling a stub is not allowed.\"))",
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled => assert!(
format!("{:?}", e).contains("Wasm execution trapped: call to a missing function env:yet_another_missing_external")
),
}
}
WasmExecutionMethod::Compiled => "call to a missing function env:yet_another_missing_external"
};
assert_eq!(error.message, expected);
},
error => panic!("unexpected error: {:?}", error),
}
}
@@ -485,6 +469,7 @@ fn should_trap_when_heap_exhausted(wasm_method: WasmExecutionMethod) {
"test_exhaust_heap",
&[0],
)
.map_err(|e| e.to_string())
.unwrap_err();
assert!(err.contains("Allocator ran out of space"));
@@ -691,7 +676,7 @@ fn panic_in_spawned_instance_panics_on_joining_its_result(wasm_method: WasmExecu
let error_result =
call_in_wasm("test_panic_in_spawned", &[], wasm_method, &mut ext).unwrap_err();
assert!(error_result.contains("Spawned task"));
assert!(error_result.to_string().contains("Spawned task"));
}
test_wasm_execution!(memory_is_cleared_between_invocations);
@@ -789,3 +774,32 @@ fn take_i8(wasm_method: WasmExecutionMethod) {
call_in_wasm("test_take_i8", &(-66_i8).encode(), wasm_method, &mut ext).unwrap();
}
test_wasm_execution!(abort_on_panic);
fn abort_on_panic(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
match call_in_wasm("test_abort_on_panic", &[], wasm_method, &mut ext).unwrap_err() {
Error::AbortedDueToPanic(error) => assert_eq!(error.message, "test_abort_on_panic called"),
error => panic!("unexpected error: {:?}", error),
}
}
test_wasm_execution!(unreachable_intrinsic);
fn unreachable_intrinsic(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();
match call_in_wasm("test_unreachable_intrinsic", &[], wasm_method, &mut ext).unwrap_err() {
Error::AbortedDueToTrap(error) => {
let expected = match wasm_method {
WasmExecutionMethod::Interpreted => "Trap: Unreachable",
#[cfg(feature = "wasmtime")]
WasmExecutionMethod::Compiled => "wasm trap: wasm `unreachable` instruction executed",
};
assert_eq!(error.message, expected);
},
error => panic!("unexpected error: {:?}", error),
}
}