Skip to content

Commit

Permalink
Merge #381
Browse files Browse the repository at this point in the history
381: Hook up error propagation r=lachlansneff a=lachlansneff

You can now retrieve the error from a panicking or error-returning imported function.

Example:

```rust
#[derive(Debug)]
struct ExitCode {
    code: i32,
}

fn do_panic(_ctx: &mut Ctx) -> Result<i32, ExitCode> {
    Err(ExitCode { code: 42 })
}

// This wasm functions calls `do_panic`.
let foo: Func<(), i32> = instance.func(...)?;

let result = foo.call();

println!("result: {:?}", result);

if let Err(RuntimeError::Error { data }) = result {
    if let Ok(exit_code) = data.downcast::<ExitCode>() {
        println!("exit code: {:?}", exit_code);
    }
}
```

outputs:

```
result: Err(unknown error)
exit code: ExitCode { code: 42 }
```

Co-authored-by: Lachlan Sneff <lachlan.sneff@gmail.com>
  • Loading branch information
bors[bot] and lachlansneff committed Apr 22, 2019
2 parents a42b7d3 + ff9de18 commit b7f98c8
Show file tree
Hide file tree
Showing 15 changed files with 240 additions and 126 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All PRs to the Wasmer repository must add to this file.
Blocks of changes will separated by version increments.

