fix(android): mask signal during CHAIN_AT_START to survive chained Mono handler re-raises#1572
fix(android): mask signal during CHAIN_AT_START to survive chained Mono handler re-raises#1572
Conversation
SA_NODEFER (added in #1446) is incompatible with the CHAIN_AT_START signal handler strategy. When chaining to the runtime's signal handler (e.g. Mono), the runtime may reset the signal to SIG_DFL and re-raise. With SA_NODEFER the re-raised signal is delivered immediately, killing the process before our handler can regain control. Without SA_NODEFER, the re-raised signal is blocked during handler execution, allowing the runtime handler to return and sentry-native to proceed with crash capture. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89200fa to
ab26763
Compare
Yeah, this makes sense, but is there no Mono test in the downstream integration tests? It is quite painful that the two runtimes differ so severely, given that we seem to lack any early warning for that particular config. Or did this only happen with .NET 10?
Not critical is an understatement. It is as critical as with all other use cases, when the signal actually comes from code that the Native SDK should handle. Wouldn't it be better to just mask the incoming signal before we invoke the handler at start? This way, a |
This reverts commit 91afd1a.
…ng process With SA_NODEFER, the chained handler's re-raise is delivered immediately and kills the process before we regain control. Mask the signal via raw rt_sigprocmask (to bypass Android's libsigchain), then after the chain: reinstall our handler if it was reset to SIG_DFL, consume any pending signal with sigtimedwait, and unmask. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This happens with both .NET 9 and .NET 10. Unfortunately, the PR that takes chained signal handling into use and would have revealed this problem, has been on hold until now due to other issues in .NET 10. |
sigtimedwait is not declared without _POSIX_C_SOURCE >= 199309L, so use the raw SYS_rt_sigtimedwait syscall instead. Also replace sizeof(sigset_t) with _NSIG/8 since the kernel expects 8 bytes, not glibc's 128. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
libsigchain's sigprocmask guard is only active inside its own special handlers, so our signal handler still gets filtered. Keep the raw syscall on Android and use the standard sigprocmask on other platforms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…raises Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Summary
SA_NODEFERdoesn't let re-raises kill the processrt_sigprocmasksyscall to bypass Android'slibsigchain, whosesigprocmaskguard is only active inside its own special handlersSIG_DFL, consume any pending signal viasigtimedwait, and unmaskContext
With
SA_NODEFER, the chained Mono handler can reset the signal handler toSIG_DFLand re-raise. The re-raised signal is delivered immediately and kills the process before inproc can capture the crash.This is gated to Android because only the Mono runtime (used on Android) does the reset+raise pattern. On desktop Linux, CoreCLR modifies the ucontext and returns without re-raising.