mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-04 03:35:19 +02:00
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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user