Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make runtime and trap errors well defined #1401

Merged
merged 9 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## **[Unreleased]**

- [#1401](https://github.com/wasmerio/wasmer/pull/1401) Make breaking change to `RuntimeError`: `RuntimeError` is now more explicit about its possible error values allowing for better insight into why a call may have failed.
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
- [#1382](https://github.com/wasmerio/wasmer/pull/1382) Refactored test infranstructure (part 2)
- [#1380](https://github.com/wasmerio/wasmer/pull/1380) Refactored test infranstructure (part 1)
- [#1357](https://github.com/wasmerio/wasmer/pull/1357) Refactored bin commands into separate files
Expand Down
1 change: 0 additions & 1 deletion examples/callback-guest/callback-guest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ extern "C" {
fn host_callback() -> u32;
}

#[no_mangle]
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
fn test_callback() -> u32 {
42
}
Expand Down
Binary file modified examples/callback-guest/callback-guest.wasm
Binary file not shown.
16 changes: 9 additions & 7 deletions lib/clif-backend/src/signal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::RuntimeError,
module::ModuleInfo,
typed_func::{Trampoline, Wasm},
types::{LocalFuncIndex, SigIndex},
Expand All @@ -26,11 +27,9 @@ 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 struct Caller {
handler_data: HandlerData,
trampolines: Arc<Trampolines>,
Expand Down Expand Up @@ -63,7 +62,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<RuntimeError>,
invoke_env: Option<NonNull<c_void>>,
) -> bool {
let handler_data = &*invoke_env.unwrap().cast().as_ptr();
Expand All @@ -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
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
*error_out = Some(err.into());
false
}
Ok(()) => true,
Expand All @@ -101,7 +103,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()
}
Expand Down
31 changes: 16 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,16 +80,12 @@ 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(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,
Expand All @@ -101,9 +98,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(InvokeError::UnknownTrapCode {
trap_code: format!("{:?}", trapcode),
srcloc,
})
}
},
Ok(SIGSEGV) | Ok(SIGBUS) => ExceptionCode::MemoryOutOfBounds,
Expand All @@ -112,7 +110,8 @@ pub fn call_protected<T>(
"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",
Expand All @@ -123,8 +122,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(InvokeError::UnknownTrap {
address: faulting_addr as usize,
signal,
})
}
}
} else {
Expand Down
98 changes: 54 additions & 44 deletions lib/clif-backend/src/signal/windows.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
relocation::{TrapCode, TrapData},
signal::{CallProtError, HandlerData},
signal::HandlerData,
};
use std::{
cell::Cell,
Expand All @@ -9,6 +9,7 @@ use std::{
};
use wasmer_runtime_core::{
backend::ExceptionCode,
error::InvokeError,
typed_func::Trampoline,
vm::{Ctx, Func},
};
Expand All @@ -32,14 +33,42 @@ thread_local! {
pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
}

fn get_signal_name(code: DWORD) -> &'static str {
match code {
EXCEPTION_FLT_DENORMAL_OPERAND
| EXCEPTION_FLT_DIVIDE_BY_ZERO
| EXCEPTION_FLT_INEXACT_RESULT
| EXCEPTION_FLT_INVALID_OPERATION
| EXCEPTION_FLT_OVERFLOW
| EXCEPTION_FLT_STACK_CHECK
| EXCEPTION_FLT_UNDERFLOW => "floating-point exception",
EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction",
EXCEPTION_ACCESS_VIOLATION => "segmentation violation",
EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment",
EXCEPTION_BREAKPOINT => "breakpoint",
EXCEPTION_SINGLE_STEP => "single step",
EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded",
EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero",
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved
EXCEPTION_INT_OVERFLOW => "int overflow",
EXCEPTION_PRIV_INSTRUCTION => "priv instruction",
EXCEPTION_IN_PAGE_ERROR => "in page error",
EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception",
EXCEPTION_STACK_OVERFLOW => "stack overflow",
EXCEPTION_GUARD_PAGE => "guard page",
EXCEPTION_INVALID_HANDLE => "invalid handle",
EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock",
_ => "unknown exception code",
}
}

pub fn call_protected(
handler_data: &HandlerData,
trampoline: Trampoline,
ctx: *mut Ctx,
func: NonNull<Func>,
param_vec: *const u64,
return_vec: *mut u64,
) -> Result<(), CallProtError> {
) -> Result<(), InvokeError> {
// TODO: trap early
// user code error
// if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Expand All @@ -58,64 +87,45 @@ pub fn call_protected(
instruction_pointer,
} = result.unwrap_err();

