diff --git a/lib/clif-backend/src/signal/mod.rs b/lib/clif-backend/src/signal/mod.rs index cb6e147b1a7..cf3d1eb021f 100644 --- a/lib/clif-backend/src/signal/mod.rs +++ b/lib/clif-backend/src/signal/mod.rs @@ -4,9 +4,10 @@ use crate::{ trampoline::Trampolines, }; use libc::c_void; -use std::{any::Any, cell::Cell, ptr::NonNull, sync::Arc}; +use std::{cell::Cell, ptr::NonNull, sync::Arc}; use wasmer_runtime_core::{ backend::RunnableModule, + error::{InvokeError, RuntimeError}, module::ModuleInfo, typed_func::{Trampoline, Wasm}, types::{LocalFuncIndex, SigIndex}, @@ -26,11 +27,9 @@ 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 struct Caller { handler_data: HandlerData, trampolines: Arc, @@ -63,7 +62,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,7 +79,10 @@ impl RunnableModule for Caller { match res { Err(err) => { - *error_out = Some(err.0); + // 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); false } Ok(()) => true, @@ -101,7 +103,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 98571cbbff3..965359e354f 100644 --- a/lib/clif-backend/src/signal/unix.rs +++ b/lib/clif-backend/src/signal/unix.rs @@ -10,7 +10,7 @@ //! unless you have memory unsafety elsewhere in your code. //! use crate::relocation::{TrapCode, TrapData}; -use crate::signal::{CallProtError, HandlerData}; +use crate::signal::HandlerData; use libc::{c_int, c_void, siginfo_t}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, @@ -19,6 +19,7 @@ use std::cell::{Cell, UnsafeCell}; use std::ptr; use std::sync::Once; use wasmer_runtime_core::backend::ExceptionCode; +use wasmer_runtime_core::error::InvokeError; extern "C" fn signal_trap_handler( signum: ::nix::libc::c_int, @@ -65,7 +66,7 @@ pub unsafe fn trigger_trap() -> ! { pub fn call_protected( handler_data: &HandlerData, f: impl FnOnce() -> T, -) -> Result { +) -> Result { unsafe { let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); let prev_jmp_buf = *jmp_buf; @@ -79,16 +80,12 @@ 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(InvokeError::EarlyTrap(Box::new(data))) } else { let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get()); - if let Some(TrapData { - trapcode, - srcloc: _, - }) = handler_data.lookup(inst_ptr) - { - Err(CallProtError(Box::new(match Signal::from_c_int(signum) { + if let Some(TrapData { trapcode, srcloc }) = handler_data.lookup(inst_ptr) { + let code = match Signal::from_c_int(signum) { Ok(SIGILL) => match trapcode { TrapCode::StackOverflow => ExceptionCode::MemoryOutOfBounds, TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds, @@ -101,9 +98,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(InvokeError::UnknownTrapCode { + trap_code: format!("{:?}", trapcode), + srcloc, + }) } }, Ok(SIGSEGV) | Ok(SIGBUS) => ExceptionCode::MemoryOutOfBounds, @@ -112,7 +110,8 @@ pub fn call_protected( "ExceptionCode::Unknown signal:{:?}", Signal::from_c_int(signum) ), - }))) + }; + Err(InvokeError::TrapCode { srcloc, code }) } else { let signal = match Signal::from_c_int(signum) { Ok(SIGFPE) => "floating-point exception", @@ -123,8 +122,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(InvokeError::UnknownTrap { + address: faulting_addr as usize, + signal, + }) } } } else { diff --git a/lib/llvm-backend/cpp/object_loader.hh b/lib/llvm-backend/cpp/object_loader.hh index 53bd2b0df85..d8d74f69be4 100644 --- a/lib/llvm-backend/cpp/object_loader.hh +++ b/lib/llvm-backend/cpp/object_loader.hh @@ -50,8 +50,8 @@ typedef struct { } callbacks_t; typedef struct { - size_t data, vtable; -} box_any_t; + size_t data; +} runtime_error_t; enum WasmTrapType { Unreachable = 0, @@ -121,7 +121,7 @@ private: struct WasmErrorSink { WasmTrapType *trap_out; - box_any_t *user_error; + runtime_error_t *user_error; }; struct WasmException : std::exception { @@ -149,14 +149,14 @@ public: struct UserException : UncatchableException { public: - UserException(size_t data, size_t vtable) : error_data({data, vtable}) {} + UserException(size_t data) : error_data({data}) {} virtual std::string description() const noexcept override { return "user exception"; } - // The parts of a `Box`. - box_any_t error_data; + // The pointer to `Option`. + runtime_error_t error_data; virtual void write_error(WasmErrorSink &out) const noexcept override { *out.user_error = error_data; @@ -274,10 +274,10 @@ result_t module_load(const uint8_t *mem_ptr, size_t mem_size, void module_delete(WasmModule *module) { delete module; } -// Throw a fat pointer that's assumed to be `*mut dyn Any` on the rust +// Throw a pointer that's assumed to be `*mut RuntimeError` on the rust // side. -[[noreturn]] void throw_any(size_t data, size_t vtable) { - unsafe_unwind(new UserException(data, vtable)); +[[noreturn]] void throw_runtime_error(size_t data) { + unsafe_unwind(new UserException(data)); } // Throw a pointer that's assumed to be codegen::BreakpointHandler on the @@ -288,7 +288,7 @@ void module_delete(WasmModule *module) { delete module; } 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 { + runtime_error_t *user_error, void *invoke_env) noexcept { try { catch_unwind([trampoline, ctx, func, params, results]() { trampoline(ctx, func, params, results); diff --git a/lib/llvm-backend/src/backend.rs b/lib/llvm-backend/src/backend.rs index a2ac15a940c..18d5a8ce3b0 100644 --- a/lib/llvm-backend/src/backend.rs +++ b/lib/llvm-backend/src/backend.rs @@ -11,7 +11,6 @@ use inkwell::{ }; use libc::c_char; use std::{ - any::Any, cell::RefCell, ffi::{c_void, CString}, mem, @@ -27,6 +26,7 @@ use wasmer_runtime_core::{ CacheGen, ExceptionCode, RunnableModule, }, cache::Error as CacheError, + error::{InvokeError, RuntimeError}, module::ModuleInfo, state::ModuleStateMap, structures::TypedIndex, @@ -56,7 +56,7 @@ extern "C" { /// but this is cleaner, I think? #[cfg_attr(nightly, unwind(allowed))] #[allow(improper_ctypes)] - fn throw_any(data: *mut dyn Any) -> !; + fn throw_runtime_error(data: *mut Option) -> !; #[allow(improper_ctypes)] fn cxx_invoke_trampoline( @@ -66,7 +66,7 @@ extern "C" { params: *const u64, results: *mut u64, trap_out: *mut i32, - error_out: *mut Option>, + error_out: *mut Option, invoke_env: Option>, ) -> bool; } @@ -79,7 +79,7 @@ unsafe extern "C" fn invoke_trampoline( func_ptr: NonNull, params: *const u64, results: *mut u64, - error_out: *mut Option>, + error_out: *mut Option, invoke_env: Option>, ) -> bool { let mut trap_out: i32 = -1; @@ -95,15 +95,22 @@ unsafe extern "C" fn invoke_trampoline( ); // 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, - })); + *error_out = { + let exception_code = match trap_out { + 0 => ExceptionCode::Unreachable, + 1 => ExceptionCode::IncorrectCallIndirectSignature, + 2 => ExceptionCode::MemoryOutOfBounds, + 3 => ExceptionCode::CallIndirectOOB, + 4 => ExceptionCode::IllegalArithmetic, + 5 => ExceptionCode::MisalignedAtomicAccess, + _ => return ret, + }; + Some(InvokeError::TrapCode { + code: exception_code, + // TODO: + srcloc: 0, + }) + }; } ret } @@ -467,8 +474,8 @@ impl RunnableModule for LLVMBackend { self.msm.clone() } - unsafe fn do_early_trap(&self, data: Box) -> ! { - throw_any(Box::leak(data)) + unsafe fn do_early_trap(&self, data: RuntimeError) -> ! { + throw_runtime_error(Box::into_raw(Box::new(Some(data)))) } } diff --git a/lib/llvm-backend/src/code.rs b/lib/llvm-backend/src/code.rs index 554628e254b..385cef47d18 100644 --- a/lib/llvm-backend/src/code.rs +++ b/lib/llvm-backend/src/code.rs @@ -35,6 +35,7 @@ use wasmer_runtime_core::{ backend::{CacheGen, CompilerConfig, Token}, cache::{Artifact, Error as CacheError}, codegen::*, + error::RuntimeError, memory::MemoryType, module::{ModuleInfo, ModuleInner}, parse::{wp_type_to_type, LoadError}, @@ -940,12 +941,11 @@ pub struct CodegenError { // prevents unused function elimination. #[no_mangle] pub unsafe extern "C" fn callback_trampoline( - b: *mut Option>, + b: *mut Option, callback: *mut BreakpointHandler, ) { let callback = Box::from_raw(callback); - let result: Result<(), Box> = - callback(BreakpointInfo { fault: None }); + let result: Result<(), RuntimeError> = callback(BreakpointInfo { fault: None }); match result { Ok(()) => *b = None, Err(e) => *b = Some(e), diff --git a/lib/middleware-common/src/metering.rs b/lib/middleware-common/src/metering.rs index 409035d4ba7..d4db44f905e 100644 --- a/lib/middleware-common/src/metering.rs +++ b/lib/middleware-common/src/metering.rs @@ -1,5 +1,6 @@ use wasmer_runtime_core::{ codegen::{Event, EventSink, FunctionMiddleware, InternalEvent}, + error::RuntimeError, module::ModuleInfo, vm::{Ctx, InternalField}, wasmparser::{Operator, Type as WpType, TypeOrFuncType as WpTypeOrFuncType}, @@ -96,7 +97,9 @@ impl FunctionMiddleware for Metering { ty: WpTypeOrFuncType::Type(WpType::EmptyBlockType), })); sink.push(Event::Internal(InternalEvent::Breakpoint(Box::new(|_| { - Err(Box::new(ExecutionLimitExceededError)) + Err(RuntimeError::Metering(Box::new( + ExecutionLimitExceededError, + ))) })))); sink.push(Event::WasmOwned(Operator::End)); } diff --git a/lib/runtime-c-api/src/import/mod.rs b/lib/runtime-c-api/src/import/mod.rs index 79631de1ced..c6d724f51e0 100644 --- a/lib/runtime-c-api/src/import/mod.rs +++ b/lib/runtime-c-api/src/import/mod.rs @@ -21,6 +21,7 @@ use wasmer::import::{ImportObject, ImportObjectIterator}; use wasmer::vm::Ctx; use wasmer::wasm::{Export, FuncSig, Global, Memory, Module, Table, Type}; use wasmer_runtime_core::{ + error::RuntimeError, export::{Context, FuncPointer}, module::ImportName, }; @@ -723,7 +724,7 @@ pub unsafe extern "C" fn wasmer_trap( (&*ctx.module) .runnable_module - .do_early_trap(Box::new(error_message)); // never returns + .do_early_trap(RuntimeError::User(Box::new(error_message))); // never returns // cbindgen does not generate a binding for a function that // returns `!`. Since we also need to error in some cases, the diff --git a/lib/runtime-c-api/tests/test-import-trap.c b/lib/runtime-c-api/tests/test-import-trap.c index 0e28208eccc..c81df2311dc 100644 --- a/lib/runtime-c-api/tests/test-import-trap.c +++ b/lib/runtime-c-api/tests/test-import-trap.c @@ -69,7 +69,7 @@ int main() wasmer_last_error_message(error_str, error_len); printf("Error str: `%s`\n", error_str); - assert(0 == strcmp(error_str, "Call error: \"Hello\"")); + assert(0 == strcmp(error_str, "Call error: User supplied error: \"Hello\"")); printf("Destroying func\n"); wasmer_import_func_destroy(func); diff --git a/lib/runtime-c-api/wasmer.h b/lib/runtime-c-api/wasmer.h index 1e7d9296d74..6262d8e8d0f 100644 --- a/lib/runtime-c-api/wasmer.h +++ b/lib/runtime-c-api/wasmer.h @@ -924,7 +924,7 @@ void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx); * // Allocate them and set them on the given instance. * my_data *data = malloc(sizeof(my_data)); * data->… = …; - * wasmer_instance_context_data_set(instance, (void*) my_data); + * wasmer_instance_context_data_set(instance, (void*) data); * * // You can read your data. * { diff --git a/lib/runtime-c-api/wasmer.hh b/lib/runtime-c-api/wasmer.hh index 0bbf819ccca..647e637d03f 100644 --- a/lib/runtime-c-api/wasmer.hh +++ b/lib/runtime-c-api/wasmer.hh @@ -736,7 +736,7 @@ void *wasmer_instance_context_data_get(const wasmer_instance_context_t *ctx); /// // Allocate them and set them on the given instance. /// my_data *data = malloc(sizeof(my_data)); /// data->… = …; -/// wasmer_instance_context_data_set(instance, (void*) my_data); +/// wasmer_instance_context_data_set(instance, (void*) data); /// /// // You can read your data. /// { diff --git a/lib/runtime-core/src/backend.rs b/lib/runtime-core/src/backend.rs index c6235475d8d..cbef1db3bc0 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/codegen.rs b/lib/runtime-core/src/codegen.rs index 4e2bf74a4a2..6ff5c596c03 100644 --- a/lib/runtime-core/src/codegen.rs +++ b/lib/runtime-core/src/codegen.rs @@ -6,13 +6,12 @@ use crate::{ backend::RunnableModule, backend::{CacheGen, Compiler, CompilerConfig, Features, Token}, cache::{Artifact, Error as CacheError}, - error::{CompileError, CompileResult}, + error::{CompileError, CompileResult, RuntimeError}, module::{ModuleInfo, ModuleInner}, structures::Map, types::{FuncIndex, FuncSig, SigIndex}, }; use smallvec::SmallVec; -use std::any::Any; use std::collections::HashMap; use std::fmt; use std::fmt::Debug; @@ -23,7 +22,7 @@ use wasmparser::{Operator, Type as WpType}; /// A type that defines a function pointer, which is called when breakpoints occur. pub type BreakpointHandler = - Box Result<(), Box> + Send + Sync + 'static>; + Box Result<(), RuntimeError> + Send + Sync + 'static>; /// Maps instruction pointers to their breakpoint handlers. pub type BreakpointMap = Arc>; diff --git a/lib/runtime-core/src/error.rs b/lib/runtime-core/src/error.rs index a685a8238f2..44c12ad6eaf 100644 --- a/lib/runtime-core/src/error.rs +++ b/lib/runtime-core/src/error.rs @@ -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,113 @@ 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 + InvokeError(InvokeError), + /// A metering triggered error value. + /// + /// An error of this type indicates that it was returned by the metering system. + Metering(Box), + /// A user triggered error value. + /// + /// An error returned from a host function. + User(Box), +} + +/// TODO: +#[derive(Debug)] +pub enum InvokeError { + /// Indicates an exceptional circumstance such as a bug that should be reported or + /// a hardware failure. + FailedWithNoError, + + /// TODO: + UnknownTrap { + /// TODO: + address: usize, + /// TODO: + signal: &'static str, + }, + /// TODO: + TrapCode { + /// TODO: + code: ExceptionCode, + /// TODO: + srcloc: u32, + }, + /// TODO: + UnknownTrapCode { + /// TODO: + trap_code: String, + /// TODO: + srcloc: u32, + }, + /// extra TODO: investigate if this can be a `Box` instead (looks like probably no) + /// TODO: + EarlyTrap(Box), + /// Indicates an error that ocurred related to breakpoints. + Breakpoint(Box), +} + +impl From for RuntimeError { + fn from(other: InvokeError) -> RuntimeError { + match other { + InvokeError::EarlyTrap(re) | InvokeError::Breakpoint(re) => *re, + _ => RuntimeError::InvokeError(other), + } + } +} + +impl PartialEq for RuntimeError { + fn eq(&self, _other: &RuntimeError) -> bool { + false + } +} + +//impl std::error::Error for InvokeError {} + +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: update invoke error formatting + RuntimeError::InvokeError(_) => write!(f, "Error when calling invoke"), + RuntimeError::Metering(_) => write!(f, "unknown metering error type"), + 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 user 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/fault.rs b/lib/runtime-core/src/fault.rs index d8c985e09b9..44cae268372 100644 --- a/lib/runtime-core/src/fault.rs +++ b/lib/runtime-core/src/fault.rs @@ -28,15 +28,15 @@ pub mod raw { } use crate::codegen::{BreakpointInfo, BreakpointMap}; +use crate::error::{InvokeError, RuntimeError}; use crate::state::x64::{build_instance_image, read_stack, X64Register, GPR}; -use crate::state::{CodeVersion, ExecutionStateImage}; +use crate::state::{CodeVersion, ExecutionStateImage, InstanceImage}; use crate::vm; use libc::{mmap, mprotect, siginfo_t, MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTRAP, }; -use std::any::Any; use std::cell::{Cell, RefCell, UnsafeCell}; use std::ffi::c_void; use std::process; @@ -61,7 +61,7 @@ type SetJmpBuffer = [i32; SETJMP_BUFFER_LEN]; struct UnwindInfo { jmpbuf: SetJmpBuffer, // in breakpoints: Option, - payload: Option>, // out + payload: Option, // out } /// A store for boundary register preservation. @@ -182,7 +182,7 @@ pub unsafe fn clear_wasm_interrupt() { pub unsafe fn catch_unsafe_unwind R>( f: F, breakpoints: Option, -) -> Result> { +) -> Result { let unwind = UNWIND.with(|x| x.get()); let old = (*unwind).take(); *unwind = Some(UnwindInfo { @@ -205,7 +205,7 @@ pub unsafe fn catch_unsafe_unwind R>( } /// Begins an unsafe unwind. -pub unsafe fn begin_unsafe_unwind(e: Box) -> ! { +pub unsafe fn begin_unsafe_unwind(e: RuntimeError) -> ! { let unwind = UNWIND.with(|x| x.get()); let inner = (*unwind) .as_mut() @@ -279,7 +279,7 @@ extern "C" fn signal_trap_handler( static ARCH: Architecture = Architecture::Aarch64; let mut should_unwind = false; - let mut unwind_result: Box = Box::new(()); + let mut unwind_result: Option> = None; unsafe { let fault = get_fault_info(siginfo as _, ucontext); @@ -302,7 +302,7 @@ extern "C" fn signal_trap_handler( ) { match ib.ty { InlineBreakpointType::Middleware => { - let out: Option>> = + let out: Option> = with_breakpoint_map(|bkpt_map| { bkpt_map.and_then(|x| x.get(&ip)).map(|x| { x(BreakpointInfo { @@ -313,7 +313,7 @@ extern "C" fn signal_trap_handler( if let Some(Ok(())) = out { } else if let Some(Err(e)) = out { should_unwind = true; - unwind_result = e; + unwind_result = Some(Err(e)); } } } @@ -328,7 +328,7 @@ extern "C" fn signal_trap_handler( }) }); if should_unwind { - begin_unsafe_unwind(unwind_result); + begin_unsafe_unwind(unwind_result.unwrap().unwrap_err()); } if early_return { return; @@ -342,20 +342,22 @@ extern "C" fn signal_trap_handler( match Signal::from_c_int(signum) { Ok(SIGTRAP) => { // breakpoint - let out: Option>> = - with_breakpoint_map(|bkpt_map| { - bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map(|x| { - x(BreakpointInfo { - fault: Some(&fault), - }) - }) + let out: Option> = + with_breakpoint_map(|bkpt_map| -> Option> { + bkpt_map.and_then(|x| x.get(&(fault.ip.get()))).map( + |x| -> Result<(), RuntimeError> { + x(BreakpointInfo { + fault: Some(&fault), + }) + }, + ) }); match out { Some(Ok(())) => { return false; } Some(Err(e)) => { - unwind_result = e; + unwind_result = Some(Err(e)); return true; } None => {} @@ -387,7 +389,7 @@ extern "C" fn signal_trap_handler( if is_suspend_signal { // If this is a suspend signal, we parse the runtime state and return the resulting image. let image = build_instance_image(ctx, es_image); - unwind_result = Box::new(image); + unwind_result = Some(Ok(image)); } else { // Otherwise, this is a real exception and we just throw it to the caller. if !es_image.frames.is_empty() { @@ -415,7 +417,11 @@ extern "C" fn signal_trap_handler( None }); if let Some(code) = exc_code { - unwind_result = Box::new(code); + unwind_result = Some(Err(RuntimeError::InvokeError(InvokeError::TrapCode { + code, + // TODO: + srcloc: 0, + }))); } } @@ -423,7 +429,7 @@ extern "C" fn signal_trap_handler( }); if should_unwind { - begin_unsafe_unwind(unwind_result); + begin_unsafe_unwind(unwind_result.unwrap().unwrap_err()); } } } diff --git a/lib/runtime-core/src/instance.rs b/lib/runtime-core/src/instance.rs index 48e59d4ec65..1c5bfb93231 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, InvokeError, ResolveError, ResolveResult, Result, RuntimeError}, export::{Context, Export, ExportIter, Exportable, FuncPointer}, global::Global, import::{ImportObject, LikeNamespace}, @@ -584,25 +584,28 @@ 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( - trampoline, - ctx_ptr, - func_ptr, - raw_args.as_ptr(), - result_space, - &mut error_out, - invoke_env, - ); + let success = unsafe { + invoke( + trampoline, + ctx_ptr, + func_ptr, + raw_args.as_ptr(), + 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(Into::into) + .unwrap_or_else(|| RuntimeError::InvokeError(InvokeError::FailedWithNoError)); + Err(error.into()) } }; diff --git a/lib/runtime-core/src/state.rs b/lib/runtime-core/src/state.rs index 55809e965d7..d7d5a6ee1a8 100644 --- a/lib/runtime-core/src/state.rs +++ b/lib/runtime-core/src/state.rs @@ -699,13 +699,13 @@ pub mod x64 { pub use super::x64_decl::*; use super::*; use crate::codegen::BreakpointMap; + use crate::error::RuntimeError; use crate::fault::{ catch_unsafe_unwind, get_boundary_register_preservation, run_on_alternative_stack, }; use crate::structures::TypedIndex; use crate::types::LocalGlobalIndex; use crate::vm::Ctx; - use std::any::Any; #[allow(clippy::cast_ptr_alignment)] unsafe fn compute_vmctx_deref(vmctx: *const Ctx, seq: &[usize]) -> u64 { @@ -738,7 +738,7 @@ pub mod x64 { image: InstanceImage, vmctx: &mut Ctx, breakpoints: Option, - ) -> Result> { + ) -> Result { let mut stack: Vec = vec![0; 1048576 * 8 / 8]; // 8MB stack let mut stack_offset: usize = stack.len(); diff --git a/lib/runtime-core/src/typed_func.rs b/lib/runtime-core/src/typed_func.rs index a6337bc5888..b4db0fbacfa 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::{InvokeError, RuntimeError}, 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,9 @@ 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 +590,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), Into::into)) } } } @@ -678,9 +678,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 +792,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 diff --git a/lib/singlepass-backend/src/codegen_x64.rs b/lib/singlepass-backend/src/codegen_x64.rs index e51db5b7c5b..63fd95dece4 100644 --- a/lib/singlepass-backend/src/codegen_x64.rs +++ b/lib/singlepass-backend/src/codegen_x64.rs @@ -10,7 +10,6 @@ use dynasmrt::x64::Assembler; use dynasmrt::{AssemblyOffset, DynamicLabel, DynasmApi, DynasmLabelApi}; use smallvec::SmallVec; use std::{ - any::Any, collections::{BTreeMap, HashMap}, ffi::c_void, iter, mem, @@ -27,6 +26,7 @@ use wasmer_runtime_core::{ }, cache::{Artifact, Error as CacheError}, codegen::*, + error::{InvokeError, RuntimeError}, fault::{self, raw::register_preservation_trampoline}, loader::CodeMemory, memory::MemoryType, @@ -214,7 +214,7 @@ pub struct X64FunctionCode { breakpoints: Option< HashMap< AssemblyOffset, - Box Result<(), Box> + Send + Sync + 'static>, + Box Result<(), RuntimeError> + Send + Sync + 'static>, >, >, returns: SmallVec<[WpType; 1]>, @@ -507,7 +507,7 @@ impl RunnableModule for X64ExecutionContext { func: NonNull, args: *const u64, rets: *mut u64, - error_out: *mut Option>, + error_out: *mut Option, num_params_plus_one: Option>, ) -> bool { let rm: &Box = &(&*(*ctx).module).runnable_module; @@ -655,7 +655,7 @@ impl RunnableModule for X64ExecutionContext { true } Err(err) => { - *error_out = Some(err); + *error_out = Some(InvokeError::Breakpoint(Box::new(err))); false } }; @@ -680,7 +680,7 @@ impl RunnableModule for X64ExecutionContext { }) } - unsafe fn do_early_trap(&self, data: Box) -> ! { + unsafe fn do_early_trap(&self, data: RuntimeError) -> ! { fault::begin_unsafe_unwind(data); } diff --git a/src/commands/run.rs b/src/commands/run.rs index b58075fd777..12d3335e90d 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -21,6 +21,7 @@ use wasmer_runtime::{ use wasmer_runtime_core::{ self, backend::{Compiler, CompilerConfig, MemoryBoundCheckMode}, + error::RuntimeError, loader::{Instance as LoadedInstance, LocalLoader}, Module, }; @@ -437,9 +438,12 @@ fn execute_wasi( } if let Err(ref err) = result { - if let Some(error_code) = err.0.downcast_ref::() { - std::process::exit(error_code.code as i32) + if let RuntimeError::User(user_error) = err { + if let Some(error_code) = user_error.downcast_ref::() { + std::process::exit(error_code.code as i32) + } } + return Err(format!("error: {:?}", err)); } } diff --git a/tests/high_level_api.rs b/tests/high_level_api.rs index 389e20c411b..5d9ae70fb03 100644 --- a/tests/high_level_api.rs +++ b/tests/high_level_api.rs @@ -268,7 +268,7 @@ wasmer_backends! { let result = foo.call(); - if let Err(RuntimeError(e)) = result { + if let Err(RuntimeError::User(e)) = result { let exit_code = e.downcast::().unwrap(); assert_eq!(exit_code.code, 42); } else { diff --git a/tests/imports.rs b/tests/imports.rs index e4ce076719c..ed1a2da81d7 100644 --- a/tests/imports.rs +++ b/tests/imports.rs @@ -52,7 +52,7 @@ macro_rules! call_and_assert { expected_value, concat!("Expected right when calling `", stringify!($function), "`.") ), - (Err(RuntimeError(data)), Err(RuntimeError(expected_data))) => { + (Err(RuntimeError::User(data)), Err(RuntimeError::User(expected_data))) => { if let (Some(data), Some(expected_data)) = ( data.downcast_ref::<&str>(), expected_data.downcast_ref::<&str>(), @@ -406,7 +406,7 @@ wasmer_backends! { test!( test_fn, function_fn(i32) -> i32, (1) == Ok(2)); test!( test_closure, function_closure(i32) -> i32, (1) == Ok(2)); test!( test_fn_dynamic, function_fn_dynamic(i32) -> i32, (1) == Ok(2)); - test!( test_fn_dynamic_panic, function_fn_dynamic_panic(i32) -> i32, (1) == Err(RuntimeError(Box::new("test")))); + test!( test_fn_dynamic_panic, function_fn_dynamic_panic(i32) -> i32, (1) == Err(RuntimeError::User(Box::new("test")))); test!( test_closure_dynamic_0, @@ -460,31 +460,31 @@ wasmer_backends! { test_fn_trap, function_fn_trap(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("foo {}", 2)))) + (1) == Err(RuntimeError::User(Box::new(format!("foo {}", 2)))) ); test!( test_closure_trap, function_closure_trap(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("bar {}", 2)))) + (1) == Err(RuntimeError::User(Box::new(format!("bar {}", 2)))) ); test!( test_fn_trap_with_vmctx, function_fn_trap_with_vmctx(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("baz {}", 2 + SHIFT)))) + (1) == Err(RuntimeError::User(Box::new(format!("baz {}", 2 + SHIFT)))) ); test!( test_closure_trap_with_vmctx, function_closure_trap_with_vmctx(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("qux {}", 2 + SHIFT)))) + (1) == Err(RuntimeError::User(Box::new(format!("qux {}", 2 + SHIFT)))) ); test!( test_closure_trap_with_vmctx_and_env, function_closure_trap_with_vmctx_and_env(i32) -> i32, - (1) == Err(RuntimeError(Box::new(format!("! {}", 2 + shift + SHIFT)))) + (1) == Err(RuntimeError::User(Box::new(format!("! {}", 2 + shift + SHIFT)))) ); #[test] diff --git a/tests/middleware_common.rs b/tests/middleware_common.rs index 90516af0c30..ca423661df9 100644 --- a/tests/middleware_common.rs +++ b/tests/middleware_common.rs @@ -6,6 +6,7 @@ use wasmer::wasm::Func; use wasmer_middleware_common::metering::*; use wasmer_runtime_core::codegen::ModuleCodeGenerator; use wasmer_runtime_core::codegen::{MiddlewareChain, StreamingCompiler}; +use wasmer_runtime_core::error::RuntimeError; use wasmer_runtime_core::fault::{pop_code_version, push_code_version}; use wasmer_runtime_core::state::CodeVersion; @@ -179,10 +180,13 @@ fn middleware_test_traps_after_costly_call(backend: &'static str, compiler: impl } let err = result.unwrap_err(); - assert!(err - .0 - .downcast_ref::() - .is_some()); + if let RuntimeError::Metering(metering_err) = err { + assert!(metering_err + .downcast_ref::() + .is_some()); + } else { + assert!(false, "metering error not found"); + } // 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.