From daf46b1dbcb15bbab64e3088b4ab5256c8219d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 25 Oct 2026 19:24:59 +0100 Subject: [PATCH] Don't set up a signal alt stack in the memory fault handler sigaltstack() is per thread. InstallExceptionHandler() runs in CPU_Init() on the loading thread, which exits after boot, while the faults come from the CPU thread and UninstallExceptionHandler() runs on the emu thread. So our stack was never used, and the uninstall "restored" the loading thread's previous alt stack onto the emu thread. On Android that's the one bionic gives each thread and frees when the thread exits, so every later fault on the emu thread ran its handler on freed memory. A Play crash report shows scudo finding a corrupted chunk header when freeing our stack in UninstallExceptionHandler(). The alt stack came with the Dolphin code, which needs it to handle its own stack overflows. Ours only handles guest memory faults, so let the handler run on the faulting thread's stack, or the alt stack that thread already has. This also leaves a libretro frontend's alt stack alone. Co-Authored-By: Claude Opus 5.5 (1M context) --- Common/ExceptionHandlerSetup.cpp | 49 +++----------------------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/Common/ExceptionHandlerSetup.cpp b/Common/ExceptionHandlerSetup.cpp index 282bca8c54..f267095988 100644 --- a/Common/ExceptionHandlerSetup.cpp +++ b/Common/ExceptionHandlerSetup.cpp @@ -24,7 +24,6 @@ #endif static BadAccessHandler g_badAccessHandler; -static void *altStack = nullptr; void SetupCRT(bool suppressDialogs) { #if defined(_MSC_VER) @@ -190,8 +189,6 @@ void UninstallExceptionHandler() { static struct sigaction old_sa_segv; 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 @@ -265,33 +262,11 @@ void InstallExceptionHandler(BadAccessHandler badAccessHandler, bool logStackTra } g_badAccessHandler = badAccessHandler; -#if PPSSPP_PLATFORM(TVOS) - // tvOS has no sigaltstack, so the handler runs on the faulting thread's stack. - INFO_LOG(Log::System, "Installed exception handler (no alt stack)."); -#else - size_t altStackSize = SIGSTKSZ; + // No sigaltstack of our own: it's per thread, and this runs on the loading thread, while the faults + // come from the CPU thread. The handler runs on the faulting thread's stack, or on its alternate + // stack if it has one (bionic gives every thread one). + INFO_LOG(Log::System, "Installed exception handler"); - // Add some extra room. - altStackSize += 65536; - - INFO_LOG(Log::System, "Installed exception handler. stack size: %d", (int)altStackSize); - - stack_t signal_stack{}; - if (sigaltstack(nullptr, &old_signal_stack) == 0) { - old_signal_stack_valid = true; - } - altStack = malloc(altStackSize); -#ifdef __FreeBSD__ - signal_stack.ss_sp = (char*)altStack; -#else - signal_stack.ss_sp = altStack; -#endif - signal_stack.ss_size = altStackSize; - signal_stack.ss_flags = 0; - if (sigaltstack(&signal_stack, nullptr)) { - _assert_msg_(false, "sigaltstack failed"); - } -#endif struct sigaction sa{}; sa.sa_handler = nullptr; sa.sa_sigaction = &sigsegv_handler; @@ -307,22 +282,6 @@ void UninstallExceptionHandler() { if (!g_badAccessHandler) { return; } -#if !PPSSPP_PLATFORM(TVOS) - if (old_signal_stack_valid) { - // Darwin fails with ENOMEM on a size below MINSIGSTKSZ, even with SS_DISABLE. - if ((old_signal_stack.ss_flags & SS_DISABLE) && old_signal_stack.ss_size < MINSIGSTKSZ) { - old_signal_stack.ss_size = MINSIGSTKSZ; - } - if (0 != sigaltstack(&old_signal_stack, nullptr)) { - ERROR_LOG(Log::System, "Could not restore previous signal altstack"); - } - old_signal_stack_valid = false; - } -#endif - if (altStack) { - free(altStack); - altStack = nullptr; - } sigaction(SIGSEGV, &old_sa_segv, nullptr); #ifdef __APPLE__ sigaction(SIGBUS, &old_sa_bus, nullptr);