if let Some(TrapData {
trapcode,
srcloc: _,
}) = handler_data.lookup(instruction_pointer as _)
{
Err(CallProtError(Box::new(match code as DWORD {
if let Some(TrapData { trapcode, srcloc }) = handler_data.lookup(instruction_pointer as _) {
let exception_code = match code as DWORD {
EXCEPTION_ACCESS_VIOLATION => ExceptionCode::MemoryOutOfBounds,
EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode {
TrapCode::BadSignature => ExceptionCode::IncorrectCallIndirectSignature,
TrapCode::IndirectCallToNull => ExceptionCode::CallIndirectOOB,
TrapCode::HeapOutOfBounds => ExceptionCode::MemoryOutOfBounds,
TrapCode::TableOutOfBounds => ExceptionCode::CallIndirectOOB,
TrapCode::UnreachableCodeReached => ExceptionCode::Unreachable,
_ => return Err(CallProtError(Box::new("unknown trap code".to_string()))),
_ => {
return Err(InvokeError::UnknownTrapCode {
trap_code: format!("{}", code as DWORD),
srcloc,
})
}
},
EXCEPTION_STACK_OVERFLOW => ExceptionCode::MemoryOutOfBounds,
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => {
ExceptionCode::IllegalArithmetic
}
_ => {
return Err(CallProtError(Box::new(
"unknown exception code".to_string(),
)))
let signal = get_signal_name(code as DWORD);
return Err(InvokeError::UnknownTrap {
address: exception_address as usize,
signal,
});
}
})))
} else {
let signal = match code as DWORD {
EXCEPTION_FLT_DENORMAL_OPERAND
| EXCEPTION_FLT_DIVIDE_BY_ZERO
| EXCEPTION_FLT_INEXACT_RESULT
| EXCEPTION_FLT_INVALID_OPERATION
| EXCEPTION_FLT_OVERFLOW
| EXCEPTION_FLT_STACK_CHECK
| EXCEPTION_FLT_UNDERFLOW => "floating-point exception",
EXCEPTION_ILLEGAL_INSTRUCTION => "illegal instruction",
EXCEPTION_ACCESS_VIOLATION => "segmentation violation",
EXCEPTION_DATATYPE_MISALIGNMENT => "datatype misalignment",
EXCEPTION_BREAKPOINT => "breakpoint",
EXCEPTION_SINGLE_STEP => "single step",
EXCEPTION_ARRAY_BOUNDS_EXCEEDED => "array bounds exceeded",
EXCEPTION_INT_DIVIDE_BY_ZERO => "int div by zero",
EXCEPTION_INT_OVERFLOW => "int overflow",
EXCEPTION_PRIV_INSTRUCTION => "priv instruction",
EXCEPTION_IN_PAGE_ERROR => "in page error",
EXCEPTION_NONCONTINUABLE_EXCEPTION => "non continuable exception",
EXCEPTION_STACK_OVERFLOW => "stack overflow",
EXCEPTION_GUARD_PAGE => "guard page",
EXCEPTION_INVALID_HANDLE => "invalid handle",
EXCEPTION_POSSIBLE_DEADLOCK => "possible deadlock",
_ => "unknown exception code",
};
return Err(InvokeError::TrapCode {
srcloc,
code: exception_code,
});
} else {
let signal = get_signal_name(code as DWORD);

let s = format!(
"unhandled trap at {:x} - code #{:x}: {}",
exception_address, code, signal,
);

Err(CallProtError(Box::new(s)))
Err(InvokeError::UnknownTrap {
address: exception_address as usize,
signal,
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/llvm-backend/cpp/object_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ MemoryManager::~MemoryManager() {
callbacks.dealloc_memory(read_section.base, read_section.size);
callbacks.dealloc_memory(readwrite_section.base, readwrite_section.size);
}
void unwinding_setjmp(jmp_buf stack_out, void (*func)(void *), void *userdata) {
void unwinding_setjmp(jmp_buf &stack_out, void (*func)(void *), void *userdata) {
if (setjmp(stack_out)) {

} else {
Expand Down
26 changes: 14 additions & 12 deletions lib/llvm-backend/cpp/object_loader.hh
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ typedef struct {
visit_fde_t visit_fde;
} callbacks_t;

typedef struct {
size_t data, vtable;
} box_any_t;
using runtime_error_t = void*;
MarkMcCaskey marked this conversation as resolved.
Show resolved Hide resolved

enum WasmTrapType {
Unreachable = 0,
Expand All @@ -65,6 +63,8 @@ enum WasmTrapType {

extern "C" void callback_trampoline(void *, void *);

extern "C" void copy_runtime_error(runtime_error_t src, runtime_error_t dst);

struct MemoryManager : llvm::RuntimeDyld::MemoryManager {
public:
MemoryManager(callbacks_t callbacks) : callbacks(callbacks) {}
Expand Down Expand Up @@ -121,7 +121,7 @@ private:

struct WasmErrorSink {
WasmTrapType *trap_out;
box_any_t *user_error;
runtime_error_t user_error;
};

struct WasmException : std::exception {
Expand Down Expand Up @@ -149,17 +149,19 @@ public:

struct UserException : UncatchableException {
public:
UserException(size_t data, size_t vtable) : error_data({data, vtable}) {}
UserException(size_t data) {
error_data = reinterpret_cast<runtime_error_t>(data);
}

virtual std::string description() const noexcept override {
return "user exception";
}

// The parts of a `Box<dyn Any>`.
box_any_t error_data;
// The pointer to `Option<RuntimeError>`.
runtime_error_t error_data;

virtual void write_error(WasmErrorSink &out) const noexcept override {
*out.user_error = error_data;
copy_runtime_error(error_data, out.user_error);
}
};

Expand Down Expand Up @@ -274,10 +276,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 Option<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
Expand All @@ -288,7 +290,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);
Expand Down
Loading