Files
servo/ports/servoshell/crash_handler.rs
Narfinger 8a38c5e217 servoshell: Port from sig to signal_hook_registry (#43891)
Testing: We do not currently have a way to test signal handling in the
servoshell binary, so this change does not include tests.
Fixes: #43836

---------

Signed-off-by: Narfinger <Narfinger@users.noreply.github.com>
2026-04-07 08:28:36 +00:00

79 lines
3.6 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "android")))]
pub fn install() {}
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn install() {
use std::io::Write;
use std::sync::atomic;
use std::thread;
use libc::siginfo_t;
use crate::backtrace;
fn handler(siginfo: &siginfo_t) {
// Only print crash message and backtrace the first time, to avoid
// infinite recursion if the printing causes another signal.
static BEEN_HERE_BEFORE: atomic::AtomicBool = atomic::AtomicBool::new(false);
if !BEEN_HERE_BEFORE.swap(true, atomic::Ordering::SeqCst) {
// stderr is unbuffered, so we wont lose output if we crash later
// in this handler, and the std::io::stderr() call never allocates.
// std::io::stdout() allocates the first time its called, which in
// practice will often segfault (see below).
let stderr = std::io::stderr();
let mut stderr = stderr.lock();
let _ = write!(&mut stderr, "Caught signal {}", siginfo.si_signo);
if let Some(name) = thread::current().name() {
let _ = write!(&mut stderr, " in thread \"{}\"", name);
}
let _ = writeln!(&mut stderr);
// This call always allocates, which in practice will segfault if
// were handling a non-main-thread (e.g. layout) segfault. Strictly
// speaking in POSIX terms, this is also undefined behaviour.
let _ = backtrace::print(&mut stderr);
}
// Outside the BEEN_HERE_BEFORE check, we must only call functions we
// know to be “async-signal-safe”, which includes sigaction(), raise(),
// and _exit(), but generally doesnt include anything that allocates.
// https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03_03
raise_signal_or_exit_with_error(siginfo.si_signo);
}
unsafe {
signal_hook_registry::register_unchecked(libc::SIGSEGV, handler)
.expect("Could not register SIGSEGV handler");
signal_hook_registry::register_unchecked(libc::SIGILL, handler)
.expect("Could not register SIGILL handler");
signal_hook_registry::register_unchecked(libc::SIGIOT, handler)
.expect("Could not register SIGIOT handler");
signal_hook_registry::register_unchecked(libc::SIGBUS, handler)
.expect("Could not register SIGBUS handler");
}
}
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "android")))]
pub(crate) fn raise_signal_or_exit_with_error(_signal: i32) {
std::process::exit(1);
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub(crate) fn raise_signal_or_exit_with_error(signal: i32) {
unsafe {
// Reset the signal to the default action, and reraise the signal.
// Unlike libc::_exit(sig), which terminates the process normally,
// this terminates abnormally just like an uncaught signal, allowing
// mach (or your shell) to distinguish it from an ordinary exit, and
// allows your kernel to make a core dump if configured to do so.
let mut action: libc::sigaction = std::mem::zeroed();
action.sa_sigaction = libc::SIG_DFL;
libc::sigaction(signal, &action, std::ptr::null_mut());
libc::raise(signal);
}
}