diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index cb6e147b1..6ce156c96 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -1,12 +1,13 @@ use crate::{ - relocation::{TrapData, TrapSink}, + relocation::{TrapData, TrapSink, TrapCode}, resolver::FuncResolver, trampoline::Trampolines, }; use libc::c_void; use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; use wasmer_runtime_core::{ - backend::RunnableModule, + backend::{RunnableModule, ExceptionCode}, + error::{InvokeError, RuntimeError}, module::ModuleInfo, typed_func::{Trampoline, Wasm}, types::{LocalFuncIndex, SigIndex}, @@ -26,10 +27,25 @@ pub use self::unix::*; pub use self::windows::*; thread_local! { - pub static TRAP_EARLY_DATA: Cell>> = Cell::new(None); + pub static TRAP_EARLY_DATA: Cell> = Cell::new(None); } -pub struct CallProtError(pub Box); +pub enum CallProtError { + UnknownTrap { + address: usize, + signal: &'static str, + }, + TrapCode { + code: ExceptionCode, + srcloc: u32, + }, + UnknownTrapCode { + trap_code: TrapCode, + srcloc: u32, + }, + EarlyTrap(RuntimeError), + Misc(Box), +} pub struct Caller { handler_data: HandlerData, @@ -63,7 +79,7 @@ impl RunnableModule for Caller { func: NonNull, args: *const u64, rets: *mut u64, - error_out: *mut Option>, + error_out: *mut Option, invoke_env: Option>, ) -> bool { let handler_data = &*invoke_env.unwrap().cast().as_ptr(); @@ -80,6 +96,9 @@ impl RunnableModule for Caller { match res { Err(err) => { + // probably makes the most sense to actually do a translation here to a + // a generic type defined in runtime-core + // TODO: figure out _this_ error return story *error_out = Some(err.0); false } @@ -101,7 +120,7 @@ impl RunnableModule for Caller { }) } - unsafe fn do_early_trap(&self, data: Box) -> ! { + unsafe fn do_early_trap(&self, data: RuntimeError) -> ! { TRAP_EARLY_DATA.with(|cell| cell.set(Some(data))); trigger_trap() } diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index 98571cbbf..8b517dac4 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -79,16 +79,16 @@ pub fn call_protected( *jmp_buf = prev_jmp_buf; if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { - Err(CallProtError(data)) + Err(CallProtError::EarlyTrap(data)) } else { let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); if let Some(TrapData { trapcode, - srcloc: _, + srcloc, }) = handler_data.lookup(inst_ptr) { - Err(CallProtError(Box::new(match Signal::from_c_int(signum) { + let code = match Signal::from_c_int(signum) { Ok(SIGILL) => match trapcode { TrapCode::StackOverflow => ExceptionCode::MemoryOutOfBounds, TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds, @@ -101,9 +101,10 @@ pub fn call_protected( TrapCode::BadConversionToInteger => ExceptionCode::IllegalArithmetic, TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable, _ => { - return Err(CallProtError(Box::new( - "unknown clif trap code".to_string(), - ))) + return Err(CallProtError::UnknownTrapCode { + trap_code: trapcode, + srcloc, + }) } }, Ok(SIGSEGV) | Ok(SIGBUS) => ExceptionCode::MemoryOutOfBounds, @@ -112,7 +113,11 @@ pub fn call_protected( "ExceptionCode::Unknown signal:{:?}", Signal::from_c_int(signum) ), - }))) + }; + Err(CallProtError::TrapCode { + srcloc, + code, + }) } else { let signal = match Signal::from_c_int(signum) { Ok(SIGFPE) => "floating-point exception", @@ -123,8 +128,10 @@ pub fn call_protected( _ => "unknown trapped signal", }; // When the trap-handler is fully implemented, this will return more information. - let s = format!("unknown trap at {:p} - {}", faulting_addr, signal); - Err(CallProtError(Box::new(s))) + Err(CallProtError::UnknownTrap { + address: faulting_addr as usize, + signal, + }) } } } else { diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index c6235475d..cbef1db3b 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -1,5 +1,5 @@ use crate::{ - error::CompileResult, + error::{CompileResult, RuntimeError}, module::ModuleInner, state::ModuleStateMap, typed_func::Wasm, @@ -282,7 +282,7 @@ pub trait RunnableModule: Send + Sync { fn get_trampoline(&self, info: &ModuleInfo, sig_index: SigIndex) -> Option; /// Trap an error. - unsafe fn do_early_trap(&self, data: Box) -> !; + unsafe fn do_early_trap(&self, data: RuntimeError) -> !; /// Returns the machine code associated with this module. fn get_code(&self) -> Option<&[u8]> { diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index a685a8238..c705e5213 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -1,6 +1,6 @@ //! The error module contains the data structures and helper functions used to implement errors that //! are produced and returned from the wasmer runtime core. -use crate::backend::ExceptionCode; +//use crate::backend::ExceptionCode; use crate::types::{FuncSig, GlobalDescriptor, MemoryDescriptor, TableDescriptor, Type}; use core::borrow::Borrow; use std::any::Any; @@ -173,6 +173,7 @@ impl std::fmt::Display for LinkError { impl std::error::Error for LinkError {} +/* /// This is the error type returned when calling /// a WebAssembly function. /// @@ -210,6 +211,68 @@ impl std::fmt::Debug for RuntimeError { impl std::error::Error for RuntimeError {} +*/ + +/// An `InternalError` is an error that happened inside of Wasmer and is a +/// catch-all for errors that would otherwise be returned as +/// `RuntimeError(Box::new())`. +/// +/// This type provides greater visibility into the kinds of things that may fail +/// and improves the ability of users to handle them, though these errors may be +/// extremely rare and impossible to handle. +#[derive(Debug)] +pub enum RuntimeError { + /// When an invoke returns an error (this is where exception codes come from?) + InvokeError(InvokeError), + /// A user triggered error value. + /// + /// An error returned from a host function. + User(Box) +} + +/// TODO: +#[derive(Debug)] +pub enum InvokeError { + /// not yet handled error cases, ideally we should be able to handle them all + Misc(Box), + /// Indicates an exceptional circumstance such as a bug that should be reported or + /// a hardware failure. + FailedWithNoError, +} + +impl std::error::Error for RuntimeError {} + +impl std::fmt::Display for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + // TODO: ideally improve the error type of invoke + RuntimeError::InvokeError(_) => write!(f, "Error when calling invoke"), + RuntimeError::User(user_error) => { + write!(f, "User supplied error: ")?; + if let Some(s) = user_error.downcast_ref::() { + write!(f, "\"{}\"", s) + } else if let Some(s) = user_error.downcast_ref::<&str>() { + write!(f, "\"{}\"", s) + } else if let Some(n) = user_error.downcast_ref::() { + write!(f, "{}", n) + } else { + write!(f, "unknown error type") + } + }, + } + } +} + +/* +impl From for RuntimeError { + fn from(other: InternalError) -> Self { + RuntimeError(Box::new(other)) + } +} + */ + + + /// This error type is produced by resolving a wasm function /// given its name. /// diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 48e59d4ec..4eb43d71f 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -5,7 +5,7 @@ use crate::{ backend::RunnableModule, backing::{ImportBacking, LocalBacking}, - error::{CallResult, ResolveError, ResolveResult, Result, RuntimeError}, + error::{CallResult, ResolveError, ResolveResult, Result, RuntimeError, InvokeError}, export::{Context, Export, ExportIter, Exportable, FuncPointer}, global::Global, import::{ImportObject, LikeNamespace}, @@ -584,10 +584,10 @@ pub(crate) fn call_func_with_index_inner( invoke_env, } = wasm; - let run_wasm = |result_space: *mut u64| unsafe { + let run_wasm = |result_space: *mut u64| -> CallResult<()> { let mut error_out = None; - let success = invoke( + let success = unsafe { invoke( trampoline, ctx_ptr, func_ptr, @@ -595,14 +595,15 @@ pub(crate) fn call_func_with_index_inner( result_space, &mut error_out, invoke_env, - ); + )}; if success { Ok(()) } else { - Err(error_out - .map(RuntimeError) - .unwrap_or_else(|| RuntimeError(Box::new("invoke(): Unknown error".to_string())))) + let error: RuntimeError = error_out + .map(RuntimeError::InvokeError) + .unwrap_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError)); + Err(error.into()) } }; diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index a6337bc58..1daa58319 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,7 +1,7 @@ //! The typed func module implements a way of representing a wasm function //! with the correct types from rust. Function calls using a typed func have a low overhead. use crate::{ - error::RuntimeError, + error::{RuntimeError, InvokeError}, export::{Context, Export, FuncPointer}, import::IsExport, types::{FuncSig, NativeWasmType, Type, WasmExternType}, @@ -37,7 +37,7 @@ pub type Invoke = unsafe extern "C" fn( func: NonNull, args: *const u64, rets: *mut u64, - error_out: *mut Option>, + error_out: *mut Option, extra: Option>, ) -> bool; @@ -340,7 +340,7 @@ impl<'a> DynamicFunc<'a> { Err(e) => { // At this point, there is an error that needs to be trapped. drop(args); // Release the Vec which will leak otherwise. - (&*vmctx.module).runnable_module.do_early_trap(e) + (&*vmctx.module).runnable_module.do_early_trap(RuntimeError::User(e)) } } } @@ -588,9 +588,7 @@ macro_rules! impl_traits { ) { Ok(Rets::from_ret_array(rets)) } else { - Err(error_out.map(RuntimeError).unwrap_or_else(|| { - RuntimeError(Box::new("invoke(): Unknown error".to_string())) - })) + Err(error_out.map_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError), RuntimeError::InvokeError)) } } } @@ -678,9 +676,10 @@ macro_rules! impl_traits { Ok(Ok(returns)) => return returns.into_c_struct(), Ok(Err(err)) => { let b: Box<_> = err.into(); - b as Box + RuntimeError::User(b as Box) }, - Err(err) => err, + // TODO(blocking): this line is wrong! + Err(err) => RuntimeError::User(err), }; // At this point, there is an error that needs to @@ -791,9 +790,10 @@ macro_rules! impl_traits { Ok(Ok(returns)) => return returns.into_c_struct(), Ok(Err(err)) => { let b: Box<_> = err.into(); - b as Box + RuntimeError::User(b as Box) }, - Err(err) => err, + // TODO(blocking): this line is wrong! + Err(err) => RuntimeError::User(err), }; // At this point, there is an error that needs to