Skip to content

Commit

Permalink
Cherry-pick bytecodealliance#3063 to dfinity/v0.26
Browse files Browse the repository at this point in the history
This patch is taken from
bytecodealliance#3079
which in turn a cherry-pick of bytecodealliance#3063 adjusted for v0.26:
bytecodealliance#3063

- Restore POSIX signal handling on MacOS behind a feature flag

As described in Issue bytecodealliance#3052, the switch to Mach Exception handling
removed unix::StoreExt from the public API of crate on MacOS.
That is a breaking change and makes it difficult for some
application to upgrade to the current stable Wasmtime.

As a workaround this PR introduces a feature flag called
posix-signals-on-macos that restores the old behaviour on MacOS.
The flag is disabled by default.

- Fix test guard

- Fix formatting in the test
  • Loading branch information
ulan committed Jul 15, 2021
1 parent 77fb6a2 commit 60d36d9
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 5 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ wasi-crypto = ["wasmtime-wasi-crypto"]
wasi-nn = ["wasmtime-wasi-nn"]
uffd = ["wasmtime/uffd"]
all-arch = ["wasmtime/all-arch"]
posix-signals-on-macos = ["wasmtime/posix-signals-on-macos"]

# Stub feature that does nothing, for Cargo-features compatibility: the new
# backend is the default now.
Expand Down
5 changes: 5 additions & 0 deletions crates/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@ async = ["wasmtime-fiber"]

# Enables support for userfaultfd in the pooling allocator when building on Linux
uffd = ["userfaultfd"]

# Enables trap handling using POSIX signals instead of Mach exceptions on MacOS.
# It is useful for applications that do not bind their own exception ports and
# need portable signal handling.
posix-signals-on-macos = []
2 changes: 1 addition & 1 deletion crates/runtime/src/traphandlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extern "C" {
}

cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
if #[cfg(all(target_os = "macos", not(feature = "posix-signals-on-macos")))] {
mod macos;
use macos as sys;
} else if #[cfg(unix)] {
Expand Down
86 changes: 84 additions & 2 deletions crates/runtime/src/traphandlers/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ pub unsafe fn platform_init() {
}

// On ARM, handle Unaligned Accesses.
if cfg!(target_arch = "arm") || cfg!(target_os = "freebsd") {
// On Darwin, guard page accesses are raised as SIGBUS.
if cfg!(target_arch = "arm") || cfg!(target_os = "macos") || cfg!(target_os = "freebsd") {
register(&mut PREV_SIGBUS, libc::SIGBUS);
}
}
Expand Down Expand Up @@ -99,7 +100,44 @@ unsafe extern "C" fn trap_handler(
return true;
}
info.capture_backtrace(pc);
Unwind(jmp_buf)
// on macOS this is a bit special, unfortunately. If we were to
// `siglongjmp` out of the signal handler that notably does
// *not* reset the sigaltstack state of our signal handler. This
// seems to trick the kernel into thinking that the sigaltstack
// is still in use upon delivery of the next signal, meaning
// that the sigaltstack is not ever used again if we immediately
// call `Unwind` here.
//
// Note that if we use `longjmp` instead of `siglongjmp` then
// the problem is fixed. The problem with that, however, is that
// `setjmp` is much slower than `sigsetjmp` due to the
// preservation of the proceses signal mask. The reason
// `longjmp` appears to work is that it seems to call a function
// (according to published macOS sources) called
// `_sigunaltstack` which updates the kernel to say the
// sigaltstack is no longer in use. We ideally want to call that
// here but I don't think there's a stable way for us to call
// that.
//
// Given all that, on macOS only, we do the next best thing. We
// return from the signal handler after updating the register
// context. This will cause control to return to our
// `unwind_shim` function defined here which will perform the
// `Unwind` (`siglongjmp`) for us. The reason this works is that
// by returning from the signal handler we'll trigger all the
// normal machinery for "the signal handler is done running"
// which will clear the sigaltstack flag and allow reusing it
// for the next signal. Then upon resuming in our custom code we
// blow away the stack anyway with a longjmp.
if cfg!(target_os = "macos") {
unsafe extern "C" fn unwind_shim(jmp_buf: *const u8) {
Unwind(jmp_buf)
}
set_pc(context, unwind_shim as usize, jmp_buf as usize);
return true;
} else {
Unwind(jmp_buf)
}
});

