mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-04 18:10:18 +00:00
Make runtime and trap errors well defined (WIP)
This commit is contained in:
parent
ab106af422
commit
bfb6814f23
@ -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<Option<Box<dyn Any + Send>>> = Cell::new(None);
|
||||
pub static TRAP_EARLY_DATA: Cell<Option<RuntimeError>> = Cell::new(None);
|
||||
}
|
||||
|
||||
pub struct CallProtError(pub Box<dyn Any + Send>);
|
||||
pub enum CallProtError {
|
||||
UnknownTrap {
|
||||
address: usize,
|
||||
signal: &'static str,
|
||||
},
|
||||
TrapCode {
|
||||
code: ExceptionCode,
|
||||
srcloc: u32,
|
||||
},
|
||||
UnknownTrapCode {
|
||||
trap_code: TrapCode,
|
||||
srcloc: u32,
|
||||
},
|
||||
EarlyTrap(RuntimeError),
|
||||
Misc(Box<dyn Any + Send>),
|
||||
}
|
||||
|
||||
pub struct Caller {
|
||||
handler_data: HandlerData,
|
||||
@ -63,7 +79,7 @@ impl RunnableModule for Caller {
|
||||
func: NonNull<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
error_out: *mut Option<InvokeError>,
|
||||
invoke_env: Option<NonNull<c_void>>,
|
||||
) -> 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<dyn Any + Send>) -> ! {
|
||||
unsafe fn do_early_trap(&self, data: RuntimeError) -> ! {
|
||||
TRAP_EARLY_DATA.with(|cell| cell.set(Some(data)));
|
||||
trigger_trap()
|
||||
}
|
||||
|
@ -79,16 +79,16 @@ 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(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<T>(
|
||||
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<T>(
|
||||
"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<T>(
|
||||
_ => "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 {
|
||||
|
@ -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<Wasm>;
|
||||
|
||||
/// Trap an error.
|
||||
unsafe fn do_early_trap(&self, data: Box<dyn Any + Send>) -> !;
|
||||
unsafe fn do_early_trap(&self, data: RuntimeError) -> !;
|
||||
|
||||
/// Returns the machine code associated with this module.
|
||||
fn get_code(&self) -> Option<&[u8]> {
|
||||
|
@ -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(<string>))`.
|
||||
///
|
||||
/// 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<dyn Any + Send>)
|
||||
}
|
||||
|
||||
/// TODO:
|
||||
#[derive(Debug)]
|
||||
pub enum InvokeError {
|
||||
/// not yet handled error cases, ideally we should be able to handle them all
|
||||
Misc(Box<dyn Any + Send>),
|
||||
/// 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::<String>() {
|
||||
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::<i32>() {
|
||||
write!(f, "{}", n)
|
||||
} else {
|
||||
write!(f, "unknown error type")
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
impl From<InternalError> for RuntimeError {
|
||||
fn from(other: InternalError) -> Self {
|
||||
RuntimeError(Box::new(other))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/// This error type is produced by resolving a wasm function
|
||||
/// given its name.
|
||||
///
|
||||
|
@ -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())
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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<vm::Func>,
|
||||
args: *const u64,
|
||||
rets: *mut u64,
|
||||
error_out: *mut Option<Box<dyn Any + Send>>,
|
||||
error_out: *mut Option<InvokeError>,
|
||||
extra: Option<NonNull<c_void>>,
|
||||
) -> 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<dyn Any + Send>
|
||||
RuntimeError::User(b as Box<dyn Any + Send>)
|
||||
},
|
||||
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<dyn Any + Send>
|
||||
RuntimeError::User(b as Box<dyn Any + Send>)
|
||||
},
|
||||
Err(err) => err,
|
||||
// TODO(blocking): this line is wrong!
|
||||
Err(err) => RuntimeError::User(err),
|
||||
};
|
||||
|
||||
// At this point, there is an error that needs to
|
||||
|
Loading…
Reference in New Issue
Block a user