diff --git a/src/recovery.rs b/src/recovery.rs index 11c1dea82..65cccb6b0 100644 --- a/src/recovery.rs +++ b/src/recovery.rs @@ -1,4 +1,8 @@ //! When a WebAssembly module triggers any traps, we perform recovery here. +//! +//! This module uses TLS (thread-local storage) to track recovery information. Since the four signals we're handling +//! are very special, the async signal unsafety of Rust's TLS implementation generally does not affect the correctness here +//! unless you have memory unsafety elsewhere in your code. use std::cell::UnsafeCell; @@ -13,8 +17,8 @@ thread_local! { static SETJMP_BUFFER: UnsafeCell<[::nix::libc::c_int; SETJMP_BUFFER_LEN]> = UnsafeCell::new([0; SETJMP_BUFFER_LEN]); } -/// Calls a function with longjmp receiver installed. The function must be compiled from WebAssembly; -/// Otherwise, the behavior is undefined. +/// 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; @@ -24,7 +28,7 @@ pub unsafe fn protected_call(f: fn(T) -> R, p: T) -> Result { *jmp_buf = prev_jmp_buf; Err(signum) } else { - let ret = f(p); + let ret = f(p); // TODO: Switch stack? *jmp_buf = prev_jmp_buf; Ok(ret) } @@ -32,6 +36,10 @@ pub unsafe fn protected_call(f: fn(T) -> R, p: T) -> Result { /// 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.) + // 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. + let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); if *jmp_buf == [0; SETJMP_BUFFER_LEN] { ::std::process::abort();