[mono] Preserve signal handlers during crash chaining#125835
[mono] Preserve signal handlers during crash chaining#125835jpnurmi wants to merge 10 commits intodotnet:mainfrom
Conversation
When mono_set_crash_chaining(true) is enabled, mono_handle_native_crash unconditionally resets SIGABRT, SIGILL, SIGCHLD, and SIGQUIT to SIG_DFL. This is problematic on Android where multiple threads may trigger signals concurrently — for example, FORTIFY detecting a destroyed mutex on an HWUI thread raises SIGABRT, but because the handler was reset to SIG_DFL, it kills the process before Mono can chain the original crash to the previous handler (e.g. sentry-native). Guard the signal handler reset with !mono_do_crash_chaining so that when crash chaining is enabled, Mono's handlers remain installed during native crash processing. This allows the crash to be fully handled and chained to any previously installed handlers before the process terminates. The fix has no effect when crash_chaining is disabled (the default), as the existing SIG_DFL reset behavior is preserved. Includes an Android functional test (CrashChaining) that: - Installs a SIGSEGV handler before mono_jit_init (gated by TEST_CRASH_CHAINING env var) to simulate a pre-Mono crash handler - Triggers a native SIGSEGV that Mono chains to the pre-Mono handler - Verifies SIGABRT was not reset to SIG_DFL during crash processing - Without the fix, the test process is killed by SIGABRT (test crashes) - With the fix, the test returns exit code 42 (pass)
|
Tagging subscribers to this area: @akoeplinger, @matouskozak, @simonrozsival |
There was a problem hiding this comment.
Pull request overview
This PR fixes Mono’s native-crash handling behavior when crash chaining is enabled so that Mono does not reset key signal handlers to SIG_DFL during mono_handle_native_crash, which can otherwise cause secondary signals (notably on Android) to terminate the process before crash chaining completes.
Changes:
- Guard signal-handler resets in
mono_handle_native_crashbehind!mono_do_crash_chaining. - Add an Android functional test that installs a pre-Mono
SIGSEGVhandler, triggers a native crash, and validatesSIGABRTwas not reset toSIG_DFL. - Wire the test via the Android app template using an environment-variable gate (
TEST_CRASH_CHAINING).
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/mono/mono/mini/mini-exceptions.c | Avoids resetting signal handlers to defaults when crash chaining is enabled. |
| src/tasks/AndroidAppBuilder/Templates/monodroid.c | Adds a crash-chaining functional test path (env-gated) and native test helpers. |
| src/tests/FunctionalTests/Android/Device_Emulator/CrashChaining/Program.cs | Managed entrypoint invoking the native test and mapping success to exit code 42. |
| src/tests/FunctionalTests/Android/Device_Emulator/CrashChaining/Android.Device_Emulator.CrashChaining.Test.csproj | New Android functional test project definition + env var injection. |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@dotnet-policy-service agree company="Sentry" |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
The fix proposal is simple, but the test gets quite complicated. Sorry about that. :) Let me know if you'd prefer to leave it out, even though then nothing guarantees that it stays fixed in the future... |
src/mono/mono/mini/mini-exceptions.c
Outdated
| // that secondary signals (e.g. SIGABRT from FORTIFY on other threads) | ||
| // don't kill the process with SIG_DFL before we can chain the original | ||
| // crash to the previous handler. | ||
| if (!mono_do_crash_chaining) { |
There was a problem hiding this comment.
Maybe it's better to restore the signal handlers instead of keeping the mono crash handlers active past this point. There is a function in mini-posix.c that removes our registered signal handlers and restore them with what existed before. It is currently a static method, but you could do a mono_runtime_remove_handlers that could remove the signals handled here and restore what was set before mono runtime installed its handlers.
| #include <sys/system_properties.h> | ||
| #include <sys/mman.h> | ||
| #include <assert.h> | ||
| #include <setjmp.h> |
There was a problem hiding this comment.
I'm not too happy to add test specific code into the Android App Builder. Maybe this can be handled differently? Maybe we could add some private property that the tests could use to add a custom source file that gets included in build of monodroid.c? Another option is to use the NativeLibrary item collection and make sure it gets linked into the app. This is what normally used with Native AOT to link custom static libraries together with an app, it is also used together with direct pinvoke. Mono Android App Builder do support direct pinvoke as well for AOT scenarios, but currently don't handle NativeLibrary items collection, so one option could be to piggyback on that. Your library could then use __attribute__((constructor)) to run the signal setup before main gets called and your test symbol should be picked up as well when linking that library.
Extract test-specific native code into a separate source file (test_crash_chaining.c) compiled via the new ExtraNativeSources property. Uses __attribute__((constructor)) to auto-install the pre-mono SIGSEGV handler at .so load time.
Use mono_runtime_posix_remove_signal_handler to restore whatever handlers were installed before Mono, rather than unconditionally resetting to SIG_DFL. This correctly handles crash chaining without needing a special case.
2b15ad2 to
10daad5
Compare
This change is part of the effort to fix native crash reporting for .NET Android apps (getsentry/sentry-dotnet#3954). We wish to install Sentry's signal handlers before Mono to capture native crashes. However,
mono_handle_native_crashunconditionally resetsSIGABRT,SIGILL,SIGCHLD, andSIGQUITtoSIG_DFL, even whencrash_chainingis enabled, killing the process before the chained handler can run.Expose static
remove_signal_handlerasmono_runtime_posix_remove_signal_handlerand use it inmono_handle_native_crashto restore pre-Mono signal handlers instead of resetting toSIG_DFL. When signal chaining is disabled, there are no saved handlers, so it falls back toSIG_DFL, same as before.Includes an Android functional test (
CrashChaining) that verifies signal handlers are preserved during crash chaining.