## **[Unreleased]**
- [#381](https://github.com/wasmerio/wasmer/pull/381) Allow retrieving propagated user errors.
- [#379](https://github.com/wasmerio/wasmer/pull/379) Fix small return types from imported functions.
- [#371](https://github.com/wasmerio/wasmer/pull/371) Add more Debug impl for WASI types
- [#368](https://github.com/wasmerio/wasmer/pull/368) Fix issue with write buffering
Expand Down
26 changes: 20 additions & 6 deletions lib/clif-backend/src/signal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ thread_local! {
pub static TRAP_EARLY_DATA: Cell<Option<Box<dyn Any>>> = Cell::new(None);
}

pub enum CallProtError {
Trap(WasmTrapInfo),
Error(Box<dyn Any>),
}

pub struct Caller {
handler_data: HandlerData,
trampolines: Arc<Trampolines>,
Expand Down Expand Up @@ -59,7 +64,8 @@ impl RunnableModule for Caller {
func: NonNull<vm::Func>,
args: *const u64,
rets: *mut u64,
_trap_info: *mut WasmTrapInfo,
trap_info: *mut WasmTrapInfo,
user_error: *mut Option<Box<dyn Any>>,
invoke_env: Option<NonNull<c_void>>,
) -> bool {
let handler_data = &*invoke_env.unwrap().cast().as_ptr();
Expand All @@ -68,14 +74,22 @@ impl RunnableModule for Caller {
let res = call_protected(handler_data, || {
// Leap of faith.
trampoline(ctx, func, args, rets);
})
.is_ok();
});

// the trampoline is called from C on windows
#[cfg(target_os = "windows")]
let res = call_protected(handler_data, trampoline, ctx, func, args, rets).is_ok();

res
let res = call_protected(handler_data, trampoline, ctx, func, args, rets);

match res {
Err(err) => {
match err {
CallProtError::Trap(info) => *trap_info = info,
CallProtError::Error(data) => *user_error = Some(data),
}
false
}
Ok(()) => true,
}
}

let trampoline = self
Expand Down
50 changes: 18 additions & 32 deletions lib/clif-backend/src/signal/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
//! unless you have memory unsafety elsewhere in your code.
//!
use crate::relocation::{TrapCode, TrapData};
use crate::signal::HandlerData;
use crate::signal::{CallProtError, HandlerData};
use libc::{c_int, c_void, siginfo_t};
use nix::sys::signal::{
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
};
use std::cell::{Cell, UnsafeCell};
use std::ptr;
use std::sync::Once;
use wasmer_runtime_core::error::{RuntimeError, RuntimeResult};
use wasmer_runtime_core::typed_func::WasmTrapInfo;

extern "C" fn signal_trap_handler(
signum: ::nix::libc::c_int,
Expand Down Expand Up @@ -62,7 +62,10 @@ pub unsafe fn trigger_trap() -> ! {
longjmp(jmp_buf as *mut c_void, 0)
}

pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> RuntimeResult<T> {
pub fn call_protected<T>(
handler_data: &HandlerData,
f: impl FnOnce() -> T,
) -> Result<T, CallProtError> {
unsafe {
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
let prev_jmp_buf = *jmp_buf;
Expand All @@ -76,7 +79,7 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
*jmp_buf = prev_jmp_buf;

if let Some(data) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Err(RuntimeError::Error { data })
Err(CallProtError::Error(data))
} else {
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());

Expand All @@ -85,33 +88,18 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
srcloc: _,
}) = handler_data.lookup(inst_ptr)
{
Err(match Signal::from_c_int(signum) {
Err(CallProtError::Trap(match Signal::from_c_int(signum) {
Ok(SIGILL) => match trapcode {
TrapCode::BadSignature => RuntimeError::Trap {
msg: "incorrect call_indirect signature".into(),
},
TrapCode::IndirectCallToNull => RuntimeError::Trap {
msg: "indirect call to null".into(),
},
TrapCode::HeapOutOfBounds => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
TrapCode::TableOutOfBounds => RuntimeError::Trap {
msg: "table out-of-bounds access".into(),
},
_ => RuntimeError::Trap {
msg: "unknown trap".into(),
},
},
Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
Ok(SIGFPE) => RuntimeError::Trap {
msg: "illegal arithmetic operation".into(),
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB,
TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds,
TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB,
_ => WasmTrapInfo::Unknown,
},
Ok(SIGSEGV) | Ok(SIGBUS) => WasmTrapInfo::MemoryOutOfBounds,
Ok(SIGFPE) => WasmTrapInfo::IllegalArithmetic,
_ => unimplemented!(),
}
.into())
}))
} else {
let signal = match Signal::from_c_int(signum) {
Ok(SIGFPE) => "floating-point exception",
Expand All @@ -122,10 +110,8 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
_ => "unkown trapped signal",
};
// When the trap-handler is fully implemented, this will return more information.
Err(RuntimeError::Trap {
msg: format!("unknown trap at {:p} - {}", faulting_addr, signal).into(),
}
.into())
let s = format!("unknown trap at {:p} - {}", faulting_addr, signal);
Err(CallProtError::Error(Box::new(s)))
}
}
} else {
Expand Down
56 changes: 20 additions & 36 deletions lib/clif-backend/src/signal/windows.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::relocation::{TrapCode, TrapData};
use crate::signal::HandlerData;
use crate::signal::{CallProtError, HandlerData};
use crate::trampoline::Trampoline;
use std::cell::Cell;
use std::ffi::c_void;
use std::ptr::{self, NonNull};
use wasmer_runtime_core::error::{RuntimeError, RuntimeResult};
use wasmer_runtime_core::typed_func::WasmTrapInfo;
use wasmer_runtime_core::vm::Ctx;
use wasmer_runtime_core::vm::Func;
use wasmer_win_exception_handler::CallProtectedData;
Expand All @@ -28,7 +29,7 @@ pub fn call_protected(
func: NonNull<Func>,
param_vec: *const u64,
return_vec: *mut u64,
) -> RuntimeResult<()> {
) -> Result<(), CallProtError> {
// TODO: trap early
// user code error
// if let Some(msg) = super::TRAP_EARLY_DATA.with(|cell| cell.replace(None)) {
Expand All @@ -52,38 +53,22 @@ pub fn call_protected(
srcloc: _,
}) = handler_data.lookup(instruction_pointer as _)
{
Err(match signum as DWORD {
EXCEPTION_ACCESS_VIOLATION => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
Err(CallProtError::Trap(match signum as DWORD {
EXCEPTION_ACCESS_VIOLATION => WasmTrapInfo::MemoryOutOfBounds,
EXCEPTION_ILLEGAL_INSTRUCTION => match trapcode {
TrapCode::BadSignature => RuntimeError::Trap {
msg: "incorrect call_indirect signature".into(),
},
TrapCode::IndirectCallToNull => RuntimeError::Trap {
msg: "indirect call to null".into(),
},
TrapCode::HeapOutOfBounds => RuntimeError::Trap {
msg: "memory out-of-bounds access".into(),
},
TrapCode::TableOutOfBounds => RuntimeError::Trap {
msg: "table out-of-bounds access".into(),
},
_ => RuntimeError::Trap {
msg: "unknown trap".into(),
},
},
EXCEPTION_STACK_OVERFLOW => RuntimeError::Trap {
msg: "stack overflow trap".into(),
},
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => RuntimeError::Trap {
msg: "illegal arithmetic operation".into(),
TrapCode::BadSignature => WasmTrapInfo::IncorrectCallIndirectSignature,
TrapCode::IndirectCallToNull => WasmTrapInfo::CallIndirectOOB,
TrapCode::HeapOutOfBounds => WasmTrapInfo::MemoryOutOfBounds,
TrapCode::TableOutOfBounds => WasmTrapInfo::CallIndirectOOB,
TrapCode::UnreachableCodeReached => WasmTrapInfo::Unreachable,
_ => WasmTrapInfo::Unknown,
},
_ => RuntimeError::Trap {
msg: "unknown trap".into(),
},
}
.into())
EXCEPTION_STACK_OVERFLOW => WasmTrapInfo::Unknown,
EXCEPTION_INT_DIVIDE_BY_ZERO | EXCEPTION_INT_OVERFLOW => {
WasmTrapInfo::IllegalArithmetic
}
_ => WasmTrapInfo::Unknown,
}))
} else {
let signal = match signum as DWORD {
EXCEPTION_FLT_DENORMAL_OPERAND
Expand All @@ -98,10 +83,9 @@ pub fn call_protected(
_ => "unkown trapped signal",
};

Err(RuntimeError::Trap {
msg: format!("unknown trap at {} - {}", exception_address, signal).into(),
}
.into())
let s = format!("unknown trap at {} - {}", exception_address, signal);

Err(CallProtError::Error(Box::new(s)))
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/llvm-backend/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,8 @@ fn main() {

println!("cargo:rustc-link-lib=static=llvm-backend");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=cpp/object_loader.cpp");
println!("cargo:rerun-if-changed=cpp/object_loader.hh");

// Enable "nightly" cfg if the current compiler is nightly.
if rustc_version::version_meta().unwrap().channel == rustc_version::Channel::Nightly {
Expand Down
15 changes: 13 additions & 2 deletions lib/llvm-backend/cpp/object_loader.hh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ typedef struct
visit_fde_t visit_fde;
} callbacks_t;

typedef struct
{
size_t data, vtable;
} box_any_t;

struct WasmException
{
public:
Expand All @@ -61,15 +66,15 @@ struct UncatchableException : WasmException
struct UserException : UncatchableException
{
public:
UserException(size_t data, size_t vtable) : data(data), vtable(vtable) {}
UserException(size_t data, size_t vtable) : error_data({ data, vtable }) {}

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

// The parts of a `Box<dyn Any>`.
size_t data, vtable;
box_any_t error_data;
};

struct WasmTrap : UncatchableException
Expand Down Expand Up @@ -194,6 +199,7 @@ extern "C"
void *params,
void *results,
WasmTrap::Type *trap_out,
box_any_t *user_error,
void *invoke_env) throw()
{
try
Expand All @@ -206,6 +212,11 @@ extern "C"
*trap_out = e.type;
return false;
}
catch (const UserException &e)
{
*user_error = e.error_data;
return false;
}
catch (const WasmException &e)
{
*trap_out = WasmTrap::Type::Unknown;
Expand Down
1 change: 1 addition & 0 deletions lib/llvm-backend/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ extern "C" {
params: *const u64,
results: *mut u64,
trap_out: *mut WasmTrapInfo,
user_error: *mut Option<Box<dyn Any>>,
invoke_env: Option<NonNull<c_void>>,
) -> bool;
}
Expand Down
12 changes: 5 additions & 7 deletions lib/runtime-core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,15 +137,13 @@ impl std::fmt::Display for RuntimeError {
write!(f, "WebAssembly trap occured during runtime: {}", msg)
}
RuntimeError::Error { data } => {
let msg = if let Some(s) = data.downcast_ref::<String>() {
s
if let Some(s) = data.downcast_ref::<String>() {
write!(f, "\"{}\"", s)
} else if let Some(s) = data.downcast_ref::<&str>() {
s
write!(f, "\"{}\"", s)
} else {
"user-defined, opaque"
};

write!(f, "{}", msg)
write!(f, "unknown error")
}
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions lib/runtime-core/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ fn call_func_with_index(

let run_wasm = |result_space: *mut u64| unsafe {
let mut trap_info = WasmTrapInfo::Unknown;
let mut user_error = None;

let success = invoke(
trampoline,
Expand All @@ -536,15 +537,20 @@ fn call_func_with_index(
raw_args.as_ptr(),
result_space,
&mut trap_info,
&mut user_error,
invoke_env,
);

if success {
Ok(())
} else {
Err(RuntimeError::Trap {
msg: trap_info.to_string().into(),
})
if let Some(data) = user_error {
Err(RuntimeError::Error { data })
} else {
Err(RuntimeError::Trap {
msg: trap_info.to_string().into(),
})
}
}
};

Expand Down
Loading

0 comments on commit b7f98c8

Please sign in to comment.