diff --git a/src/macros.rs b/src/macros.rs index 94f7dfe61..dddfd57fe 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -4,12 +4,7 @@ #[macro_export] macro_rules! get_instance_function { ($instance:expr, $func_index:expr) => {{ - use crate::sighandler::install_sighandler; use std::mem; - - unsafe { - install_sighandler(); - }; let func_addr = $instance.get_function_pointer($func_index); unsafe { mem::transmute(func_addr) } }}; diff --git a/src/main.rs b/src/main.rs index 73acf367c..d68417f94 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,9 +25,10 @@ use structopt::StructOpt; #[macro_use] mod macros; +#[macro_use] +mod recovery; pub mod apis; pub mod common; -mod recovery; pub mod sighandler; #[cfg(test)] mod spectests; @@ -86,7 +87,7 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> { }; let main: extern "C" fn(u32, u32, &webassembly::Instance) = get_instance_function!(instance, func_index); - main(0, 0, &instance); + return call_protected!(main(0, 0, &instance)).map_err(|err| format!("{}", err)); } else { let func_index = instance @@ -95,10 +96,10 @@ fn execute_wasm(wasm_path: PathBuf) -> Result<(), String> { Some(&webassembly::Export::Function(index)) => index, _ => 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) { diff --git a/src/recovery.rs b/src/recovery.rs index 65cccb6b0..b685bf12d 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -5,35 +5,63 @@ //! unless you have memory unsafety elsewhere in your code. use std::cell::UnsafeCell; +use nix::sys::signal::{Signal, SIGFPE, SIGILL, SIGSEGV, SIGBUS}; +use super::webassembly::ErrorKind; +use super::sighandler::install_sighandler; 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) -> !; } const SETJMP_BUFFER_LEN: usize = 27; 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, -/// the behavior of protected_call is undefined. -pub unsafe fn protected_call(f: fn(T) -> R, p: T) -> Result { - let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); - let prev_jmp_buf = *jmp_buf; +/// the behavior of call_protected is undefined. +#[macro_export] +macro_rules! call_protected { + ($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); - if signum != 0 { - *jmp_buf = prev_jmp_buf; - Err(signum) - } else { - let ret = f(p); // TODO: Switch stack? - *jmp_buf = prev_jmp_buf; - Ok(ret) - } + use nix::sys::signal::{Signal, SIGFPE, SIGILL, SIGSEGV, SIGBUS}; + + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); + let prev_jmp_buf = *jmp_buf; + + install_sighandler(); + + 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. 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.) diff --git a/src/webassembly/instance.rs b/src/webassembly/instance.rs index b680bc1a2..cd816632d 100644 --- a/src/webassembly/instance.rs +++ b/src/webassembly/instance.rs @@ -519,16 +519,13 @@ impl Instance { get_function_addr(&func_index, &self.import_functions, &self.functions) } - pub fn start_func(&self, func_index: FuncIndex) -> Result<(), i32> { - let func: fn(&Instance) = get_instance_function!(&self, func_index); - unsafe { recovery::protected_call(func, self) } - } - - pub fn start(&self) -> Result<(), i32> { + pub fn start(&self) -> Result<(), ErrorKind> { if let Some(func_index) = self.start_func { - self.start_func(func_index) - } else { - panic!("start func not found") + let func: fn(&Instance) = get_instance_function!(&self, func_index); + call_protected!(func(self)) + } + else { + Ok(()) } }