add capability to invoke previously registered handlers

This commit is contained in:
vms 2019-09-28 17:49:57 +03:00
parent 67ae86bdbd
commit ca0054f2a8

View File

@ -12,9 +12,7 @@
use crate::relocation::{TrapCode, TrapData}; use crate::relocation::{TrapCode, TrapData};
use crate::signal::{CallProtError, HandlerData}; use crate::signal::{CallProtError, HandlerData};
use libc::{c_int, c_void, siginfo_t}; use libc::{c_int, c_void, siginfo_t};
use nix::sys::signal::{ use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV};
sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV,
};
use std::cell::{Cell, UnsafeCell}; use std::cell::{Cell, UnsafeCell};
use std::ptr; use std::ptr;
use std::sync::Once; use std::sync::Once;
@ -35,16 +33,22 @@ extern "C" {
fn longjmp(env: *mut c_void, val: c_int) -> !; fn longjmp(env: *mut c_void, val: c_int) -> !;
} }
static mut SIGFPE_SYS_HANDLER: Option<SigAction> = None;
static mut SIGILL_SYS_HANDLER: Option<SigAction> = None;
static mut SIGSEGV_SYS_HANDLER: Option<SigAction> = None;
static mut SIGBUS_SYS_HANDLER: Option<SigAction> = None;
pub unsafe fn install_sighandler() { pub unsafe fn install_sighandler() {
let sa = SigAction::new( let sa = SigAction::new(
SigHandler::SigAction(signal_trap_handler), SigHandler::SigAction(signal_trap_handler),
SaFlags::SA_ONSTACK, SaFlags::SA_ONSTACK,
SigSet::empty(), SigSet::empty(),
); );
sigaction(SIGFPE, &sa).unwrap();
sigaction(SIGILL, &sa).unwrap(); SIGFPE_SYS_HANDLER = sigaction(SIGFPE, &sa).ok();
sigaction(SIGSEGV, &sa).unwrap(); SIGILL_SYS_HANDLER = sigaction(SIGILL, &sa).ok();
sigaction(SIGBUS, &sa).unwrap(); SIGSEGV_SYS_HANDLER = sigaction(SIGSEGV, &sa).ok();
SIGBUS_SYS_HANDLER = sigaction(SIGBUS, &sa).ok();
} }
const SETJMP_BUFFER_LEN: usize = 27; const SETJMP_BUFFER_LEN: usize = 27;
@ -123,14 +127,48 @@ pub fn call_protected<T>(
} }
/// Unwinds to last protected_call. /// Unwinds to last protected_call.
pub unsafe fn do_unwind(signum: i32, siginfo: *const c_void, ucontext: *const c_void) -> ! { pub unsafe fn do_unwind(signum: i32, siginfo: *mut c_void, ucontext: *mut 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.
// Calls given handler for specified signal.
unsafe fn call_signal_handler(sig: Signal, siginfo: *mut c_void, ucontext: *mut c_void, sig_action: &SigAction) {
match sig_action.handler() {
SigHandler::SigDfl => {
sigaction(sig, sig_action).unwrap();
return
},
SigHandler::SigIgn => return,
SigHandler::Handler(handler) => handler(sig as _),
SigHandler::SigAction(handler) => handler(sig as _, siginfo as _, ucontext),
}
}
let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get()); let jmp_buf = SETJMP_BUFFER.with(|buf| buf.get());
if *jmp_buf == [0; SETJMP_BUFFER_LEN] { if *jmp_buf == [0; SETJMP_BUFFER_LEN] {
::std::process::abort(); let sig = Signal::from_c_int(signum).unwrap_or_else(|_| ::std::process::abort());
match sig {
// just abort if the previous handler hasn't been set
SIGFPE => SIGFPE_SYS_HANDLER.map_or_else(
|| ::std::process::abort(),
|prev_handler| call_signal_handler( SIGFPE as _, siginfo, ucontext, &prev_handler)
),
SIGILL => SIGILL_SYS_HANDLER.map_or_else(
|| ::std::process::abort(),
|prev_handler| call_signal_handler( SIGILL as _, siginfo, ucontext, &prev_handler)
),
SIGSEGV => SIGSEGV_SYS_HANDLER.map_or_else(
|| ::std::process::abort(),
|prev_handler| call_signal_handler( SIGSEGV as _, siginfo, ucontext, &prev_handler)
),
SIGBUS => SIGBUS_SYS_HANDLER.map_or_else(
|| ::std::process::abort(),
|prev_handler| call_signal_handler( SIGBUS as _, siginfo, ucontext, &prev_handler)
),
_ => ::std::process::abort(),
}
return;
} }
CAUGHT_ADDRESSES.with(|cell| cell.set(get_faulting_addr_and_ip(siginfo, ucontext))); CAUGHT_ADDRESSES.with(|cell| cell.set(get_faulting_addr_and_ip(siginfo, ucontext)));