From c8e0a35808b49b2a53bb24cbb30557a05454c506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 31 Aug 2026 12:56:01 +0200 Subject: [PATCH] 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. --- Common/ExceptionHandlerSetup.cpp | 49 ++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/Common/ExceptionHandlerSetup.cpp b/Common/ExceptionHandlerSetup.cpp index e26b9c7d85..2cdb36a87f 100644 --- a/Common/ExceptionHandlerSetup.cpp +++ b/Common/ExceptionHandlerSetup.cpp @@ -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); } }