Improved trap catching with call_protected macro

This commit is contained in:
Syrus Akbary 2018-11-24 10:50:48 -08:00
parent a316253b94
commit e258875d4b
4 changed files with 55 additions and 34 deletions

View File

@ -4,12 +4,7 @@
#[macro_export] #[macro_export]
macro_rules! get_instance_function { macro_rules! get_instance_function {
($instance:expr, $func_index:expr) => {{ ($instance:expr, $func_index:expr) => {{
use crate::sighandler::install_sighandler;
use std::mem; use std::mem;
unsafe {
install_sighandler();
};
let func_addr = $instance.get_function_pointer($func_index); let func_addr = $instance.get_function_pointer($func_index);
unsafe { mem::transmute(func_addr) } unsafe { mem::transmute(func_addr) }
}}; }};

View File

@ -25,9 +25,10 @@ use structopt::StructOpt;
#[macro_use] #[macro_use]
mod macros; mod macros;
#[macro_use]
mod recovery;
pub mod apis; pub mod apis;
pub mod common; pub mod common;
mod recovery;
pub mod sighandler; pub mod sighandler;
#[cfg(test)] #[cfg(test)]
mod spectests; mod spectests;
@ -86,7 +87,7 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> {
}; };
let main: extern "C" fn(u32, u32, &webassembly::Instance) = let main: extern "C" fn(u32, u32, &webassembly::Instance) =
get_instance_function!(instance, func_index); get_instance_function!(instance, func_index);
main(0, 0, &instance); return call_protected!(main(0, 0, &instance)).map_err(|err| format!("{}", err));
} else { } else {
let func_index = let func_index =
instance instance
@ -95,10 +96,10 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> {
Some(&webassembly::Export::Function(index)) => index, Some(&webassembly::Export::Function(index)) => index,
_ => panic!("Main function not found"), _ => panic!("Main function not found"),
}); });
instance.start_func(func_index).unwrap(); let main: extern "C" fn(&webassembly::Instance) =
get_instance_function!(instance, func_index);
return call_protected!(main(&instance)).map_err(|err| format!("{}", err));
} }
Ok(())
} }
fn run(options: Run) { fn run(options: Run) {

View File

@ -5,35 +5,63 @@
//! unless you have memory unsafety elsewhere in your code. //! unless you have memory unsafety elsewhere in your code.
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
use nix::sys::signal::{Signal, SIGFPE, SIGILL, SIGSEGV, SIGBUS};
use super::webassembly::ErrorKind;
use super::sighandler::install_sighandler;
extern "C" { extern "C" {
fn setjmp(env: *mut ::nix::libc::c_void) -> ::nix::libc::c_int; pub fn setjmp(env: *mut ::nix::libc::c_void) -> ::nix::libc::c_int;
fn longjmp(env: *mut ::nix::libc::c_void, val: ::nix::libc::c_int) -> !; fn longjmp(env: *mut ::nix::libc::c_void, val: ::nix::libc::c_int) -> !;
} }
const SETJMP_BUFFER_LEN: usize = 27; const SETJMP_BUFFER_LEN: usize = 27;
thread_local! { thread_local! {
static SETJMP_BUFFER: UnsafeCell<[::nix::libc::c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]); pub static SETJMP_BUFFER: UnsafeCell<[::nix::libc::c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]);
} }
// We need a macro since the arguments we will provide to the funciton
// (and the return value) are not fixed to just one case: f(x) -> y
// but multiple: f(x) -> y, f(a,b) -> c, ...
// And right now it's impossible to handle with Rust function type system
/// Calls a WebAssembly function with longjmp receiver installed. If a non-WebAssembly function is passed in, /// Calls a WebAssembly function with longjmp receiver installed. If a non-WebAssembly function is passed in,
/// the behavior of protected_call is undefined. /// the behavior of call_protected is undefined.
pub unsafe fn protected_call<T, R>(f: fn(T) -> R, p: T) -> Result<R, i32> { #[macro_export]
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); macro_rules! call_protected {
let prev_jmp_buf = *jmp_buf; ($x:expr) => {unsafe {
use crate::webassembly::ErrorKind;
use crate::recovery::{SETJMP_BUFFER, setjmp};
use crate::sighandler::install_sighandler;
let signum = setjmp(jmp_buf as *mut ::nix::libc::c_void); use nix::sys::signal::{Signal, SIGFPE, SIGILL, SIGSEGV, SIGBUS};
if signum != 0 {
*jmp_buf = prev_jmp_buf; let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
Err(signum) let prev_jmp_buf = *jmp_buf;
} else {
let ret = f(p); // TODO: Switch stack? install_sighandler();
*jmp_buf = prev_jmp_buf;
Ok(ret) let signum = setjmp(jmp_buf as *mut ::nix::libc::c_void);
} if signum != 0 {
*jmp_buf = prev_jmp_buf;
let signal = match Signal::from_c_int(signum) {
Ok(SIGFPE) => "floating-point exception",
Ok(SIGILL) => "illegal instruction",
Ok(SIGSEGV) => "segmentation violation",
Ok(SIGBUS) => "bus error",
Err(_) => "error while getting the Signal",
_ => "unkown trapped signal",
};
Err(ErrorKind::RuntimeError(format!("trap - {}", signal)))
} else {
let ret = $x; // TODO: Switch stack?
*jmp_buf = prev_jmp_buf;
Ok(ret)
}
}}
} }
/// Unwinds to last protected_call. /// Unwinds to last protected_call.
pub unsafe fn do_unwind(signum: i32) -> ! { pub unsafe fn do_unwind(signum: i32) -> ! {
// 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.)

View File

@ -519,16 +519,13 @@ impl Instance {
get_function_addr(&func_index, &self.import_functions, &self.functions) get_function_addr(&func_index, &self.import_functions, &self.functions)
} }
pub fn start_func(&self, func_index: FuncIndex) -> Result<(), i32> { pub fn start(&self) -> Result<(), ErrorKind> {
let func: fn(&Instance) = get_instance_function!(&self, func_index);
unsafe { recovery::protected_call(func, self) }
}
pub fn start(&self) -> Result<(), i32> {
if let Some(func_index) = self.start_func { if let Some(func_index) = self.start_func {
self.start_func(func_index) let func: fn(&Instance) = get_instance_function!(&self, func_index);
} else { call_protected!(func(self))
panic!("start func not found") }
else {
Ok(())
} }
} }