From ce7f233d59eed0a85ded0700c38bd63051b796c0 Mon Sep 17 00:00:00 2001 From: finn Date: Wed, 5 Aug 2026 01:03:28 +0900 Subject: [PATCH] fix: don't reset the original terminate handler when the C++ exception monitor is disabled For fatal C++ exceptions, CPPExceptionTerminate() calls sentrycrashcm_handleException(), which disables all monitors and thereby runs this monitor's setEnabled(false), resetting g_originalTerminateHandler to NULL. The subsequent sentrycrashcm_cppexception_callOriginalTerminationHandler() then becomes a no-op, so any terminate handler installed before the SDK (e.g. Kotlin/Native's unhandled-exception reporter) never runs, and libc++abi aborts with "terminate_handler unexpectedly returned". The same applies when an exception is thrown while the monitor is enabled but the monitor is disabled before the exception terminates the process: libc++abi stores the terminate handler per exception at throw time, so CPPExceptionTerminate can still run after setEnabled(false). Keep g_originalTerminateHandler when disabling the monitor, matching upstream KSCrash behavior. setEnabled(true) overwrites it anyway, and callOriginalTerminationHandler() already guards against NULL for the never-enabled case. --- CHANGELOG.md | 1 + .../Recording/Monitors/SentryCrashMonitor_CPPException.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75db97997dd..882dae501ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ### Fixes +- Call the original terminate handler even after fatal C++ exception handling disables monitors, instead of aborting with "terminate_handler unexpectedly returned" (#8663) - Reduce memory usage when storing envelopes with large attachments (#8649) - Fix incorrect `duration` sent for active sessions (#8612) - Session `duration` is now set only when the session ends. Active sessions (including on error increments) no longer emit a bogus `duration`. diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp index 80ee40a6119..fd022b7fc95 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_CPPException.cpp @@ -370,7 +370,12 @@ setEnabled(bool isEnabled) g_originalTerminateHandler = std::set_terminate(CPPExceptionTerminate); } else { std::set_terminate(g_originalTerminateHandler); - g_originalTerminateHandler = NULL; + // Keep g_originalTerminateHandler: CPPExceptionTerminate can still run after the + // monitor is disabled, because libc++abi stores the terminate handler per exception + // at throw time, and sentrycrashcm_handleException() disables all monitors while + // handling a fatal exception. Resetting the handler to NULL here would make + // CPPExceptionTerminate return without calling the original handler, causing + // libc++abi to abort with "terminate_handler unexpectedly returned". // This method is a no-op if cxa_throw is not swapped. sentrycrashct_unswap_cxa_throw();