@@ -104,6 +104,14 @@ volatile sig_atomic_t g_installed = 0;
104104// the previous handler instead of recursing.
105105volatile sig_atomic_t g_inHandler = 0 ;
106106
107+ // Permanent "dump written" latch. Set to 1 the first time we successfully
108+ // begin writing a dump; never reset. Prevents a second full dump being
109+ // written if the crash handler is somehow re-invoked in the same process
110+ // lifetime (e.g. Mono re-raises SIGSEGV via tgkill after our handler chains
111+ // to it, and our handler gets re-installed between the two deliveries).
112+ // Unlike g_inHandler this is intentionally NOT cleared before the re-raise.
113+ volatile sig_atomic_t g_dumpWritten = 0 ;
114+
107115// ----------------------------------------------------------------------------
108116// Async-signal-safe formatters (no malloc, no stdio, no locale).
109117// ----------------------------------------------------------------------------
@@ -983,6 +991,28 @@ static void crashHandler(int sig, siginfo_t* info, void* ucontext) {
983991 }
984992 g_inHandler = 1 ;
985993
994+ // Duplicate-dump guard. If we already wrote a dump for this process
995+ // lifetime (e.g. the handler was re-invoked after Mono re-raised the
996+ // signal), skip the dump but still chain to the previous handler so the
997+ // system tombstone is produced. Unlike g_inHandler this latch is never
998+ // cleared — one dump per crash, not one dump per signal delivery.
999+ if (g_dumpWritten) {
1000+ bool restored = false ;
1001+ for (size_t i = 0 ; i < kNumSignals ; ++i) {
1002+ if (kSignals [i] == sig) {
1003+ restored = (sigaction (sig, &g_prevHandlers[i], nullptr ) == 0 );
1004+ break ;
1005+ }
1006+ }
1007+ // If sigaction failed we cannot chain cleanly — fall back to default
1008+ // disposition so the process at least terminates and debuggerd runs.
1009+ if (!restored) signal (sig, SIG_DFL );
1010+ g_inHandler = 0 ;
1011+ raise (sig);
1012+ return ;
1013+ }
1014+ g_dumpWritten = 1 ;
1015+
9861016 // Open the dump file (append). If g_logPath is empty we still log to logcat.
9871017 //
9881018 // Pre-rotate runaway: if the existing log is more than 4× the soft cap
0 commit comments