mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-24 14:11:32 +00:00
Fix runtime error catching (#157)
This commit is contained in:
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -348,6 +348,18 @@ dependencies = [
|
|||||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "opaque-debug"
|
name = "opaque-debug"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
@ -798,7 +810,8 @@ dependencies = [
|
|||||||
"cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cranelift-native 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cranelift-wasm 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.48 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.85 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde-bench 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -948,6 +961,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
||||||
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
|
"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
|
||||||
"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f"
|
"checksum nix 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "921f61dc817b379d0834e45d5ec45beaacfae97082090a49c2cf30dcbc30206f"
|
||||||
|
"checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b"
|
||||||
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
|
"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409"
|
||||||
"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
|
"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
|
||||||
"checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242"
|
"checksum page_size 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f89ef58b3d32420dbd1a43d2f38ae92f6239ef12bb556ab09ca55445f5a67242"
|
||||||
|
@ -17,7 +17,8 @@ hashbrown = "0.1"
|
|||||||
target-lexicon = "0.2.0"
|
target-lexicon = "0.2.0"
|
||||||
wasmparser = "0.23.0"
|
wasmparser = "0.23.0"
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
nix = "0.12.0"
|
nix = "0.13.0"
|
||||||
|
libc = "0.2.48"
|
||||||
|
|
||||||
# Dependencies for caching.
|
# Dependencies for caching.
|
||||||
[dependencies.serde]
|
[dependencies.serde]
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
use crate::call::sighandler::install_sighandler;
|
use crate::call::sighandler::install_sighandler;
|
||||||
use crate::relocation::{TrapCode, TrapData, TrapSink};
|
use crate::relocation::{TrapCode, TrapData, TrapSink};
|
||||||
use nix::libc::{c_void, siginfo_t};
|
use libc::{c_int, c_void, siginfo_t};
|
||||||
use nix::sys::signal::{Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV};
|
use nix::sys::signal::{Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV};
|
||||||
use std::cell::{Cell, UnsafeCell};
|
use std::cell::{Cell, UnsafeCell};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
@ -18,15 +18,15 @@ use wasmer_runtime_core::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn setjmp(env: *mut ::nix::libc::c_void) -> ::nix::libc::c_int;
|
pub fn setjmp(env: *mut c_void) -> c_int;
|
||||||
fn longjmp(env: *mut ::nix::libc::c_void, val: ::nix::libc::c_int) -> !;
|
fn longjmp(env: *mut c_void, val: c_int) -> !;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SETJMP_BUFFER_LEN: usize = 27;
|
const SETJMP_BUFFER_LEN: usize = 27;
|
||||||
pub static SIGHANDLER_INIT: Once = Once::new();
|
pub static SIGHANDLER_INIT: Once = Once::new();
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
pub static SETJMP_BUFFER: UnsafeCell<[::nix::libc::c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]);
|
pub static SETJMP_BUFFER: UnsafeCell<[c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]);
|
||||||
pub static CAUGHT_ADDRESSES: Cell<(*const c_void, *const c_void)> = Cell::new((ptr::null(), ptr::null()));
|
pub static CAUGHT_ADDRESSES: Cell<(*const c_void, *const c_void)> = Cell::new((ptr::null(), ptr::null()));
|
||||||
pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
|
pub static CURRENT_EXECUTABLE_BUFFER: Cell<*const c_void> = Cell::new(ptr::null());
|
||||||
}
|
}
|
||||||
@ -75,15 +75,15 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
|
|||||||
install_sighandler();
|
install_sighandler();
|
||||||
});
|
});
|
||||||
|
|
||||||
let signum = setjmp(jmp_buf as *mut ::nix::libc::c_void);
|
let signum = setjmp(jmp_buf as *mut _);
|
||||||
if signum != 0 {
|
if signum != 0 {
|
||||||
*jmp_buf = prev_jmp_buf;
|
*jmp_buf = prev_jmp_buf;
|
||||||
let (faulting_addr, _) = CAUGHT_ADDRESSES.with(|cell| cell.get());
|
let (faulting_addr, inst_ptr) = CAUGHT_ADDRESSES.with(|cell| cell.get());
|
||||||
|
|
||||||
if let Some(TrapData {
|
if let Some(TrapData {
|
||||||
trapcode,
|
trapcode,
|
||||||
srcloc: _,
|
srcloc: _,
|
||||||
}) = handler_data.lookup(faulting_addr)
|
}) = handler_data.lookup(inst_ptr)
|
||||||
{
|
{
|
||||||
Err(match Signal::from_c_int(signum) {
|
Err(match Signal::from_c_int(signum) {
|
||||||
Ok(SIGILL) => match trapcode {
|
Ok(SIGILL) => match trapcode {
|
||||||
@ -95,7 +95,7 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
|
|||||||
},
|
},
|
||||||
TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess {
|
TrapCode::HeapOutOfBounds => RuntimeError::OutOfBoundsAccess {
|
||||||
memory: MemoryIndex::new(0),
|
memory: MemoryIndex::new(0),
|
||||||
addr: 0,
|
addr: None,
|
||||||
},
|
},
|
||||||
TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds {
|
TrapCode::TableOutOfBounds => RuntimeError::TableOutOfBounds {
|
||||||
table: TableIndex::new(0),
|
table: TableIndex::new(0),
|
||||||
@ -106,7 +106,7 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
|
|||||||
},
|
},
|
||||||
Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess {
|
Ok(SIGSEGV) | Ok(SIGBUS) => RuntimeError::OutOfBoundsAccess {
|
||||||
memory: MemoryIndex::new(0),
|
memory: MemoryIndex::new(0),
|
||||||
addr: 0,
|
addr: None,
|
||||||
},
|
},
|
||||||
Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation,
|
Ok(SIGFPE) => RuntimeError::IllegalArithmeticOperation,
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
@ -136,7 +136,7 @@ pub fn call_protected<T>(handler_data: &HandlerData, f: impl FnOnce() -> T) -> R
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unwinds to last protected_call.
|
/// Unwinds to last protected_call.
|
||||||
pub unsafe fn do_unwind(signum: i32, siginfo: *mut siginfo_t, ucontext: *const c_void) -> ! {
|
pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! {
|
||||||
// Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.)
|
// Since do_unwind is only expected to get called from WebAssembly code which doesn't hold any host resources (locks etc.)
|
||||||
// itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should
|
// itself, accessing TLS here is safe. In case any other code calls this, it often indicates a memory safety bug and you should
|
||||||
// temporarily disable the signal handlers to debug it.
|
// temporarily disable the signal handlers to debug it.
|
||||||
@ -153,18 +153,91 @@ pub unsafe fn do_unwind(signum: i32, siginfo: *mut siginfo_t, ucontext: *const c
|
|||||||
|
|
||||||
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
|
||||||
unsafe fn get_faulting_addr_and_ip(
|
unsafe fn get_faulting_addr_and_ip(
|
||||||
siginfo: *mut siginfo_t,
|
siginfo: *const c_void,
|
||||||
_ucontext: *const c_void,
|
ucontext: *const c_void,
|
||||||
) -> (*const c_void, *const c_void) {
|
) -> (*const c_void, *const c_void) {
|
||||||
(ptr::null(), ptr::null())
|
use libc::{ucontext_t, RIP};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct siginfo_t {
|
||||||
|
si_signo: i32,
|
||||||
|
si_errno: i32,
|
||||||
|
si_code: i32,
|
||||||
|
si_addr: u64,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
let siginfo = siginfo as *const siginfo_t;
|
||||||
|
let si_addr = (*siginfo).si_addr;
|
||||||
|
|
||||||
|
let ucontext = ucontext as *const ucontext_t;
|
||||||
|
let rip = (*ucontext).uc_mcontext.gregs[RIP as usize];
|
||||||
|
|
||||||
|
(si_addr as _, rip as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
#[cfg(all(target_os = "macos", target_arch = "x86_64"))]
|
||||||
unsafe fn get_faulting_addr_and_ip(
|
unsafe fn get_faulting_addr_and_ip(
|
||||||
siginfo: *mut siginfo_t,
|
siginfo: *const c_void,
|
||||||
_ucontext: *const c_void,
|
ucontext: *const c_void,
|
||||||
) -> (*const c_void, *const c_void) {
|
) -> (*const c_void, *const c_void) {
|
||||||
((*siginfo).si_addr, ptr::null())
|
#[allow(dead_code)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct ucontext_t {
|
||||||
|
uc_onstack: u32,
|
||||||
|
uc_sigmask: u32,
|
||||||
|
uc_stack: libc::stack_t,
|
||||||
|
uc_link: *const ucontext_t,
|
||||||
|
uc_mcsize: u64,
|
||||||
|
uc_mcontext: *const mcontext_t,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
struct exception_state {
|
||||||
|
trapno: u16,
|
||||||
|
cpu: u16,
|
||||||
|
err: u32,
|
||||||
|
faultvaddr: u64,
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
struct regs {
|
||||||
|
rax: u64,
|
||||||
|
rbx: u64,
|
||||||
|
rcx: u64,
|
||||||
|
rdx: u64,
|
||||||
|
rdi: u64,
|
||||||
|
rsi: u64,
|
||||||
|
rbp: u64,
|
||||||
|
rsp: u64,
|
||||||
|
r8: u64,
|
||||||
|
r9: u64,
|
||||||
|
r10: u64,
|
||||||
|
r11: u64,
|
||||||
|
r12: u64,
|
||||||
|
r13: u64,
|
||||||
|
r14: u64,
|
||||||
|
r15: u64,
|
||||||
|
rip: u64,
|
||||||
|
rflags: u64,
|
||||||
|
cs: u64,
|
||||||
|
fs: u64,
|
||||||
|
gs: u64,
|
||||||
|
}
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct mcontext_t {
|
||||||
|
es: exception_state,
|
||||||
|
ss: regs,
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
let siginfo = siginfo as *const siginfo_t;
|
||||||
|
let si_addr = (*siginfo).si_addr;
|
||||||
|
|
||||||
|
let ucontext = ucontext as *const ucontext_t;
|
||||||
|
let rip = (*(*ucontext).uc_mcontext).ss.rip;
|
||||||
|
|
||||||
|
(si_addr, rip as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(
|
#[cfg(not(any(
|
||||||
|
@ -26,6 +26,6 @@ extern "C" fn signal_trap_handler(
|
|||||||
ucontext: *mut c_void,
|
ucontext: *mut c_void,
|
||||||
) {
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
recovery::do_unwind(signum, siginfo, ucontext);
|
recovery::do_unwind(signum, siginfo as _, ucontext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,10 @@ impl TrapSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn lookup(&self, offset: usize) -> Option<TrapData> {
|
pub fn lookup(&self, offset: usize) -> Option<TrapData> {
|
||||||
self.trap_datas.get(offset).map(|(_, trap_data)| *trap_data)
|
self.trap_datas
|
||||||
|
.iter()
|
||||||
|
.find(|(trap_offset, _)| *trap_offset == offset)
|
||||||
|
.map(|(_, trap_data)| *trap_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn drain_local(&mut self, current_func_offset: usize, local: &mut LocalTrapSink) {
|
pub fn drain_local(&mut self, current_func_offset: usize, local: &mut LocalTrapSink) {
|
||||||
|
@ -83,12 +83,23 @@ impl PartialEq for LinkError {
|
|||||||
/// Comparing two `RuntimeError`s always evaluates to false.
|
/// Comparing two `RuntimeError`s always evaluates to false.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
OutOfBoundsAccess { memory: MemoryIndex, addr: u32 },
|
OutOfBoundsAccess {
|
||||||
TableOutOfBounds { table: TableIndex },
|
memory: MemoryIndex,
|
||||||
IndirectCallSignature { table: TableIndex },
|
addr: Option<u32>,
|
||||||
IndirectCallToNull { table: TableIndex },
|
},
|
||||||
|
TableOutOfBounds {
|
||||||
|
table: TableIndex,
|
||||||
|
},
|
||||||
|
IndirectCallSignature {
|
||||||
|
table: TableIndex,
|
||||||
|
},
|
||||||
|
IndirectCallToNull {
|
||||||
|
table: TableIndex,
|
||||||
|
},
|
||||||
IllegalArithmeticOperation,
|
IllegalArithmeticOperation,
|
||||||
Unknown { msg: String },
|
Unknown {
|
||||||
|
msg: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for RuntimeError {
|
impl PartialEq for RuntimeError {
|
||||||
|
Reference in New Issue
Block a user