testing: Trigger a crash more reliably when panicking and hard fail is active (#32947)

Before when handling panics and hard-fail was activated, Servo would
just exit with an error return code. This isn't interpreted as a crash
by the WPT test runner. This change raises a SEGV signal instead, which
means that panics should more reliably be treated as crashes.

This doesn't seem to change any test results, at least any non-flaky
test results. It is necessary for the test in #32782 to work though.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2024-08-06 19:43:06 +02:00 committed by GitHub
parent 89d20fc401
commit c9fbe018f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 30 additions and 18 deletions

View file

@ -11,8 +11,6 @@ pub fn install() {
use std::sync::atomic;
use std::thread;
use sig::ffi::Sig;
use crate::backtrace;
extern "C" fn handler(sig: i32) {
@ -42,21 +40,31 @@ pub fn install() {
// 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
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(sig, &action, std::ptr::null_mut());
libc::raise(sig);
}
raise_signal_or_exit_with_error(sig);
}
signal!(Sig::SEGV, handler); // handle segfaults
signal!(Sig::ILL, handler); // handle stack overflow and unsupported CPUs
signal!(Sig::IOT, handler); // handle double panics
signal!(Sig::BUS, handler); // handle invalid memory access
signal!(libc::SIGSEGV, handler); // handle segfaults
signal!(libc::SIGILL, handler); // handle stack overflow and unsupported CPUs
signal!(libc::SIGIOT, handler); // handle double panics
signal!(libc::SIGBUS, handler); // handle invalid memory access
}
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
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);
}
}

View file

@ -9,6 +9,8 @@ use std::{env, thread};
use log::{error, warn};
use servo::config::opts;
use crate::crash_handler::raise_signal_or_exit_with_error;
pub(crate) fn panic_hook(info: &PanicInfo) {
warn!("Panic hook called.");
let msg = match info.payload().downcast_ref::<&'static str>() {
@ -40,7 +42,9 @@ pub(crate) fn panic_hook(info: &PanicInfo) {
drop(stderr);
if opts::get().hard_fail && !opts::get().multiprocess {
std::process::exit(1);
// When we are exiting due to a hard-failure mode, we trigger a segfault so that crash
// tests detect that we crashed. If we exit normally it just looks like a non-crash exit.
raise_signal_or_exit_with_error(libc::SIGSEGV);
}
error!("{}", msg);