mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-16 16:20:49 +00:00
Merge #1192
1192: Use `ExceptionCode` for error representation. r=losfair a=losfair Extends https://github.com/wasmerio/wasmer/pull/1129 to all backends. Co-authored-by: losfair <zhy20000919@hotmail.com>
This commit is contained in:
commit
eaa081e359
@ -2,6 +2,7 @@
|
||||
|
||||
## **[Unreleased]**
|
||||
|
||||
- [#1192](https://github.com/wasmerio/wasmer/pull/1192) Use `ExceptionCode` for error representation.
|
||||
- [#1180](https://github.com/wasmerio/wasmer/pull/1180) Fix compilation for target `x86_64-unknown-linux-musl`.
|
||||
- [#1170](https://github.com/wasmerio/wasmer/pull/1170) Improve the WasiFs builder API with convenience methods for overriding stdin, stdout, and stderr as well as a new sub-builder for controlling the permissions and properties of preopened directories. Also breaks that implementations of `WasiFile` must be `Send` -- please file an issue if this change causes you any issues.
|
||||
- [#1161](https://github.com/wasmerio/wasmer/pull/1161) Require imported functions to be `Send`. This is a breaking change that fixes a soundness issue in the API.
|
||||
|
@ -8,7 +8,7 @@ use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc};
|
||||
use wasmer_runtime_core::{
|
||||
backend::RunnableModule,
|
||||
module::ModuleInfo,
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
typed_func::{Trampoline, Wasm},
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm,
|
||||
};
|
||||
@ -29,10 +29,7 @@ thread_local! {
|
||||
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any + Send>>> = Cell::new(None);
|
||||
}
|
||||
|
||||
pub enum CallProtError {
|
||||
Trap(WasmTrapInfo),
|
||||
Error(Box<dyn Any + Send>),
|
||||
}
|
||||
pub struct CallProtError(pub Box<dyn Any + Send>);
|
||||
|
||||
pub struct Caller {
|
||||
handler_data: HandlerData,
|
||||
@ -66,8 +63,7 @@ impl RunnableModule for Caller {
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
trap_info: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any + Send>>,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> bool {
|
||||
let handler_data = &*invoke_env.unwrap().cast().as_ptr();
|
||||
@ -84,10 +80,7 @@ impl RunnableModule for Caller {
|
||||
|
||||
match res {
|
||||
Err(err) => {
|
||||
match err {
|
||||
CallProtError::Trap(info) => *trap_info = info,
|
||||
CallProtError::Error(data) => *user_error = Some(data),
|
||||
}
|
||||
*error_out = Some(err.0);
|
||||
false
|
||||
}
|
||||
Ok(()) => true,
|
||||
|
@ -18,7 +18,7 @@ use nix::sys::signal::{
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::ptr;
|
||||
use std::sync::Once;
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
use wasmer_runtime_core::backend::ExceptionCode;
|
||||
|
||||
extern "C" fn signal_trap_handler(
|
||||
signum: ::nix::libc::c_int,
|
||||
@ -79,7 +79,7 @@ pub fn call_protected<T>(
|
||||
*jmp_buf = prev_jmp_buf;
|
||||
|
||||
if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
|
||||
Err(CallProtError::Error(data))
|
||||
Err(CallProtError(data))
|
||||
} else {
|
||||
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
|
||||
|
||||
@ -88,21 +88,31 @@ pub fn call_protected<T>(
|
||||
srcloc: _,
|
||||
}) = handler_data.lookup(inst_ptr)
|
||||
{
|
||||
Err(CallProtError::Trap(match Signal::from_c_int(signum) {
|
||||
Err(CallProtError(Box::new(match Signal::from_c_int(signum) {
|
||||
Ok(SIGILL) => match trapcode {
|
||||
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
|
||||
TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB,
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
TrapCode::StackOverflow => ExceptionCode::MemoryOutOfBounds,
|
||||
TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => ExceptionCode::CallIndirectOOB,
|
||||
TrapCode::OutOfBounds => ExceptionCode::MemoryOutOfBounds,
|
||||
TrapCode::IndirectCallToNull => ExceptionCode::CallIndirectOOB,
|
||||
TrapCode::BadSignature => ExceptionCode::IncorrectCallIndirectSignature,
|
||||
TrapCode::IntegerOverflow => ExceptionCode::IllegalArithmetic,
|
||||
TrapCode::IntegerDivisionByZero => ExceptionCode::IllegalArithmetic,
|
||||
TrapCode::BadConversionToInteger => ExceptionCode::IllegalArithmetic,
|
||||
TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable,
|
||||
_ => {
|
||||
return Err(CallProtError(Box::new(
|
||||
"unknown clif trap code".to_string(),
|
||||
)))
|
||||
}
|
||||
},
|
||||
Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds,
|
||||
Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic,
|
||||
Ok(SIGSEGV) | Ok(SIGBUS) => ExceptionCode::MemoryOutOfBounds,
|
||||
Ok(SIGFPE) => ExceptionCode::IllegalArithmetic,
|
||||
_ => unimplemented!(
|
||||
"WasmTrapInfo::Unknown signal:{:?}",
|
||||
"ExceptionCode::Unknown signal:{:?}",
|
||||
Signal::from_c_int(signum)
|
||||
),
|
||||
}))
|
||||
})))
|
||||
} else {
|
||||
let signal = match Signal::from_c_int(signum) {
|
||||
Ok(SIGFPE) => "floating-point exception",
|
||||
@ -114,7 +124,7 @@ pub fn call_protected<T>(
|
||||
};
|
||||
// When the trap-handler is fully implemented, this will return more information.
|
||||
let s = format!("unknown trap at {:p} - {}", faulting_addr, signal);
|
||||
Err(CallProtError::Error(Box::new(s)))
|
||||
Err(CallProtError(Box::new(s)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -8,7 +8,8 @@ use std::{
|
||||
ptr::{self, NonNull},
|
||||
};
|
||||
use wasmer_runtime_core::{
|
||||
typed_func::{Trampoline, WasmTrapInfo},
|
||||
backend::ExceptionCode,
|
||||
typed_func::Trampoline,
|
||||
vm::{Ctx, Func},
|
||||
};
|
||||
use wasmer_win_exception_handler::CallProtectedData;
|
||||
@ -62,22 +63,26 @@ pub fn call_protected(
|
||||
srcloc: _,
|
||||
}) = handler_data.lookup(instruction_pointer as _)
|
||||
{
|
||||
Err(CallProtError::Trap(match code as DWORD {
|
||||
EXCEPTION_ACCESS_VIOLATION => WasmTrapInfo::MemoryOutOfBounds,
|
||||
Err(CallProtError(Box::new(match code as DWORD {
|
||||
EXCEPTION_ACCESS_VIOLATION => ExceptionCode::MemoryOutOfBounds,
|
||||
EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode {
|
||||
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
|
||||
TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB,
|
||||
TrapCode::UnreachableCodeReached => WasmTrapInfo::Unreachable,
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
TrapCode::BadSignature => ExceptionCode::IncorrectCallIndirectSignature,
|
||||
TrapCode::IndirectCallToNull => ExceptionCode::CallIndirectOOB,
|
||||
TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds,
|
||||
TrapCode::TableOutOfBounds => ExceptionCode::CallIndirectOOB,
|
||||
TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable,
|
||||
_ => return Err(CallProtError(Box::new("unknown trap code".to_string()))),
|
||||
},
|
||||
EXCEPTION_STACK_OVERFLOW => WasmTrapInfo::Unknown,
|
||||
EXCEPTION_STACK_OVERFLOW => ExceptionCode::MemoryOutOfBounds,
|
||||
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => {
|
||||
WasmTrapInfo::IllegalArithmetic
|
||||
ExceptionCode::IllegalArithmetic
|
||||
}
|
||||
_ => WasmTrapInfo::Unknown,
|
||||
}))
|
||||
_ => {
|
||||
return Err(CallProtError(Box::new(
|
||||
"unknown exception code".to_string(),
|
||||
)))
|
||||
}
|
||||
})))
|
||||
} else {
|
||||
let signal = match code as DWORD {
|
||||
EXCEPTION_FLT_DENORMAL_OPERAND
|
||||
@ -110,7 +115,7 @@ pub fn call_protected(
|
||||
exception_address, code, signal,
|
||||
);
|
||||
|
||||
Err(CallProtError::Error(Box::new(s)))
|
||||
Err(CallProtError(Box::new(s)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ enum WasmTrapType {
|
||||
MemoryOutOfBounds = 2,
|
||||
CallIndirectOOB = 3,
|
||||
IllegalArithmetic = 4,
|
||||
MisalignedAtomicAccess = 5,
|
||||
Unknown,
|
||||
};
|
||||
|
||||
@ -285,7 +286,7 @@ void module_delete(WasmModule *module) { delete module; }
|
||||
unsafe_unwind(new BreakpointException(callback));
|
||||
}
|
||||
|
||||
bool invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
|
||||
bool cxx_invoke_trampoline(trampoline_t trampoline, void *ctx, void *func,
|
||||
void *params, void *results, WasmTrapType *trap_out,
|
||||
box_any_t *user_error, void *invoke_env) noexcept {
|
||||
try {
|
||||
|
@ -24,13 +24,13 @@ use std::{
|
||||
use wasmer_runtime_core::{
|
||||
backend::{
|
||||
sys::{Memory, Protect},
|
||||
CacheGen, RunnableModule,
|
||||
CacheGen, ExceptionCode, RunnableModule,
|
||||
},
|
||||
cache::Error as CacheError,
|
||||
module::ModuleInfo,
|
||||
state::ModuleStateMap,
|
||||
structures::TypedIndex,
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
typed_func::{Trampoline, Wasm},
|
||||
types::{LocalFuncIndex, SigIndex},
|
||||
vm, vmcalls,
|
||||
};
|
||||
@ -59,18 +59,55 @@ extern "C" {
|
||||
fn throw_any(data: *mut dyn Any) -> !;
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
fn invoke_trampoline(
|
||||
fn cxx_invoke_trampoline(
|
||||
trampoline: Trampoline,
|
||||
vmctx_ptr: *mut vm::Ctx,
|
||||
func_ptr: NonNull<vm::Func>,
|
||||
params: *const u64,
|
||||
results: *mut u64,
|
||||
trap_out: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any + Send>>,
|
||||
trap_out: *mut i32,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
/// `invoke_trampoline` is a wrapper around `cxx_invoke_trampoline`, for fixing up the obsoleted
|
||||
/// `trap_out` in the C++ part.
|
||||
unsafe extern "C" fn invoke_trampoline(
|
||||
trampoline: Trampoline,
|
||||
vmctx_ptr: *mut vm::Ctx,
|
||||
func_ptr: NonNull<vm::Func>,
|
||||
params: *const u64,
|
||||
results: *mut u64,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> bool {
|
||||
let mut trap_out: i32 = -1;
|
||||
let ret = cxx_invoke_trampoline(
|
||||
trampoline,
|
||||
vmctx_ptr,
|
||||
func_ptr,
|
||||
params,
|
||||
results,
|
||||
&mut trap_out,
|
||||
error_out,
|
||||
invoke_env,
|
||||
);
|
||||
// Translate trap code if an error occurred.
|
||||
if !ret && (*error_out).is_none() && trap_out != -1 {
|
||||
*error_out = Some(Box::new(match trap_out {
|
||||
0 => ExceptionCode::Unreachable,
|
||||
1 => ExceptionCode::IncorrectCallIndirectSignature,
|
||||
2 => ExceptionCode::MemoryOutOfBounds,
|
||||
3 => ExceptionCode::CallIndirectOOB,
|
||||
4 => ExceptionCode::IllegalArithmetic,
|
||||
5 => ExceptionCode::MisalignedAtomicAccess,
|
||||
_ => return ret,
|
||||
}));
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
static SIGNAL_HANDLER_INSTALLED: Once = Once::new();
|
||||
|
||||
fn get_callbacks() -> Callbacks {
|
||||
|
@ -165,12 +165,10 @@ mod tests {
|
||||
}
|
||||
|
||||
let err = result.unwrap_err();
|
||||
match err {
|
||||
RuntimeError::Error { data } => {
|
||||
assert!(data.downcast_ref::<ExecutionLimitExceededError>().is_some());
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
assert!(err
|
||||
.0
|
||||
.downcast_ref::<ExecutionLimitExceededError>()
|
||||
.is_some());
|
||||
|
||||
// verify it used the correct number of points
|
||||
assert_eq!(get_points_used(&instance), 109); // Used points will be slightly more than `limit` because of the way we do gas checking.
|
||||
|
@ -16,12 +16,7 @@ macro_rules! call_and_assert {
|
||||
expected_value,
|
||||
concat!("Expected right when calling `", stringify!($function), "`.")
|
||||
),
|
||||
(
|
||||
Err(RuntimeError::Error { data }),
|
||||
Err(RuntimeError::Error {
|
||||
data: expected_data,
|
||||
}),
|
||||
) => {
|
||||
(Err(RuntimeError(data)), Err(RuntimeError(expected_data))) => {
|
||||
if let (Some(data), Some(expected_data)) = (
|
||||
data.downcast_ref::<&str>(),
|
||||
expected_data.downcast_ref::<&str>(),
|
||||
@ -260,35 +255,25 @@ test!(
|
||||
test!(
|
||||
test_fn_trap,
|
||||
function_fn_trap,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("foo {}", 2))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("foo {}", 2))))
|
||||
);
|
||||
test!(
|
||||
test_closure_trap,
|
||||
function_closure_trap,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("bar {}", 2))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("bar {}", 2))))
|
||||
);
|
||||
test!(
|
||||
test_fn_trap_with_vmctx,
|
||||
function_fn_trap_with_vmctx,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("baz {}", 2 + SHIFT))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT))))
|
||||
);
|
||||
test!(
|
||||
test_closure_trap_with_vmctx,
|
||||
function_closure_trap_with_vmctx,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("qux {}", 2 + SHIFT))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT))))
|
||||
);
|
||||
test!(
|
||||
test_closure_trap_with_vmctx_and_env,
|
||||
function_closure_trap_with_vmctx_and_env,
|
||||
Err(RuntimeError::Error {
|
||||
data: Box::new(format!("! {}", 2 + shift + SHIFT))
|
||||
})
|
||||
Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT))))
|
||||
);
|
||||
|
@ -13,6 +13,7 @@ use crate::{
|
||||
module::ModuleInfo,
|
||||
sys::Memory,
|
||||
};
|
||||
use std::fmt;
|
||||
use std::{any::Any, ptr::NonNull};
|
||||
|
||||
use std::collections::HashMap;
|
||||
@ -158,13 +159,36 @@ impl ExceptionTable {
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum ExceptionCode {
|
||||
/// An `unreachable` opcode was executed.
|
||||
Unreachable,
|
||||
|
||||
Unreachable = 0,
|
||||
/// Call indirect incorrect signature trap.
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
/// Memory out of bounds trap.
|
||||
MemoryOutOfBounds = 2,
|
||||
/// Call indirect out of bounds trap.
|
||||
CallIndirectOOB = 3,
|
||||
/// An arithmetic exception, e.g. divided by zero.
|
||||
Arithmetic,
|
||||
IllegalArithmetic = 4,
|
||||
/// Misaligned atomic access trap.
|
||||
MisalignedAtomicAccess = 5,
|
||||
}
|
||||
|
||||
/// Memory access exception, e.g. misaligned/out-of-bound read/write.
|
||||
Memory,
|
||||
impl fmt::Display for ExceptionCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
ExceptionCode::Unreachable => "unreachable",
|
||||
ExceptionCode::IncorrectCallIndirectSignature => {
|
||||
"incorrect `call_indirect` signature"
|
||||
}
|
||||
ExceptionCode::MemoryOutOfBounds => "memory out-of-bounds access",
|
||||
ExceptionCode::CallIndirectOOB => "`call_indirect` out-of-bounds",
|
||||
ExceptionCode::IllegalArithmetic => "illegal arithmetic operation",
|
||||
ExceptionCode::MisalignedAtomicAccess => "misaligned atomic access",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Compiler {
|
||||
|
@ -179,18 +179,7 @@ impl std::error::Error for LinkError {}
|
||||
/// The main way to do this is `Instance.call`.
|
||||
///
|
||||
/// Comparing two `RuntimeError`s always evaluates to false.
|
||||
pub enum RuntimeError {
|
||||
/// Trap.
|
||||
Trap {
|
||||
/// Trap message.
|
||||
msg: Box<str>,
|
||||
},
|
||||
/// Error.
|
||||
Error {
|
||||
/// Error data.
|
||||
data: Box<dyn Any + Send>,
|
||||
},
|
||||
}
|
||||
pub struct RuntimeError(pub Box<dyn Any + Send>);
|
||||
|
||||
impl PartialEq for RuntimeError {
|
||||
fn eq(&self, _other: &RuntimeError) -> bool {
|
||||
@ -200,21 +189,15 @@ impl PartialEq for RuntimeError {
|
||||
|
||||
impl std::fmt::Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
RuntimeError::Trap { ref msg } => {
|
||||
write!(f, "WebAssembly trap occurred during runtime: {}", msg)
|
||||
}
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(s) = data.downcast_ref::<String>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(s) = data.downcast_ref::<&str>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(exc_code) = data.downcast_ref::<ExceptionCode>() {
|
||||
write!(f, "Caught exception of type \"{:?}\".", exc_code)
|
||||
} else {
|
||||
write!(f, "unknown error")
|
||||
}
|
||||
}
|
||||
let data = &*self.0;
|
||||
if let Some(s) = data.downcast_ref::<String>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(s) = data.downcast_ref::<&str>() {
|
||||
write!(f, "\"{}\"", s)
|
||||
} else if let Some(exc_code) = data.downcast_ref::<ExceptionCode>() {
|
||||
write!(f, "Caught exception of type \"{:?}\".", exc_code)
|
||||
} else {
|
||||
write!(f, "unknown error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
sig_registry::SigRegistry,
|
||||
structures::TypedIndex,
|
||||
table::Table,
|
||||
typed_func::{Func, Wasm, WasmTrapInfo, WasmTypeList},
|
||||
typed_func::{Func, Wasm, WasmTypeList},
|
||||
types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Type, Value},
|
||||
vm::{self, InternalField},
|
||||
};
|
||||
@ -673,8 +673,7 @@ pub(crate) fn call_func_with_index_inner(
|
||||
} = wasm;
|
||||
|
||||
let run_wasm = |result_space: *mut u64| unsafe {
|
||||
let mut trap_info = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
let mut error_out = None;
|
||||
|
||||
let success = invoke(
|
||||
trampoline,
|
||||
@ -682,21 +681,16 @@ pub(crate) fn call_func_with_index_inner(
|
||||
func_ptr,
|
||||
raw_args.as_ptr(),
|
||||
result_space,
|
||||
&mut trap_info,
|
||||
&mut user_error,
|
||||
&mut error_out,
|
||||
invoke_env,
|
||||
);
|
||||
|
||||
if success {
|
||||
Ok(())
|
||||
} else {
|
||||
if let Some(data) = user_error {
|
||||
Err(RuntimeError::Error { data })
|
||||
} else {
|
||||
Err(RuntimeError::Trap {
|
||||
msg: trap_info.to_string().into(),
|
||||
})
|
||||
}
|
||||
Err(error_out
|
||||
.map(RuntimeError)
|
||||
.unwrap_or_else(|| RuntimeError(Box::new("invoke(): Unknown error".to_string()))))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,52 +11,12 @@ use std::{
|
||||
any::Any,
|
||||
convert::Infallible,
|
||||
ffi::c_void,
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
mem, panic,
|
||||
ptr::{self, NonNull},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Wasm trap info.
|
||||
#[repr(C)]
|
||||
pub enum WasmTrapInfo {
|
||||
/// Unreachable trap.
|
||||
Unreachable = 0,
|
||||
/// Call indirect incorrect signature trap.
|
||||
IncorrectCallIndirectSignature = 1,
|
||||
/// Memory out of bounds trap.
|
||||
MemoryOutOfBounds = 2,
|
||||
/// Call indirect out of bounds trap.
|
||||
CallIndirectOOB = 3,
|
||||
/// Illegal arithmetic trap.
|
||||
IllegalArithmetic = 4,
|
||||
/// Misaligned atomic access trap.
|
||||
MisalignedAtomicAccess = 5,
|
||||
/// Unknown trap.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl fmt::Display for WasmTrapInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
WasmTrapInfo::Unreachable => "unreachable",
|
||||
WasmTrapInfo::IncorrectCallIndirectSignature => {
|
||||
"incorrect `call_indirect` signature"
|
||||
}
|
||||
WasmTrapInfo::MemoryOutOfBounds => "memory out-of-bounds access",
|
||||
WasmTrapInfo::CallIndirectOOB => "`call_indirect` out-of-bounds",
|
||||
WasmTrapInfo::IllegalArithmetic => "illegal arithmetic operation",
|
||||
WasmTrapInfo::MisalignedAtomicAccess => "misaligned atomic access",
|
||||
WasmTrapInfo::Unknown => "unknown",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// This is just an empty trait to constrict that types that
|
||||
/// can be put into the third/fourth (depending if you include lifetimes)
|
||||
/// of the `Func` struct.
|
||||
@ -77,8 +37,7 @@ pub type Invoke = unsafe extern "C" fn(
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
trap_info: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any + Send>>,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
extra: Option<NonNull<c_void>>,
|
||||
) -> bool;
|
||||
|
||||
@ -401,8 +360,7 @@ macro_rules! impl_traits {
|
||||
let ( $( $x ),* ) = self;
|
||||
let args = [ $( $x.to_native().to_binary()),* ];
|
||||
let mut rets = Rets::empty_ret_array();
|
||||
let mut trap = WasmTrapInfo::Unknown;
|
||||
let mut user_error = None;
|
||||
let mut error_out = None;
|
||||
|
||||
if (wasm.invoke)(
|
||||
wasm.trampoline,
|
||||
@ -410,17 +368,14 @@ macro_rules! impl_traits {
|
||||
f,
|
||||
args.as_ptr(),
|
||||
rets.as_mut().as_mut_ptr(),
|
||||
&mut trap,
|
||||
&mut user_error,
|
||||
&mut error_out,
|
||||
wasm.invoke_env
|
||||
) {
|
||||
Ok(Rets::from_ret_array(rets))
|
||||
} else {
|
||||
if let Some(data) = user_error {
|
||||
Err(RuntimeError::Error { data })
|
||||
} else {
|
||||
Err(RuntimeError::Trap { msg: trap.to_string().into() })
|
||||
}
|
||||
Err(error_out.map(RuntimeError).unwrap_or_else(|| {
|
||||
RuntimeError(Box::new("invoke(): Unknown error".to_string()))
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,8 +70,8 @@ fn main() -> Result<(), error::Error> {
|
||||
|
||||
println!("result: {:?}", result);
|
||||
|
||||
if let Err(RuntimeError::Error { data }) = result {
|
||||
if let Ok(exit_code) = data.downcast::<ExitCode>() {
|
||||
if let Err(e) = result {
|
||||
if let Ok(exit_code) = e.0.downcast::<ExitCode>() {
|
||||
println!("exit code: {:?}", exit_code);
|
||||
}
|
||||
}
|
||||
|
@ -40,10 +40,10 @@ fn error_propagation() {
|
||||
|
||||
let result = foo.call();
|
||||
|
||||
if let Err(RuntimeError::Error { data }) = result {
|
||||
let exit_code = data.downcast::<ExitCode>().unwrap();
|
||||
if let Err(RuntimeError(e)) = result {
|
||||
let exit_code = e.downcast::<ExitCode>().unwrap();
|
||||
assert_eq!(exit_code.code, 42);
|
||||
} else {
|
||||
panic!("didn't return RuntimeError::Error")
|
||||
panic!("didn't return RuntimeError")
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ use wasmer_runtime_core::{
|
||||
ModuleStateMap, OffsetInfo, SuspendOffset, WasmAbstractValue,
|
||||
},
|
||||
structures::{Map, TypedIndex},
|
||||
typed_func::{Trampoline, Wasm, WasmTrapInfo},
|
||||
typed_func::{Trampoline, Wasm},
|
||||
types::{
|
||||
FuncIndex, FuncSig, GlobalIndex, LocalFuncIndex, LocalOrImport, MemoryIndex, SigIndex,
|
||||
TableIndex, Type,
|
||||
@ -375,8 +375,7 @@ impl RunnableModule for X64ExecutionContext {
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
trap_info: *mut WasmTrapInfo,
|
||||
user_error: *mut Option<Box<dyn Any + Send>>,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
num_params_plus_one: Option<NonNull<c_void>>,
|
||||
) -> bool {
|
||||
let rm: &Box<dyn RunnableModule> = &(&*(*ctx).module).runnable_module;
|
||||
@ -520,10 +519,7 @@ impl RunnableModule for X64ExecutionContext {
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
match err {
|
||||
protect_unix::CallProtError::Trap(info) => *trap_info = info,
|
||||
protect_unix::CallProtError::Error(data) => *user_error = Some(data),
|
||||
}
|
||||
*error_out = Some(err.0);
|
||||
false
|
||||
}
|
||||
};
|
||||
@ -1010,14 +1006,14 @@ impl X64FunctionCode {
|
||||
Self::mark_trappable(a, m, fsm, control_stack);
|
||||
etable
|
||||
.offset_to_code
|
||||
.insert(a.get_offset().0, ExceptionCode::Arithmetic);
|
||||
.insert(a.get_offset().0, ExceptionCode::IllegalArithmetic);
|
||||
op(a, sz, Location::GPR(GPR::RCX));
|
||||
}
|
||||
_ => {
|
||||
Self::mark_trappable(a, m, fsm, control_stack);
|
||||
etable
|
||||
.offset_to_code
|
||||
.insert(a.get_offset().0, ExceptionCode::Arithmetic);
|
||||
.insert(a.get_offset().0, ExceptionCode::IllegalArithmetic);
|
||||
op(a, sz, loc);
|
||||
}
|
||||
}
|
||||
@ -2003,9 +1999,12 @@ impl X64FunctionCode {
|
||||
a.emit_add(Size::S64, Location::GPR(tmp_base), Location::GPR(tmp_addr));
|
||||
a.emit_cmp(Size::S64, Location::GPR(tmp_bound), Location::GPR(tmp_addr));
|
||||
|
||||
Self::mark_range_with_exception_code(a, etable, ExceptionCode::Memory, |a| {
|
||||
a.emit_conditional_trap(Condition::Above)
|
||||
});
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
etable,
|
||||
ExceptionCode::MemoryOutOfBounds,
|
||||
|a| a.emit_conditional_trap(Condition::Above),
|
||||
);
|
||||
|
||||
m.release_temp_gpr(tmp_bound);
|
||||
}
|
||||
@ -2045,13 +2044,16 @@ impl X64FunctionCode {
|
||||
Location::Imm32(align - 1),
|
||||
Location::GPR(tmp_aligncheck),
|
||||
);
|
||||
Self::mark_range_with_exception_code(a, etable, ExceptionCode::Memory, |a| {
|
||||
a.emit_conditional_trap(Condition::NotEqual)
|
||||
});
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
etable,
|
||||
ExceptionCode::MemoryOutOfBounds,
|
||||
|a| a.emit_conditional_trap(Condition::NotEqual),
|
||||
);
|
||||
m.release_temp_gpr(tmp_aligncheck);
|
||||
}
|
||||
|
||||
Self::mark_range_with_exception_code(a, etable, ExceptionCode::Memory, |a| {
|
||||
Self::mark_range_with_exception_code(a, etable, ExceptionCode::MemoryOutOfBounds, |a| {
|
||||
cb(a, m, tmp_addr)
|
||||
})?;
|
||||
|
||||
@ -2186,7 +2188,7 @@ impl X64FunctionCode {
|
||||
a.emit_label(trap);
|
||||
etable
|
||||
.offset_to_code
|
||||
.insert(a.get_offset().0, ExceptionCode::Arithmetic);
|
||||
.insert(a.get_offset().0, ExceptionCode::IllegalArithmetic);
|
||||
a.emit_ud2();
|
||||
a.emit_label(end);
|
||||
}
|
||||
@ -2314,7 +2316,7 @@ impl X64FunctionCode {
|
||||
a.emit_label(trap);
|
||||
etable
|
||||
.offset_to_code
|
||||
.insert(a.get_offset().0, ExceptionCode::Arithmetic);
|
||||
.insert(a.get_offset().0, ExceptionCode::IllegalArithmetic);
|
||||
a.emit_ud2();
|
||||
a.emit_label(end);
|
||||
}
|
||||
@ -2442,7 +2444,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
self.exception_table.as_mut().unwrap(),
|
||||
ExceptionCode::Memory,
|
||||
ExceptionCode::MemoryOutOfBounds,
|
||||
|a| a.emit_conditional_trap(Condition::Below),
|
||||
);
|
||||
}
|
||||
@ -6311,7 +6313,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
self.exception_table.as_mut().unwrap(),
|
||||
ExceptionCode::Memory,
|
||||
ExceptionCode::CallIndirectOOB,
|
||||
|a| a.emit_conditional_trap(Condition::BelowEqual),
|
||||
);
|
||||
a.emit_mov(Size::S64, func_index, Location::GPR(table_count));
|
||||
@ -6342,7 +6344,7 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
|
||||
Self::mark_range_with_exception_code(
|
||||
a,
|
||||
self.exception_table.as_mut().unwrap(),
|
||||
ExceptionCode::Memory,
|
||||
ExceptionCode::IncorrectCallIndirectSignature,
|
||||
|a| a.emit_conditional_trap(Condition::NotEqual),
|
||||
);
|
||||
|
||||
|
@ -13,7 +13,6 @@ use std::any::Any;
|
||||
use std::cell::Cell;
|
||||
use wasmer_runtime_core::codegen::BreakpointMap;
|
||||
use wasmer_runtime_core::fault::{begin_unsafe_unwind, catch_unsafe_unwind, ensure_sighandler};
|
||||
use wasmer_runtime_core::typed_func::WasmTrapInfo;
|
||||
|
||||
thread_local! {
|
||||
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any + Send>>> = Cell::new(None);
|
||||
@ -23,10 +22,7 @@ pub unsafe fn trigger_trap() -> ! {
|
||||
begin_unsafe_unwind(Box::new(()));
|
||||
}
|
||||
|
||||
pub enum CallProtError {
|
||||
Trap(WasmTrapInfo),
|
||||
Error(Box<dyn Any + Send>),
|
||||
}
|
||||
pub struct CallProtError(pub Box<dyn Any + Send>);
|
||||
|
||||
pub fn call_protected<T>(
|
||||
f: impl FnOnce() -> T,
|
||||
@ -37,13 +33,13 @@ pub fn call_protected<T>(
|
||||
let ret = catch_unsafe_unwind(|| f(), breakpoints);
|
||||
match ret {
|
||||
Ok(x) => Ok(x),
|
||||
Err(e) => {
|
||||
Err(e) => Err(CallProtError(
|
||||
if let Some(data) = TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
|
||||
Err(CallProtError::Error(data))
|
||||
data
|
||||
} else {
|
||||
Err(CallProtError::Error(e))
|
||||
}
|
||||
}
|
||||
e
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ mod tests {
|
||||
use wabt::wat2wasm;
|
||||
use wasmer_runtime::{
|
||||
error::{CallError, RuntimeError},
|
||||
ImportObject,
|
||||
ExceptionCode, ImportObject,
|
||||
};
|
||||
|
||||
// The semantics of stack overflow are documented at:
|
||||
@ -29,9 +29,9 @@ mod tests {
|
||||
|
||||
match result {
|
||||
Err(err) => match err {
|
||||
CallError::Runtime(RuntimeError::Trap { msg }) => {
|
||||
assert!(!msg.contains("segmentation violation"));
|
||||
assert!(!msg.contains("bus error"));
|
||||
CallError::Runtime(RuntimeError(e)) => {
|
||||
e.downcast::<ExceptionCode>()
|
||||
.expect("expecting exception code");
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
|
@ -674,54 +674,41 @@ mod tests {
|
||||
let call_result = maybe_call_result.unwrap();
|
||||
use wasmer_runtime::error::{CallError, RuntimeError};
|
||||
match call_result {
|
||||
Err(e) => {
|
||||
match e {
|
||||
CallError::Resolve(_) => {
|
||||
Err(e) => match e {
|
||||
CallError::Resolve(_) => {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
file: filename.to_string(),
|
||||
line,
|
||||
kind: format!("{}", "AssertTrap"),
|
||||
message: format!("expected trap, got {:?}", e),
|
||||
},
|
||||
&test_key,
|
||||
excludes,
|
||||
line,
|
||||
);
|
||||
}
|
||||
CallError::Runtime(RuntimeError(e)) => {
|
||||
use wasmer_runtime::ExceptionCode;
|
||||
if let Some(_) = e.downcast_ref::<ExceptionCode>() {
|
||||
test_report.count_passed();
|
||||
} else {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
file: filename.to_string(),
|
||||
line,
|
||||
kind: format!("{}", "AssertTrap"),
|
||||
message: format!("expected trap, got {:?}", e),
|
||||
message: format!(
|
||||
"expected trap, got RuntimeError"
|
||||
),
|
||||
},
|
||||
&test_key,
|
||||
excludes,
|
||||
line,
|
||||
);
|
||||
}
|
||||
CallError::Runtime(r) => {
|
||||
match r {
|
||||
RuntimeError::Trap { .. } => {
|
||||
// TODO assert message?
|
||||
test_report.count_passed()
|
||||
}
|
||||
RuntimeError::Error { ref data } => {
|
||||
use wasmer_runtime::ExceptionCode;
|
||||
if let Some(_) =
|
||||
data.downcast_ref::<ExceptionCode>()
|
||||
{
|
||||
test_report.count_passed();
|
||||
} else {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
file: filename.to_string(),
|
||||
line,
|
||||
kind: format!("{}", "AssertTrap"),
|
||||
message: format!(
|
||||
"expected trap, got Runtime:Error {:?}",
|
||||
r
|
||||
),
|
||||
},
|
||||
&test_key,
|
||||
excludes,
|
||||
line,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Ok(values) => {
|
||||
test_report.add_failure(
|
||||
SpecFailure {
|
||||
|
@ -43,7 +43,7 @@ pub use self::utils::{get_wasi_version, is_wasi_module, WasiVersion};
|
||||
|
||||
use wasmer_runtime_core::{func, import::ImportObject, imports};
|
||||
|
||||
/// This is returned in the Box<dyn Any> RuntimeError::Error variant.
|
||||
/// This is returned in `RuntimeError`.
|
||||
/// Use `downcast` or `downcast_ref` to retrieve the `ExitCode`.
|
||||
pub struct ExitCode {
|
||||
pub code: syscalls::types::__wasi_exitcode_t,
|
||||
|
@ -482,7 +482,6 @@ fn execute_wasi(
|
||||
|
||||
#[cfg(not(feature = "managed"))]
|
||||
{
|
||||
use wasmer_runtime::error::RuntimeError;
|
||||
let result;
|
||||
|
||||
#[cfg(unix)]
|
||||
@ -521,13 +520,8 @@ fn execute_wasi(
|
||||
}
|
||||
|
||||
if let Err(ref err) = result {
|
||||
match err {
|
||||
RuntimeError::Trap { msg } => return Err(format!("wasm trap occured: {}", msg)),
|
||||
RuntimeError::Error { data } => {
|
||||
if let Some(error_code) = data.downcast_ref::<wasmer_wasi::ExitCode>() {
|
||||
std::process::exit(error_code.code as i32)
|
||||
}
|
||||
}
|
||||
if let Some(error_code) = err.0.downcast_ref::<wasmer_wasi::ExitCode>() {
|
||||
std::process::exit(error_code.code as i32)
|
||||
}
|
||||
return Err(format!("error: {:?}", err));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user