if handled {
Expand Down Expand Up @@ -138,6 +176,15 @@ unsafe fn get_pc(cx: *mut libc::c_void) -> *const u8 {
} else if #[cfg(all(any(target_os = "linux", target_os = "android"), target_arch = "aarch64"))] {
let cx = &*(cx as *const libc::ucontext_t);
cx.uc_mcontext.pc as *const u8
} else if #[cfg(all(target_os = "macos", target_arch = "x86_64"))] {
let cx = &*(cx as *const libc::ucontext_t);
(*cx.uc_mcontext).__ss.__rip as *const u8
} else if #[cfg(all(target_os = "macos", target_arch = "x86"))] {
let cx = &*(cx as *const libc::ucontext_t);
(*cx.uc_mcontext).__ss.__eip as *const u8
} else if #[cfg(all(target_os = "macos", target_arch = "aarch64"))] {
let cx = &*(cx as *const libc::ucontext_t);
(*cx.uc_mcontext).__ss.__pc as *const u8
} else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] {
let cx = &*(cx as *const libc::ucontext_t);
cx.uc_mcontext.mc_rip as *const u8
Expand All @@ -147,6 +194,41 @@ unsafe fn get_pc(cx: *mut libc::c_void) -> *const u8 {
}
}

// This is only used on macOS targets for calling an unwinding shim
// function to ensure that we return from the signal handler.
//
// See more comments above where this is called for what it's doing.
unsafe fn set_pc(cx: *mut libc::c_void, pc: usize, arg1: usize) {
cfg_if::cfg_if! {
if #[cfg(not(target_os = "macos"))] {
drop((cx, pc, arg1));
unreachable!(); // not used on these platforms
} else if #[cfg(target_arch = "x86_64")] {
let cx = &mut *(cx as *mut libc::ucontext_t);
(*cx.uc_mcontext).__ss.__rip = pc as u64;
(*cx.uc_mcontext).__ss.__rdi = arg1 as u64;
// We're simulating a "pseudo-call" so we need to ensure
// stack alignment is properly respected, notably that on a
// `call` instruction the stack is 8/16-byte aligned, then
// the function adjusts itself to be 16-byte aligned.
//
// Most of the time the stack pointer is 16-byte aligned at
// the time of the trap but for more robust-ness with JIT
// code where it may ud2 in a prologue check before the
// stack is aligned we double-check here.
if (*cx.uc_mcontext).__ss.__rsp % 16 == 0 {
(*cx.uc_mcontext).__ss.__rsp -= 8;
}
} else if #[cfg(target_arch = "aarch64")] {
let cx = &mut *(cx as *mut libc::ucontext_t);
(*cx.uc_mcontext).__ss.__pc = pc as u64;
(*cx.uc_mcontext).__ss.__x[0] = arg1 as u64;
} else {
compile_error!("unsupported macos target architecture");
}
}
}

/// A function for registering a custom alternate signal stack (sigaltstack).
///
/// Rust's libstd installs an alternate stack with size `SIGSTKSZ`, which is not
Expand Down
5 changes: 5 additions & 0 deletions crates/wasmtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,8 @@ uffd = ["wasmtime-runtime/uffd"]

# Enables support for all architectures in JIT and the `wasmtime compile` CLI command.
all-arch = ["wasmtime-jit/all-arch"]

# Enables trap handling using POSIX signals instead of Mach exceptions on MacOS.
# It is useful for applications that do not bind their own exception ports and
# need portable signal handling.
posix-signals-on-macos = ["wasmtime-runtime/posix-signals-on-macos"]
2 changes: 1 addition & 1 deletion crates/wasmtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ pub use crate::types::*;
pub use crate::values::*;

cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
if #[cfg(all(target_os = "macos", not(feature = "posix-signals-on-macos")))] {
// no extensions for macOS at this time
} else if #[cfg(unix)] {
pub mod unix;
Expand Down
5 changes: 4 additions & 1 deletion tests/all/custom_signal_handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#[cfg(target_os = "linux")]
#[cfg(any(
target_os = "linux",
all(target_os = "macos", feature = "posix-signals-on-macos")
))]
mod tests {
use anyhow::Result;
use std::rc::Rc;
Expand Down

0 comments on commit 60d36d9

Please sign in to comment.