ExceptionHandlerSetup: chain signals we don't handle instead of returning

The POSIX handler returned early for any si_code other than SEGV_MAPERR/SEGV_ACCERR,
without passing the signal on. Returning from a fault handler re-executes the
faulting instruction, so anything that keeps faulting - an MTE fault on Android
arm64, a protection-key or shadow-stack fault - became a livelock at 100% CPU rather
than a crash with a usable report. It also swallowed SIGSEGV sent via kill()
(si_code SI_USER), and meant whatever handler was installed before us, such as a
crash reporter, never ran.

The code to chain properly was already there, just inside the 'we couldn't handle
this address' branch further down - factored out and used for both.
This commit is contained in:
Henrik Rydgård
2026-08-31 12:56:01 +02:00
parent 820c6e0134
commit c8e0a35808
+28 -21
View File
@@ -312,6 +312,28 @@ static struct sigaction old_sa_bus;
static stack_t old_signal_stack{};
static bool old_signal_stack_valid = false;
// Hand the signal on to whatever was installed before us. Returning from a fault handler just
// re-runs the faulting instruction, so for anything we can't deal with this is the only exit
// that isn't an infinite loop.
static void ChainToPreviousHandler(int sig, siginfo_t *info, void *raw_context) {
struct sigaction *old_sa = sig == SIGSEGV ? &old_sa_segv : &old_sa_bus;
// Per the sigaction man page: with SA_SIGINFO it's sa_sigaction, otherwise sa_handler is
// SIG_DFL, SIG_IGN, or a handler pointer.
if (old_sa->sa_flags & SA_SIGINFO) {
old_sa->sa_sigaction(sig, info, raw_context);
return;
}
if (old_sa->sa_handler == SIG_DFL) {
signal(sig, SIG_DFL);
return;
}
if (old_sa->sa_handler == SIG_IGN) {
// Ignore signal
return;
}
old_sa->sa_handler(sig);
}
static void sigsegv_handler(int sig, siginfo_t* info, void* raw_context) {
if (sig != SIGSEGV && sig != SIGBUS) {
// We are not interested in other signals - handle it as usual.
@@ -320,7 +342,11 @@ static void sigsegv_handler(int sig, siginfo_t* info, void* raw_context) {
ucontext_t* context = (ucontext_t*)raw_context;
int sicode = info->si_code;
if (sicode != SEGV_MAPERR && sicode != SEGV_ACCERR) {
// Huh? Return.
// Not an address fault we can do anything with - an MTE, protection-key or shadow
// stack fault, or a signal sent with kill(). Returning here would re-run the
// faulting instruction forever at 100% CPU, and would also swallow the signal from
// whatever was installed before us (a crash reporter, say).
ChainToPreviousHandler(sig, info, raw_context);
return;
}
uintptr_t bad_address = (uintptr_t)info->si_addr;
@@ -346,26 +372,7 @@ static void sigsegv_handler(int sig, siginfo_t* info, void* raw_context) {
// SIG_IGN: The signal is ignored
// Any other value is a function pointer to a signal handler
struct sigaction* old_sa;
if (sig == SIGSEGV) {
old_sa = &old_sa_segv;
} else {
old_sa = &old_sa_bus;
}
if (old_sa->sa_flags & SA_SIGINFO) {
old_sa->sa_sigaction(sig, info, raw_context);
return;
}
if (old_sa->sa_handler == SIG_DFL) {
signal(sig, SIG_DFL);
return;
}
if (old_sa->sa_handler == SIG_IGN) {
// Ignore signal
return;
}
old_sa->sa_handler(sig);
ChainToPreviousHandler(sig, info, raw_context);
}
}