diff --git a/lib/clif-backend/src/module.rs b/lib/clif-backend/src/module.rs index 9654af7cc..c48072ac8 100644 --- a/lib/clif-backend/src/module.rs +++ b/lib/clif-backend/src/module.rs @@ -16,7 +16,7 @@ use wasmer_runtime_core::{ cache::{Cache, Error as CacheError}, }; use wasmer_runtime_core::{ - backend::{Backend, FuncResolver, ProtectedCaller, Token}, + backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper}, error::{CompileResult, RuntimeResult}, module::{ModuleInfo, ModuleInner, StringTable}, structures::{Map, TypedIndex}, @@ -51,6 +51,10 @@ impl ProtectedCaller for Placeholder { ) -> RuntimeResult> { Ok(vec![]) } + + fn get_early_trapper(&self) -> Box { + unimplemented!() + } } /// This contains all of the items in a `ModuleInner` except the `func_resolver`. diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index dbcd0c6b8..eab9e62e7 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -2,9 +2,9 @@ use crate::relocation::{TrapData, TrapSink}; use crate::trampoline::Trampolines; use hashbrown::HashSet; use libc::c_void; -use std::sync::Arc; +use std::{cell::Cell, sync::Arc}; use wasmer_runtime_core::{ - backend::{ProtectedCaller, Token}, + backend::{ProtectedCaller, Token, UserTrapper}, error::RuntimeResult, export::Context, module::{ExportIndex, ModuleInfo, ModuleInner}, @@ -24,6 +24,19 @@ pub use self::unix::*; #[cfg(windows)] pub use self::windows::*; +thread_local! { + pub static TRAP_EARLY_DATA: Cell> = Cell::new(None); +} + +pub struct Trapper; + +impl UserTrapper for Trapper { + unsafe fn do_early_trap(&self, msg: String) -> ! { + TRAP_EARLY_DATA.with(|cell| cell.set(Some(msg))); + trigger_trap() + } +} + pub struct Caller { func_export_set: HashSet, handler_data: HandlerData, @@ -118,6 +131,10 @@ impl ProtectedCaller for Caller { }) .collect()) } + + fn get_early_trapper(&self) -> Box { + Box::new(Trapper) + } } fn get_func_from_index( diff --git a/lib/clif-backend/src/signal/unix.rs b/lib/clif-backend/src/signal/unix.rs index 8e707433e..9d2f80eca 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -9,7 +9,7 @@ //! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here //! unless you have memory unsafety elsewhere in your code. //! -use crate::relocation::{TrapCode, TrapData, TrapSink}; +use crate::relocation::{TrapCode, TrapData}; use crate::signal::HandlerData; use libc::{c_int, c_void, siginfo_t}; use nix::sys::signal::{ @@ -60,6 +60,12 @@ thread_local! { pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null()); } +pub unsafe fn trigger_trap() -> ! { + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); + + longjmp(jmp_buf as *mut c_void, 0) +} + pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult { unsafe { let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); @@ -72,54 +78,59 @@ pub fn call_protected(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R let signum = setjmp(jmp_buf as *mut _); if signum != 0 { *jmp_buf = prev_jmp_buf; - let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); - if let Some(TrapData { - trapcode, - srcloc: _, - }) = handler_data.lookup(inst_ptr) - { - Err(match Signal::from_c_int(signum) { - Ok(SIGILL) => match trapcode { - TrapCode::BadSignature => RuntimeError::IndirectCallSignature { - table: TableIndex::new(0), + if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) { + Err(RuntimeError::User { msg }) + } else { + let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); + + if let Some(TrapData { + trapcode, + srcloc: _, + }) = handler_data.lookup(inst_ptr) + { + Err(match Signal::from_c_int(signum) { + Ok(SIGILL) => match trapcode { + TrapCode::BadSignature => RuntimeError::IndirectCallSignature { + table: TableIndex::new(0), + }, + TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull { + table: TableIndex::new(0), + }, + TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess { + memory: MemoryIndex::new(0), + addr: None, + }, + TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds { + table: TableIndex::new(0), + }, + _ => RuntimeError::Unknown { + msg: "unknown trap".to_string(), + }, }, - TrapCode::IndirectCallToNull => RuntimeError::IndirectCallToNull { - table: TableIndex::new(0), - }, - TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess { + Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess { memory: MemoryIndex::new(0), addr: None, }, - TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds { - table: TableIndex::new(0), - }, - _ => RuntimeError::Unknown { - msg: "unknown trap".to_string(), - }, - }, - Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess { - memory: MemoryIndex::new(0), - addr: None, - }, - Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation, - _ => unimplemented!(), + Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation, + _ => unimplemented!(), + } + .into()) + } else { + let signal = match Signal::from_c_int(signum) { + Ok(SIGFPE) => "floating-point exception", + Ok(SIGILL) => "illegal instruction", + Ok(SIGSEGV) => "segmentation violation", + Ok(SIGBUS) => "bus error", + Err(_) => "error while getting the Signal", + _ => "unkown trapped signal", + }; + // When the trap-handler is fully implemented, this will return more information. + Err(RuntimeError::Unknown { + msg: format!("trap at {:p} - {}", faulting_addr, signal), + } + .into()) } - .into()) - } else { - let signal = match Signal::from_c_int(signum) { - Ok(SIGFPE) => "floating-point exception", - Ok(SIGILL) => "illegal instruction", - Ok(SIGSEGV) => "segmentation violation", - Ok(SIGBUS) => "bus error", - Err(_) => "error while getting the Signal", - _ => "unkown trapped signal", - }; - // When the trap-handler is fully implemented, this will return more information. - Err(RuntimeError::Unknown { - msg: format!("trap at {:p} - {}", faulting_addr, signal), - } - .into()) } } else { let ret = f(); // TODO: Switch stack? diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index 8fef9bee3..9b5ec049e 100644 --- a/lib/runtime-core/src/backend.rs +++ b/lib/runtime-core/src/backend.rs @@ -82,6 +82,12 @@ pub trait ProtectedCaller: Send + Sync { vmctx: *mut vm::Ctx, _: Token, ) -> RuntimeResult>; + + fn get_early_trapper(&self) -> Box; +} + +pub trait UserTrapper { + unsafe fn do_early_trap(&self, msg: String) -> !; } pub trait FuncResolver: Send + Sync { diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index 60d6175ee..b72c74242 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -97,6 +97,9 @@ pub enum RuntimeError { table: TableIndex, }, IllegalArithmeticOperation, + User { + msg: String, + }, Unknown { msg: String, }, diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index a06da5f04..a8f36325a 100644 --- a/lib/runtime-core/src/instance.rs +++ b/lib/runtime-core/src/instance.rs @@ -7,7 +7,6 @@ use crate::{ import::{ImportObject, LikeNamespace}, memory::Memory, module::{ExportIndex, Module, ModuleInner}, - sig_registry::SigRegistry, table::Table, typed_func::{Func, Safe, WasmTypeList}, types::{FuncIndex, FuncSig, GlobalIndex, LocalOrImport, MemoryIndex, TableIndex, Value}, diff --git a/lib/runtime-core/src/module.rs b/lib/runtime-core/src/module.rs index dbf1f3635..0fdb24018 100644 --- a/lib/runtime-core/src/module.rs +++ b/lib/runtime-core/src/module.rs @@ -3,6 +3,7 @@ use crate::{ error::Result, import::ImportObject, structures::{Map, TypedIndex}, + typed_func::EARLY_TRAPPER, types::{ FuncIndex, FuncSig, GlobalDescriptor, GlobalIndex, GlobalInit, ImportedFuncIndex, ImportedGlobalIndex, ImportedMemoryIndex, ImportedTableIndex, Initializer, @@ -63,6 +64,10 @@ pub struct Module(#[doc(hidden)] pub Arc); impl Module { pub(crate) fn new(inner: Arc) -> Self { + unsafe { + EARLY_TRAPPER + .with(|ucell| *ucell.get() = Some(inner.protected_caller.get_early_trapper())); + } Module(inner) } diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index bd1bb2a2a..9cbcdc911 100644 --- a/lib/runtime-core/src/typed_func.rs +++ b/lib/runtime-core/src/typed_func.rs @@ -1,11 +1,16 @@ use crate::{ + backend::UserTrapper, error::RuntimeError, export::{Context, Export, FuncPointer}, import::IsExport, types::{FuncSig, Type, WasmExternType}, vm::Ctx, }; -use std::{marker::PhantomData, mem, ptr, sync::Arc}; +use std::{cell::UnsafeCell, fmt, marker::PhantomData, mem, panic, ptr, sync::Arc}; + +thread_local! { + pub static EARLY_TRAPPER: UnsafeCell>> = UnsafeCell::new(None); +} pub trait Safeness {} pub struct Safe; @@ -28,9 +33,44 @@ where Args: WasmTypeList, Rets: WasmTypeList, { - fn to_raw(self) -> *const (); + fn to_raw(&self) -> *const (); } +pub trait TrapEarly +where + Rets: WasmTypeList, +{ + fn report(self) -> Result; +} + +impl TrapEarly for Rets +where + Rets: WasmTypeList, +{ + fn report(self) -> Result { + Ok(self) + } +} + +impl TrapEarly for Result +where + Rets: WasmTypeList, + E: fmt::Debug, +{ + fn report(self) -> Result { + self.map_err(|err| format!("Error: {:?}", err)) + } +} + +// pub fn Func<'a, Args, Rets, F>(f: F) -> Func<'a, Args, Rets, Unsafe> +// where +// Args: WasmTypeList, +// Rets: WasmTypeList, +// F: ExternalFunction +// { +// Func::new(f) +// } + pub struct Func<'a, Args = (), Rets = (), Safety: Safeness = Safe> { f: *const (), ctx: *mut Ctx, @@ -143,18 +183,41 @@ macro_rules! impl_traits { } } - impl< $( $x: WasmExternType, )* Rets: WasmTypeList, FN: Fn( $( $x, )* &mut Ctx) -> Rets> ExternalFunction<($( $x ),*), Rets> for FN { + impl< $( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( $( $x, )* &mut Ctx) -> Trap> ExternalFunction<($( $x ),*), Rets> for FN { #[allow(non_snake_case)] - fn to_raw(self) -> *const () { + fn to_raw(&self) -> *const () { assert_eq!(mem::size_of::(), 0, "you cannot use a closure that captures state for `Func`."); - extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, FN: Fn( $( $x, )* &mut Ctx) -> Rets>( $( $x: $x, )* ctx: &mut Ctx) -> Rets::CStruct { + extern fn wrap<$( $x: WasmExternType, )* Rets: WasmTypeList, Trap: TrapEarly, FN: Fn( $( $x, )* &mut Ctx) -> Trap>( $( $x: $x, )* ctx: &mut Ctx) -> Rets::CStruct { let f: FN = unsafe { mem::transmute_copy(&()) }; - let rets = f( $( $x, )* ctx); - rets.into_c_struct() + + let msg = match panic::catch_unwind(panic::AssertUnwindSafe(|| { + f( $( $x, )* ctx).report() + })) { + Ok(Ok(returns)) => return returns.into_c_struct(), + Ok(Err(err)) => err, + Err(err) => { + if let Some(s) = err.downcast_ref::<&str>() { + s.to_string() + } else if let Some(s) = err.downcast_ref::() { + s.clone() + } else { + "a panic occurred, but no additional information is available".to_string() + } + }, + }; + + unsafe { + if let Some(early_trapper) = &*EARLY_TRAPPER.with(|ucell| ucell.get()) { + early_trapper.do_early_trap(msg) + } else { + eprintln!("panic handling not setup"); + std::process::exit(1) + } + } } - wrap::<$( $x, )* Rets, Self> as *const () + wrap::<$( $x, )* Rets, Trap, Self> as *const () } } diff --git a/lib/runtime-core/src/vm.rs b/lib/runtime-core/src/vm.rs index d470bc217..8cb9f0986 100644 --- a/lib/runtime-core/src/vm.rs +++ b/lib/runtime-core/src/vm.rs @@ -493,7 +493,7 @@ mod vm_ctx_tests { fn generate_module() -> ModuleInner { use super::Func; - use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token}; + use crate::backend::{Backend, FuncResolver, ProtectedCaller, Token, UserTrapper}; use crate::error::RuntimeResult; use crate::types::{FuncIndex, LocalFuncIndex, Value}; use hashbrown::HashMap; @@ -520,6 +520,9 @@ mod vm_ctx_tests { ) -> RuntimeResult> { Ok(vec![]) } + fn get_early_trapper(&self) -> Box { + unimplemented!() + } } ModuleInner { diff --git a/lib/spectests/examples/simple/main.rs b/lib/spectests/examples/simple/main.rs index a0c6ab296..fb215ee21 100644 --- a/lib/spectests/examples/simple/main.rs +++ b/lib/spectests/examples/simple/main.rs @@ -3,7 +3,7 @@ use wabt::wat2wasm; use wasmer_clif_backend::CraneliftCompiler; use wasmer_runtime_core::{ cache::Cache, - error::Result, + error, global::Global, memory::Memory, prelude::*, @@ -14,7 +14,7 @@ use wasmer_runtime_core::{ static EXAMPLE_WASM: &'static [u8] = include_bytes!("simple.wasm"); -fn main() -> Result<()> { +fn main() -> error::Result<()> { let compiler = CraneliftCompiler::new(); let wasm_binary = wat2wasm(IMPORT_MODULE.as_bytes()).expect("WAST not valid or malformed"); @@ -61,14 +61,14 @@ fn main() -> Result<()> { Ok(()) } -fn print_num(n: i32, ctx: &mut vm::Ctx) -> i32 { +fn print_num(n: i32, ctx: &mut vm::Ctx) -> Result { println!("print_num({})", n); let memory: &Memory = ctx.memory(0); let a: i32 = memory.view()[0].get(); - a + n + 1 + Ok(a + n + 1) } static IMPORT_MODULE: &str = r#"