Skip to content

Commit

Permalink
Define runtime error values explicitly in Singlepass
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark McCaskey committed Apr 24, 2020
1 parent bfb6814 commit b9ec8f9
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 102 deletions.
25 changes: 4 additions & 21 deletions lib/clif-backend/src/signal/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::{
relocation::{TrapData, TrapSink, TrapCode},
relocation::{TrapData, TrapSink},
resolver::FuncResolver,
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, ExceptionCode},
backend::RunnableModule,
error::{InvokeError, RuntimeError},
module::ModuleInfo,
typed_func::{Trampoline, Wasm},
Expand All @@ -30,23 +30,6 @@ thread_local! {
pub static TRAP_EARLY_DATA: Cell<Option<RuntimeError>> = Cell::new(None);
}

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,
trampolines: Arc<Trampolines>,
Expand Down Expand Up @@ -99,7 +82,7 @@ impl RunnableModule for Caller {
// 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);
*error_out = Some(err);
false
}
Ok(()) => true,
Expand Down
24 changes: 9 additions & 15 deletions lib/clif-backend/src/signal/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -65,7 +66,7 @@ pub unsafe fn trigger_trap() -> ! {
pub fn call_protected<T>(
handler_data: &HandlerData,
f: impl FnOnce() -> T,
) -> Result<T, CallProtError> {
) -> Result<T, InvokeError> {
unsafe {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
let prev_jmp_buf = *jmp_buf;
Expand All @@ -79,15 +80,11 @@ 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::EarlyTrap(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)
{
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,
Expand All @@ -101,8 +98,8 @@ pub fn call_protected<T>(
TrapCode::BadConversionToInteger => ExceptionCode::IllegalArithmetic,
TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable,
_ => {
return Err(CallProtError::UnknownTrapCode {
trap_code: trapcode,
return Err(InvokeError::UnknownTrapCode {
trap_code: format!("{:?}", trapcode),
srcloc,
})
}
Expand All @@ -114,10 +111,7 @@ pub fn call_protected<T>(
Signal::from_c_int(signum)
),
};
Err(CallProtError::TrapCode {
srcloc,
code,
})
Err(InvokeError::TrapCode { srcloc, code })
} else {
let signal = match Signal::from_c_int(signum) {
Ok(SIGFPE) => "floating-point exception",
Expand All @@ -128,7 +122,7 @@ pub fn call_protected<T>(
_ => "unknown trapped signal",
};
// When the trap-handler is fully implemented, this will return more information.
Err(CallProtError::UnknownTrap {
Err(InvokeError::UnknownTrap {
address: faulting_addr as usize,
signal,
})
Expand Down
38 changes: 23 additions & 15 deletions lib/llvm-backend/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use inkwell::{
};
use libc::c_char;
use std::{
any::Any,
cell::RefCell,
ffi::{c_void, CString},
mem,
Expand All @@ -27,6 +26,7 @@ use wasmer_runtime_core::{
CacheGen, ExceptionCode, RunnableModule,
},
cache::Error as CacheError,
error::{InvokeError, RuntimeError},
module::ModuleInfo,
state::ModuleStateMap,
structures::TypedIndex,
Expand Down Expand Up @@ -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: RuntimeError) -> !;

#[allow(improper_ctypes)]
fn cxx_invoke_trampoline(
Expand All @@ -66,7 +66,7 @@ extern "C" {
params: *const u64,
results: *mut u64,
trap_out: *mut i32,
error_out: *mut Option<Box<dyn Any + Send>>,
error_out: *mut Option<InvokeError>,
invoke_env: Option<NonNull<c_void>>,
) -> bool;
}
Expand All @@ -79,7 +79,7 @@ unsafe extern "C" fn invoke_trampoline(
func_ptr: NonNull<vm::Func>,
params: *const u64,
results: *mut u64,
error_out: *mut Option<Box<dyn Any + Send>>,
error_out: *mut Option<InvokeError>,
invoke_env: Option<NonNull<c_void>>,
) -> bool {
let mut trap_out: i32 = -1;
Expand All @@ -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
}
Expand Down Expand Up @@ -467,8 +474,9 @@ impl RunnableModule for LLVMBackend {
self.msm.clone()
}

unsafe fn do_early_trap(&self, data: Box<dyn Any + Send>) -> ! {
throw_any(Box::leak(data))
unsafe fn do_early_trap(&self, data: RuntimeError) -> ! {
// maybe need to box leak it?
throw_runtime_error(data)
}
}

Expand Down
3 changes: 2 additions & 1 deletion lib/middleware-common/src/metering.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -96,7 +97,7 @@ 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));
}
Expand Down
5 changes: 2 additions & 3 deletions lib/runtime-core/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<dyn Fn(BreakpointInfo) -> Result<(), Box<dyn Any + Send>> + Send + Sync + 'static>;
Box<dyn Fn(BreakpointInfo) -> Result<(), RuntimeError> + Send + Sync + 'static>;

/// Maps instruction pointers to their breakpoint handlers.
pub type BreakpointMap = Arc<HashMap<usize, BreakpointHandler>>;
Expand Down
50 changes: 40 additions & 10 deletions lib/runtime-core/src/error.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -222,31 +222,63 @@ impl std::error::Error for RuntimeError {}
/// extremely rare and impossible to handle.
#[derive(Debug)]
pub enum RuntimeError {
/// When an invoke returns an error (this is where exception codes come from?)
/// 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<dyn Any + Send>),
/// A user triggered error value.
///
/// An error returned from a host function.
User(Box<dyn Any + Send>)
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,

/// 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<InvokeError>` instead (looks like probably no)
/// TODO:
EarlyTrap(Box<RuntimeError>),
/// Indicates an error that ocurred related to breakpoints. (currently Singlepass only)
Breakpoint(Box<RuntimeError>),
}

//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: ideally improve the error type of invoke
// 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::<String>() {
Expand All @@ -256,9 +288,9 @@ impl std::fmt::Display for RuntimeError {
} else if let Some(n) = user_error.downcast_ref::<i32>() {
write!(f, "{}", n)
} else {
write!(f, "unknown error type")
write!(f, "unknown user error type")
}
},
}
}
}
}
Expand All @@ -270,8 +302,6 @@ impl From<InternalError> for RuntimeError {
}
}
*/



/// This error type is produced by resolving a wasm function
/// given its name.
Expand Down
Loading

0 comments on commit b9ec8f9

Please sign in to comment.