From 07724c62ec19bcd8823ef1a9b04e0ddc282a9e2b Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 5 Aug 2024 14:54:20 +0200 Subject: [PATCH 01/23] transferring to linux test machine --- include/sentry.h | 35 ++++++++++++++++++++++++ src/backends/sentry_backend_inproc.c | 41 ++++++++++------------------ src/sentry_options.c | 23 ++++++++++++++++ src/sentry_options.h | 1 + 4 files changed, 74 insertions(+), 26 deletions(-) diff --git a/include/sentry.h b/include/sentry.h index 9ef068b41..c5769d369 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -768,6 +768,14 @@ typedef enum { SENTRY_USER_CONSENT_REVOKED = 0, } sentry_user_consent_t; +/** + * The crash handler strategy. + */ +typedef enum { + SENTRY_HANDLER_STRATEGY_DEFAULT = 0, + SENTRY_HANDLER_STRATEGY_CHAIN_AT_START = 1, +} sentry_handler_strategy_t; + /** * Creates a new options struct. * Can be freed with `sentry_options_free`. @@ -1492,6 +1500,33 @@ SENTRY_EXPERIMENTAL_API void sentry_options_set_traces_sample_rate( SENTRY_EXPERIMENTAL_API double sentry_options_get_traces_sample_rate( sentry_options_t *opts); + +#ifdef SENTRY_PLATFORM_LINUX + +/** + * Returns the currently set strategy for the handler. + * + * This option does only work for the `inproc` backend on `Linux` and `Android`. + * + * The main use-case is when the Native SDK is used in the context of the CLR/Mono runtimes + * which convert some POSIX signals into managed-code exceptions and discontinue the signal chain. + * + * If this happens and we invoke the previous handler at the end (i.e., after our handler processed the signal, which is the default strategy) + * we will end up sending a native crash in addition to the managed code exception (which will either generate + * another crash-event if uncaught or could be handled in the managed code and neither terminate the application nor create a crash event). + * To correctly process the signals of CLR/Mono applications, we must invoke the runtime handler at the start of our handler. + */ +SENTRY_EXPERIMENTAL_API sentry_handler_strategy_t sentry_options_get_handler_strategy( + sentry_options_t *opts); + +/** + * Sets the handler strategy. + */ +SENTRY_EXPERIMENTAL_API void sentry_options_get_handler_strategy( + sentry_options_t *opts, sentry_handler_strategy_t handler_strategy); + +#endif // SENTRY_PLATFORM_LINUX + /* -- Session APIs -- */ typedef enum { diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 042729be1..6d8485d98 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -16,27 +16,6 @@ #include "transports/sentry_disk_transport.h" #include -/** - * Android's bionic libc seems to allocate alternate signal handler stacks for - * every thread and also references them from their internal maintenance - * structs. - * - * The way we currently set up our sigaltstack seems to interfere with this - * setup and causes crashes whenever an ART signal handler touches the thread - * that called `sentry_init()`. - * - * In addition to this problem, it also means there is no need for our own - * sigaltstack on Android since our signal handler will always be running on - * an alternate stack managed by bionic. - * - * Note: In bionic the sigaltstacks for 32-bit devices have a size of 16KiB and - * on 64-bit devices they have 32KiB. The size of our own was set to 64KiB - * independent of the device. If this is a problem, we need figure out - * together with Google if there is a way in which our configs can coexist. - * - * Both breakpad and crashpad are way more defensive in the setup of their - * signal stacks and take existing stacks into account (or reuse them). - */ #define SIGNAL_DEF(Sig, Desc) { Sig, #Sig, Desc } #define MAX_FRAMES 128 @@ -563,12 +542,20 @@ handle_ucontext(const sentry_ucontext_t *uctx) sentry__enter_signal_handler(); #endif - sentry_value_t event = make_signal_event(sig_slot, uctx); - SENTRY_WITH_OPTIONS (options) { - sentry__write_crash_marker(options); +#ifdef SENTRY_PLATFORM_UNIX + sentry_handler_strategy_t handler_strategy = sentry_options_get_handler_strategy(options); + if (handler_strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { + // CLR/Mono convert signals provoked by "managed" native code into managed code exceptions. + // In these cases, we shouldn't react to the signal at all and let their handler discontinue the signal chain. + invoke_signal_handler(uctx->signum, uctx->siginfo, (void *)uctx->user_context); + } +#endif + + sentry_value_t event = make_signal_event(sig_slot, uctx); bool should_handle = true; + sentry__write_crash_marker(options); if (options->on_crash_func) { SENTRY_TRACE("invoking `on_crash` hook"); @@ -610,8 +597,10 @@ handle_ucontext(const sentry_ucontext_t *uctx) // forward as we're not restoring the page allocator. reset_signal_handlers(); sentry__leave_signal_handler(); - invoke_signal_handler( - uctx->signum, uctx->siginfo, (void *)uctx->user_context); + if (handler_strategy == SENTRY_HANDLER_STRATEGY_DEFAULT) { + invoke_signal_handler( + uctx->signum, uctx->siginfo, (void *)uctx->user_context); + } #endif } diff --git a/src/sentry_options.c b/src/sentry_options.c index 014ec491f..8a667f790 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -57,6 +57,7 @@ sentry_options_new(void) opts->shutdown_timeout = SENTRY_DEFAULT_SHUTDOWN_TIMEOUT; opts->traces_sample_rate = 0.0; opts->max_spans = 0; + opts->handler_strategy = SENTRY_HANDLER_STRATEGY_DEFAULT; return opts; } @@ -594,3 +595,25 @@ sentry_options_set_backend(sentry_options_t *opts, sentry_backend_t *backend) sentry__backend_free(opts->backend); opts->backend = backend; } + +void +sentry_options_set_handler_strategy(sentry_options_t *opts, sentry_backend_t *backend) +{ + sentry__backend_free(opts->backend); + opts->backend = backend; +} + +#ifdef SENTRY_PLATFORM_LINUX + +sentry_handler_strategy_t sentry_options_get_handler_strategy( + sentry_options_t *opts) { + return opts->handler_strategy; +} + +void sentry_options_get_handler_strategy( + sentry_options_t *opts, sentry_handler_strategy_t handler_strategy) { + opts->handler_strategy = handler_strategy; +} + +#endif // SENTRY_PLATFORM_LINUX + diff --git a/src/sentry_options.h b/src/sentry_options.h index 060b17f70..5359e8009 100644 --- a/src/sentry_options.h +++ b/src/sentry_options.h @@ -71,6 +71,7 @@ typedef struct sentry_options_s { long user_consent; long refcount; uint64_t shutdown_timeout; + sentry_handler_strategy_t handler_strategy; } sentry_options_t; /** From 752b125bd91340c707172cfb11c984c356499e45 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 5 Aug 2024 14:57:12 +0200 Subject: [PATCH 02/23] clean up formatting --- include/sentry.h | 21 ++++++++++++--------- src/backends/sentry_backend_inproc.c | 20 ++++++++++++-------- src/sentry_options.c | 17 ++++++++++------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/include/sentry.h b/include/sentry.h index c5769d369..176e8ef2a 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1500,7 +1500,6 @@ SENTRY_EXPERIMENTAL_API void sentry_options_set_traces_sample_rate( SENTRY_EXPERIMENTAL_API double sentry_options_get_traces_sample_rate( sentry_options_t *opts); - #ifdef SENTRY_PLATFORM_LINUX /** @@ -1508,16 +1507,20 @@ SENTRY_EXPERIMENTAL_API double sentry_options_get_traces_sample_rate( * * This option does only work for the `inproc` backend on `Linux` and `Android`. * - * The main use-case is when the Native SDK is used in the context of the CLR/Mono runtimes - * which convert some POSIX signals into managed-code exceptions and discontinue the signal chain. + * The main use-case is when the Native SDK is used in the context of the + * CLR/Mono runtimes which convert some POSIX signals into managed-code + * exceptions and discontinue the signal chain. * - * If this happens and we invoke the previous handler at the end (i.e., after our handler processed the signal, which is the default strategy) - * we will end up sending a native crash in addition to the managed code exception (which will either generate - * another crash-event if uncaught or could be handled in the managed code and neither terminate the application nor create a crash event). - * To correctly process the signals of CLR/Mono applications, we must invoke the runtime handler at the start of our handler. + * If this happens and we invoke the previous handler at the end (i.e., after + * our handler processed the signal, which is the default strategy) we will end + * up sending a native crash in addition to the managed code exception (which + * will either generate another crash-event if uncaught or could be handled in + * the managed code and neither terminate the application nor create a crash + * event). To correctly process the signals of CLR/Mono applications, we must + * invoke the runtime handler at the start of our handler. */ -SENTRY_EXPERIMENTAL_API sentry_handler_strategy_t sentry_options_get_handler_strategy( - sentry_options_t *opts); +SENTRY_EXPERIMENTAL_API sentry_handler_strategy_t +sentry_options_get_handler_strategy(sentry_options_t *opts); /** * Sets the handler strategy. diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 6d8485d98..6a0ff999b 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -544,16 +544,20 @@ handle_ucontext(const sentry_ucontext_t *uctx) SENTRY_WITH_OPTIONS (options) { #ifdef SENTRY_PLATFORM_UNIX - sentry_handler_strategy_t handler_strategy = sentry_options_get_handler_strategy(options); - - if (handler_strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { - // CLR/Mono convert signals provoked by "managed" native code into managed code exceptions. - // In these cases, we shouldn't react to the signal at all and let their handler discontinue the signal chain. - invoke_signal_handler(uctx->signum, uctx->siginfo, (void *)uctx->user_context); - } + sentry_handler_strategy_t handler_strategy + = sentry_options_get_handler_strategy(options); + + if (handler_strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { + // CLR/Mono convert signals provoked by "managed" native code into + // managed code exceptions. In these cases, we shouldn't react to + // the signal at all and let their handler discontinue the signal + // chain. + invoke_signal_handler( + uctx->signum, uctx->siginfo, (void *)uctx->user_context); + } #endif - sentry_value_t event = make_signal_event(sig_slot, uctx); + sentry_value_t event = make_signal_event(sig_slot, uctx); bool should_handle = true; sentry__write_crash_marker(options); diff --git a/src/sentry_options.c b/src/sentry_options.c index 8a667f790..b04baa972 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -597,7 +597,8 @@ sentry_options_set_backend(sentry_options_t *opts, sentry_backend_t *backend) } void -sentry_options_set_handler_strategy(sentry_options_t *opts, sentry_backend_t *backend) +sentry_options_set_handler_strategy( + sentry_options_t *opts, sentry_backend_t *backend) { sentry__backend_free(opts->backend); opts->backend = backend; @@ -605,15 +606,17 @@ sentry_options_set_handler_strategy(sentry_options_t *opts, sentry_backend_t *ba #ifdef SENTRY_PLATFORM_LINUX -sentry_handler_strategy_t sentry_options_get_handler_strategy( - sentry_options_t *opts) { - return opts->handler_strategy; +sentry_handler_strategy_t +sentry_options_get_handler_strategy(sentry_options_t *opts) +{ + return opts->handler_strategy; } -void sentry_options_get_handler_strategy( - sentry_options_t *opts, sentry_handler_strategy_t handler_strategy) { +void +sentry_options_get_handler_strategy( + sentry_options_t *opts, sentry_handler_strategy_t handler_strategy) +{ opts->handler_strategy = handler_strategy; } #endif // SENTRY_PLATFORM_LINUX - From b38d0e609c445a0af44a0b9945028e7f2f116d83 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 5 Aug 2024 15:18:51 +0200 Subject: [PATCH 03/23] fix build --- include/sentry.h | 2 +- src/backends/sentry_backend_inproc.c | 5 +++-- src/sentry_options.c | 10 +--------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/include/sentry.h b/include/sentry.h index 176e8ef2a..463c6dad7 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1525,7 +1525,7 @@ sentry_options_get_handler_strategy(sentry_options_t *opts); /** * Sets the handler strategy. */ -SENTRY_EXPERIMENTAL_API void sentry_options_get_handler_strategy( +SENTRY_EXPERIMENTAL_API void sentry_options_set_handler_strategy( sentry_options_t *opts, sentry_handler_strategy_t handler_strategy); #endif // SENTRY_PLATFORM_LINUX diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 6a0ff999b..32db5b159 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -541,11 +541,12 @@ handle_ucontext(const sentry_ucontext_t *uctx) // pthread mutex. sentry__enter_signal_handler(); #endif + sentry_handler_strategy_t handler_strategy + = SENTRY_HANDLER_STRATEGY_DEFAULT; SENTRY_WITH_OPTIONS (options) { #ifdef SENTRY_PLATFORM_UNIX - sentry_handler_strategy_t handler_strategy - = sentry_options_get_handler_strategy(options); + handler_strategy = sentry_options_get_handler_strategy(options); if (handler_strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { // CLR/Mono convert signals provoked by "managed" native code into diff --git a/src/sentry_options.c b/src/sentry_options.c index b04baa972..e23db81d0 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -596,14 +596,6 @@ sentry_options_set_backend(sentry_options_t *opts, sentry_backend_t *backend) opts->backend = backend; } -void -sentry_options_set_handler_strategy( - sentry_options_t *opts, sentry_backend_t *backend) -{ - sentry__backend_free(opts->backend); - opts->backend = backend; -} - #ifdef SENTRY_PLATFORM_LINUX sentry_handler_strategy_t @@ -613,7 +605,7 @@ sentry_options_get_handler_strategy(sentry_options_t *opts) } void -sentry_options_get_handler_strategy( +sentry_options_set_handler_strategy( sentry_options_t *opts, sentry_handler_strategy_t handler_strategy) { opts->handler_strategy = handler_strategy; From b41ed01ddc89bd3cd0ee5f6b06b56135cb1c1321 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 5 Aug 2024 15:26:48 +0200 Subject: [PATCH 04/23] reintroduce #ifdef symmetry regarding the usage of the handler_strategy (which must work on all UNIXes) and the query towards the handler_strategy option which must only work on Linux. --- src/backends/sentry_backend_inproc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 32db5b159..3372fc197 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -541,11 +541,14 @@ handle_ucontext(const sentry_ucontext_t *uctx) // pthread mutex. sentry__enter_signal_handler(); #endif + +#ifdef SENTRY_PLATFORM_UNIX sentry_handler_strategy_t handler_strategy = SENTRY_HANDLER_STRATEGY_DEFAULT; +#endif SENTRY_WITH_OPTIONS (options) { -#ifdef SENTRY_PLATFORM_UNIX +#ifdef SENTRY_PLATFORM_LINUX handler_strategy = sentry_options_get_handler_strategy(options); if (handler_strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { From 4f57a0eb8bc49d908b568df479c18117cfdfebc2 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 5 Aug 2024 16:32:17 +0200 Subject: [PATCH 05/23] const options param in the handler_strategy getter --- include/sentry.h | 2 +- src/sentry_options.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/sentry.h b/include/sentry.h index 463c6dad7..360cd7ab4 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -1520,7 +1520,7 @@ SENTRY_EXPERIMENTAL_API double sentry_options_get_traces_sample_rate( * invoke the runtime handler at the start of our handler. */ SENTRY_EXPERIMENTAL_API sentry_handler_strategy_t -sentry_options_get_handler_strategy(sentry_options_t *opts); +sentry_options_get_handler_strategy(const sentry_options_t *opts); /** * Sets the handler strategy. diff --git a/src/sentry_options.c b/src/sentry_options.c index e23db81d0..a18bf1894 100644 --- a/src/sentry_options.c +++ b/src/sentry_options.c @@ -599,7 +599,7 @@ sentry_options_set_backend(sentry_options_t *opts, sentry_backend_t *backend) #ifdef SENTRY_PLATFORM_LINUX sentry_handler_strategy_t -sentry_options_get_handler_strategy(sentry_options_t *opts) +sentry_options_get_handler_strategy(const sentry_options_t *opts) { return opts->handler_strategy; } From 09020db5c0b16b4cd3445fed07e7eb92372e18b6 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 5 Aug 2024 18:13:13 +0200 Subject: [PATCH 06/23] ensure we "leave" the signal-handler when we invoke the CLR/Mono runtime handler --- src/backends/sentry_backend_inproc.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 3372fc197..80a68e121 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -534,6 +534,10 @@ handle_ucontext(const sentry_ucontext_t *uctx) #ifdef SENTRY_PLATFORM_UNIX // give us an allocator we can use safely in signals before we tear down. + // TODO: for `SENTRY_HANDLER_STRATEGY_CHAIN_AT_START` to work together with + // our page allocator we must find a way to maintain memory usage. + // We might be able to move this "enable" far enough down, because I + // think we do not allocate before we hit `make_signal_event()`. sentry__page_allocator_enable(); // inform the sentry_sync system that we're in a signal handler. This will @@ -551,13 +555,21 @@ handle_ucontext(const sentry_ucontext_t *uctx) #ifdef SENTRY_PLATFORM_LINUX handler_strategy = sentry_options_get_handler_strategy(options); + // CLR/Mono convert signals provoked by "managed" native code into + // managed code exceptions. In these cases, we shouldn't react to + // the signal at all and let their handler discontinue the signal + // chain by invoking the runtime handler before we process the signal. if (handler_strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { - // CLR/Mono convert signals provoked by "managed" native code into - // managed code exceptions. In these cases, we shouldn't react to - // the signal at all and let their handler discontinue the signal - // chain. + // there is a good chance that we won't return from the previous + // handler and that would mean we couldn't enter this handler with + // the next signal coming in if we didn't "leave" here. + sentry__leave_signal_handler(); + invoke_signal_handler( uctx->signum, uctx->siginfo, (void *)uctx->user_context); + + // let's re-enter because it means this was an actual native crash + sentry__enter_signal_handler(); } #endif From fee764e4a427f9909cf668ef37d9165ec691906f Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Tue, 12 Nov 2024 14:19:29 +0100 Subject: [PATCH 07/23] ensure the page_allocator is only enabled when we have an actual native crash, we don't allocate before --- src/backends/sentry_backend_inproc.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 80a68e121..5757aad78 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -533,38 +533,29 @@ handle_ucontext(const sentry_ucontext_t *uctx) } #ifdef SENTRY_PLATFORM_UNIX - // give us an allocator we can use safely in signals before we tear down. - // TODO: for `SENTRY_HANDLER_STRATEGY_CHAIN_AT_START` to work together with - // our page allocator we must find a way to maintain memory usage. - // We might be able to move this "enable" far enough down, because I - // think we do not allocate before we hit `make_signal_event()`. - sentry__page_allocator_enable(); - // inform the sentry_sync system that we're in a signal handler. This will // make mutexes spin on a spinlock instead as it's no longer safe to use a // pthread mutex. sentry__enter_signal_handler(); #endif -#ifdef SENTRY_PLATFORM_UNIX - sentry_handler_strategy_t handler_strategy - = SENTRY_HANDLER_STRATEGY_DEFAULT; -#endif - SENTRY_WITH_OPTIONS (options) { #ifdef SENTRY_PLATFORM_LINUX handler_strategy = sentry_options_get_handler_strategy(options); - // CLR/Mono convert signals provoked by "managed" native code into - // managed code exceptions. In these cases, we shouldn't react to - // the signal at all and let their handler discontinue the signal - // chain by invoking the runtime handler before we process the signal. + // On Linux (and thus Android) CLR/Mono converts signals provoked by + // AOT/JIT-generated native code into managed code exceptions. In these + // cases, we shouldn't react to the signal at all and let their handler + // discontinue the signal chain by invoking the runtime handler before + // we process the signal. if (handler_strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { // there is a good chance that we won't return from the previous // handler and that would mean we couldn't enter this handler with // the next signal coming in if we didn't "leave" here. sentry__leave_signal_handler(); + // invoke the previous handler (typically the CLR/Mono + // signal-to-managed-exception handler) invoke_signal_handler( uctx->signum, uctx->siginfo, (void *)uctx->user_context); @@ -572,6 +563,8 @@ handle_ucontext(const sentry_ucontext_t *uctx) sentry__enter_signal_handler(); } #endif + // use a signal-safe allocator before we tear down. + sentry__page_allocator_enable(); sentry_value_t event = make_signal_event(sig_slot, uctx); bool should_handle = true; @@ -587,7 +580,7 @@ handle_ucontext(const sentry_ucontext_t *uctx) sentry_envelope_t *envelope = sentry__prepare_event( options, event, NULL, !options->on_crash_func); // TODO(tracing): Revisit when investigating transaction flushing - // during hard crashes. + // during hard crashes. sentry_session_t *session = sentry__end_current_session_with_status( SENTRY_SESSION_STATUS_CRASHED); From 1a8626d398e9274a7963124881d25e8c7bbe664a Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Tue, 12 Nov 2024 14:21:46 +0100 Subject: [PATCH 08/23] continuing the signal chain at the end is something we want for both strategies, because CHAIN_AT_START will reach this execution-path only if the runtime-handler decided that it was an actual native crash. --- src/backends/sentry_backend_inproc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 5757aad78..cb2c348f1 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -610,10 +610,8 @@ handle_ucontext(const sentry_ucontext_t *uctx) // forward as we're not restoring the page allocator. reset_signal_handlers(); sentry__leave_signal_handler(); - if (handler_strategy == SENTRY_HANDLER_STRATEGY_DEFAULT) { - invoke_signal_handler( - uctx->signum, uctx->siginfo, (void *)uctx->user_context); - } + invoke_signal_handler( + uctx->signum, uctx->siginfo, (void *)uctx->user_context); #endif } From eb12874f43ae5870ba7f2ab6c526dcaa022a3ddb Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Tue, 12 Nov 2024 14:27:12 +0100 Subject: [PATCH 09/23] get rid of obsolete local var --- src/backends/sentry_backend_inproc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index cb2c348f1..4ac51f5c5 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -541,14 +541,13 @@ handle_ucontext(const sentry_ucontext_t *uctx) SENTRY_WITH_OPTIONS (options) { #ifdef SENTRY_PLATFORM_LINUX - handler_strategy = sentry_options_get_handler_strategy(options); - // On Linux (and thus Android) CLR/Mono converts signals provoked by // AOT/JIT-generated native code into managed code exceptions. In these // cases, we shouldn't react to the signal at all and let their handler // discontinue the signal chain by invoking the runtime handler before // we process the signal. - if (handler_strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { + if (sentry_options_get_handler_strategy(options) + == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { // there is a good chance that we won't return from the previous // handler and that would mean we couldn't enter this handler with // the next signal coming in if we didn't "leave" here. From b08d939066ecb60d7974bf4528059d8fba5d854b Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Tue, 12 Nov 2024 15:33:25 +0100 Subject: [PATCH 10/23] add trace logs to at-start chaining, so we can see the behavior in the field when debugging is enabled --- src/backends/sentry_backend_inproc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index 4ac51f5c5..fa9c2513e 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -548,6 +548,7 @@ handle_ucontext(const sentry_ucontext_t *uctx) // we process the signal. if (sentry_options_get_handler_strategy(options) == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { + SENTRY_TRACE("defer to runtime signal handler at start"); // there is a good chance that we won't return from the previous // handler and that would mean we couldn't enter this handler with // the next signal coming in if we didn't "leave" here. @@ -560,6 +561,7 @@ handle_ucontext(const sentry_ucontext_t *uctx) // let's re-enter because it means this was an actual native crash sentry__enter_signal_handler(); + SENTRY_TRACE("return from runtime signal handler, we handle the signal"); } #endif // use a signal-safe allocator before we tear down. From 2841e64932afbe81c9945ead1b93289297d0a2d0 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Tue, 12 Nov 2024 16:58:03 +0100 Subject: [PATCH 11/23] ensure page-allocator is only referenced on UNIXes --- src/backends/sentry_backend_inproc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index fa9c2513e..f1f36b94c 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -561,11 +561,15 @@ handle_ucontext(const sentry_ucontext_t *uctx) // let's re-enter because it means this was an actual native crash sentry__enter_signal_handler(); - SENTRY_TRACE("return from runtime signal handler, we handle the signal"); + SENTRY_TRACE( + "return from runtime signal handler, we handle the signal"); } #endif + +#ifdef SENTRY_PLATFORM_UNIX // use a signal-safe allocator before we tear down. sentry__page_allocator_enable(); +#endif sentry_value_t event = make_signal_event(sig_slot, uctx); bool should_handle = true; From 799743fa846f08791528a3eda9164d6357817f30 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 14 Nov 2024 18:50:37 +0100 Subject: [PATCH 12/23] add integration test for managed and native crash --- tests/fixtures/dotnet_signal/Program.cs | 52 +++++++++ tests/fixtures/dotnet_signal/crash.c | 4 + .../fixtures/dotnet_signal/test_dotnet.csproj | 8 ++ tests/test_dotnet_signals.py | 108 ++++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 tests/fixtures/dotnet_signal/Program.cs create mode 100644 tests/fixtures/dotnet_signal/crash.c create mode 100644 tests/fixtures/dotnet_signal/test_dotnet.csproj create mode 100644 tests/test_dotnet_signals.py diff --git a/tests/fixtures/dotnet_signal/Program.cs b/tests/fixtures/dotnet_signal/Program.cs new file mode 100644 index 000000000..9f01df56c --- /dev/null +++ b/tests/fixtures/dotnet_signal/Program.cs @@ -0,0 +1,52 @@ +namespace dotnet_signal; + +using System; +using System.Runtime.InteropServices; + +class Program +{ + [DllImport("crash", EntryPoint = "native_crash")] + static extern void native_crash(); + + [DllImport("sentry", EntryPoint = "sentry_options_new")] + static extern IntPtr sentry_options_new(); + + [DllImport("sentry", EntryPoint = "sentry_options_set_handler_strategy")] + static extern IntPtr sentry_options_set_handler_strategy(IntPtr options, int strategy); + + [DllImport("sentry", EntryPoint = "sentry_options_set_debug")] + static extern IntPtr sentry_options_set_debug(IntPtr options, int debug); + + [DllImport("sentry", EntryPoint = "sentry_init")] + static extern int sentry_init(IntPtr options); + + static void Main(string[] args) + { + // setup minimal sentry-native + var options = sentry_options_new(); + sentry_options_set_handler_strategy(options, 1); + sentry_options_set_debug(options, 1); + sentry_init(options); + + var doNativeCrash = args is ["native-crash"]; + if (doNativeCrash) + { + native_crash(); + } + else + { + try + { + Console.WriteLine("dereference a NULL object from managed code"); + var s = default(string); + var c = s.Length; + } + catch (NullReferenceException exception) + { + Console.WriteLine("dereference another NULL object from managed code"); + var s = default(string); + var c = s.Length; + } + } + } +} \ No newline at end of file diff --git a/tests/fixtures/dotnet_signal/crash.c b/tests/fixtures/dotnet_signal/crash.c new file mode 100644 index 000000000..510182144 --- /dev/null +++ b/tests/fixtures/dotnet_signal/crash.c @@ -0,0 +1,4 @@ +void native_crash(void) +{ + *(int *)10 = 100; +} \ No newline at end of file diff --git a/tests/fixtures/dotnet_signal/test_dotnet.csproj b/tests/fixtures/dotnet_signal/test_dotnet.csproj new file mode 100644 index 000000000..64e34a8d4 --- /dev/null +++ b/tests/fixtures/dotnet_signal/test_dotnet.csproj @@ -0,0 +1,8 @@ + + + Exe + net8.0 + enable + enable + + diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py new file mode 100644 index 000000000..c2823fbc6 --- /dev/null +++ b/tests/test_dotnet_signals.py @@ -0,0 +1,108 @@ +import os +import pathlib +import shutil +import subprocess +import sys + +import pytest + +project_fixture_path = pathlib.Path("tests/fixtures/dotnet_signal") + + +def assert_empty_run_dir(database_path): + run_dirs = [d for d in database_path.glob("*.run") if d.is_dir()] + assert ( + len(run_dirs) == 1 + ), f"Expected exactly one .run directory, found {len(run_dirs)}" + + run_dir = run_dirs[0] + assert not any(run_dir.iterdir()), f"The directory {run_dir} is not empty" + + +def assert_run_dir_with_envelope(database_path): + run_dirs = [d for d in database_path.glob("*.run") if d.is_dir()] + assert ( + len(run_dirs) == 1 + ), f"Expected exactly one .run directory, found {len(run_dirs)}" + + run_dir = run_dirs[0] + crash_envelopes = [f for f in run_dir.glob("*.envelope") if f.is_file()] + assert len(crash_envelopes) > 0, f"Crash envelope is missing" + assert ( + len(crash_envelopes) == 1 + ), f"There is more than one crash envelope ({len(crash_envelopes)}" + + +def run_dotnet(tmp_path, args): + env = os.environ.copy() + env["LD_LIBRARY_PATH"] = str(tmp_path) + ":" + env.get("LD_LIBRARY_PATH", "") + return subprocess.Popen( + args, + text=True, + cwd=str(project_fixture_path), + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + +def run_dotnet_managed_exception(tmp_path): + return run_dotnet(tmp_path, ["dotnet", "run"]) + + +def run_dotnet_native_crash(tmp_path): + return run_dotnet(tmp_path, ["dotnet", "run", "native-crash"]) + + +@pytest.mark.skipif( + sys.platform != "linux", + reason="dotnet signal handling is currently only supported on Linux", +) +def test_dotnet_signals_inproc(cmake): + try: + # build native client library with inproc and the example for crash dumping + tmp_path = cmake( + ["sentry"], + {"SENTRY_BACKEND": "inproc", "SENTRY_TRANSPORT": "none"}, + ) + + # build the crashing native library + subprocess.run( + [ + "gcc", + "-Wall", + "-Wextra", + "-fPIC", + "-shared", + str(project_fixture_path / "crash.c"), + "-o", + str(tmp_path / "libcrash.so"), + ], + check=True, + ) + + # this runs the dotnet program with the Native SDK and chain-at-start, when managed code raises a signal that CLR convert to an exception. + dotnet_run = run_dotnet_managed_exception(tmp_path) + dotnet_run_stdout, dotnet_run_stderr = dotnet_run.communicate() + + # the program will fail with a `NullReferenceException`, but the Native SDK won't register a crash. + assert dotnet_run.returncode != 0 + assert "NullReferenceException" in dotnet_run_stderr + database_path = project_fixture_path / ".sentry-native" + assert database_path.exists(), "No database-path exists" + assert not (database_path / "last_crash").exists(), "A crash was registered" + assert_empty_run_dir(database_path) + + # this runs the dotnet program with the Native SDK and chain-at-start, when an actual native crash raises a signal + dotnet_run = run_dotnet_native_crash(tmp_path) + dotnet_run_stdout, dotnet_run_stderr = dotnet_run.communicate() + + # the program will fail with a SIGSEGV, that has been processed by the Native SDK which produced a crash envelope + assert dotnet_run.returncode != 0 + assert "crash has been captured" in dotnet_run_stderr + assert (database_path / "last_crash").exists() + assert_run_dir_with_envelope(database_path) + finally: + shutil.rmtree(project_fixture_path / ".sentry-native", ignore_errors=True) + shutil.rmtree(project_fixture_path / "bin", ignore_errors=True) + shutil.rmtree(project_fixture_path / "obj", ignore_errors=True) From 0aac4e935ea2d2e678507dade87c3eeb860edec9 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 14 Nov 2024 19:05:44 +0100 Subject: [PATCH 13/23] ignore 32-bit Linux build in the integration test --- tests/test_dotnet_signals.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index c2823fbc6..7f052b734 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -55,8 +55,8 @@ def run_dotnet_native_crash(tmp_path): @pytest.mark.skipif( - sys.platform != "linux", - reason="dotnet signal handling is currently only supported on Linux", + sys.platform != "linux" and not os.environ.get("TEST_X86"), + reason="dotnet signal handling is currently only supported on 64-bit Linux", ) def test_dotnet_signals_inproc(cmake): try: From fbe98189b0dcc08bb12962f09a893d961c6a813f Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 14 Nov 2024 19:06:56 +0100 Subject: [PATCH 14/23] remove native sdk logging assert temporarily --- tests/test_dotnet_signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index 7f052b734..4fcec508b 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -99,7 +99,7 @@ def test_dotnet_signals_inproc(cmake): # the program will fail with a SIGSEGV, that has been processed by the Native SDK which produced a crash envelope assert dotnet_run.returncode != 0 - assert "crash has been captured" in dotnet_run_stderr + # assert "crash has been captured" in dotnet_run_stderr assert (database_path / "last_crash").exists() assert_run_dir_with_envelope(database_path) finally: From 0403d1889461f09a5d869e93eaba122b4f3a9323 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 14 Nov 2024 19:13:02 +0100 Subject: [PATCH 15/23] fix pytest skip for x86 --- tests/test_dotnet_signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index 4fcec508b..9f938107c 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -55,7 +55,7 @@ def run_dotnet_native_crash(tmp_path): @pytest.mark.skipif( - sys.platform != "linux" and not os.environ.get("TEST_X86"), + sys.platform != "linux" and os.environ.get("TEST_X86"), reason="dotnet signal handling is currently only supported on 64-bit Linux", ) def test_dotnet_signals_inproc(cmake): From 42316427d22d6d1b1307668d389dc85b1b2536e0 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 14 Nov 2024 19:25:39 +0100 Subject: [PATCH 16/23] inverted demorgan --- tests/test_dotnet_signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index 9f938107c..615911c1b 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -55,7 +55,7 @@ def run_dotnet_native_crash(tmp_path): @pytest.mark.skipif( - sys.platform != "linux" and os.environ.get("TEST_X86"), + sys.platform != "linux" or os.environ.get("TEST_X86"), reason="dotnet signal handling is currently only supported on 64-bit Linux", ) def test_dotnet_signals_inproc(cmake): From f926707a07a47504252c13998c642bc9a13fda0d Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 14 Nov 2024 23:54:33 +0100 Subject: [PATCH 17/23] extract skip_condition to check what provokes the invalid syntax --- tests/test_dotnet_signals.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index 615911c1b..d54de37c4 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -54,8 +54,10 @@ def run_dotnet_native_crash(tmp_path): return run_dotnet(tmp_path, ["dotnet", "run", "native-crash"]) +skip_condition = sys.platform != "linux" or bool(os.environ.get("TEST_X86")) + @pytest.mark.skipif( - sys.platform != "linux" or os.environ.get("TEST_X86"), + skip_condition, reason="dotnet signal handling is currently only supported on 64-bit Linux", ) def test_dotnet_signals_inproc(cmake): From 6e65b62ff4abd3a30f45a6331b9f1b013d25e5f3 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 15 Nov 2024 10:26:51 +0100 Subject: [PATCH 18/23] re-enable log check, since we know it is not about log flushing but dotnet failing to start the handler in CI --- tests/test_dotnet_signals.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index d54de37c4..b6dda5934 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -56,6 +56,7 @@ def run_dotnet_native_crash(tmp_path): skip_condition = sys.platform != "linux" or bool(os.environ.get("TEST_X86")) + @pytest.mark.skipif( skip_condition, reason="dotnet signal handling is currently only supported on 64-bit Linux", @@ -101,7 +102,7 @@ def test_dotnet_signals_inproc(cmake): # the program will fail with a SIGSEGV, that has been processed by the Native SDK which produced a crash envelope assert dotnet_run.returncode != 0 - # assert "crash has been captured" in dotnet_run_stderr + assert "crash has been captured" in dotnet_run_stderr assert (database_path / "last_crash").exists() assert_run_dir_with_envelope(database_path) finally: From 67de2454426c44b7f950769b367d4c4682f79377 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 15 Nov 2024 14:58:09 +0100 Subject: [PATCH 19/23] clean up sigaltstack initialization * overwrite it only when the flag `SS_DISABLED` is set and the query didn't result in an error * if the query was successful but the flag is anything but `SS_DISABLED`, only log the size and flags of the current stack * if the query failed then log the corresponding error --- src/backends/sentry_backend_inproc.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/backends/sentry_backend_inproc.c b/src/backends/sentry_backend_inproc.c index f1f36b94c..7ad78ab5b 100644 --- a/src/backends/sentry_backend_inproc.c +++ b/src/backends/sentry_backend_inproc.c @@ -87,8 +87,8 @@ startup_inproc_backend( // set up an alternate signal stack if noone defined one before stack_t old_sig_stack; - if (sigaltstack(NULL, &old_sig_stack) == -1 || old_sig_stack.ss_sp == NULL - || old_sig_stack.ss_size == 0) { + int ret = sigaltstack(NULL, &old_sig_stack); + if (ret == 0 && old_sig_stack.ss_flags == SS_DISABLE) { SENTRY_TRACEF("installing signal stack (size: %d)", SIGNAL_STACK_SIZE); g_signal_stack.ss_sp = sentry_malloc(SIGNAL_STACK_SIZE); if (!g_signal_stack.ss_sp) { @@ -97,9 +97,11 @@ startup_inproc_backend( g_signal_stack.ss_size = SIGNAL_STACK_SIZE; g_signal_stack.ss_flags = 0; sigaltstack(&g_signal_stack, 0); - } else { - SENTRY_TRACEF( - "using existing signal stack (size: %d)", old_sig_stack.ss_size); + } else if (ret == 0) { + SENTRY_TRACEF("using existing signal stack (size: %d, flags: %d)", + old_sig_stack.ss_size, old_sig_stack.ss_flags); + } else if (ret == -1) { + SENTRY_WARNF("Failed to query signal stack size: %s", strerror(errno)); } // install our own signal handler From bc47fcf3c4f0b057695c1be076b4d4120c0caa64 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 15 Nov 2024 15:40:42 +0100 Subject: [PATCH 20/23] clean up run assertion on output, so we can actually see what's happening. --- tests/test_dotnet_signals.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index b6dda5934..5cc82ef81 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -38,9 +38,9 @@ def run_dotnet(tmp_path, args): env["LD_LIBRARY_PATH"] = str(tmp_path) + ":" + env.get("LD_LIBRARY_PATH", "") return subprocess.Popen( args, - text=True, cwd=str(project_fixture_path), env=env, + text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) @@ -90,7 +90,9 @@ def test_dotnet_signals_inproc(cmake): # the program will fail with a `NullReferenceException`, but the Native SDK won't register a crash. assert dotnet_run.returncode != 0 - assert "NullReferenceException" in dotnet_run_stderr + assert ( + "NullReferenceException" in dotnet_run_stderr + ), f"Managed exception run failed.\nstdout:\n{dotnet_run_stdout}\nstderr:\n{dotnet_run_stderr}" database_path = project_fixture_path / ".sentry-native" assert database_path.exists(), "No database-path exists" assert not (database_path / "last_crash").exists(), "A crash was registered" @@ -102,7 +104,9 @@ def test_dotnet_signals_inproc(cmake): # the program will fail with a SIGSEGV, that has been processed by the Native SDK which produced a crash envelope assert dotnet_run.returncode != 0 - assert "crash has been captured" in dotnet_run_stderr + assert ( + "crash has been captured" in dotnet_run_stderr + ), f"Native exception run failed.\nstdout:\n{dotnet_run_stdout}\nstderr:\n{dotnet_run_stderr}" assert (database_path / "last_crash").exists() assert_run_dir_with_envelope(database_path) finally: From 84c94df532902a20f43dc11af26aba05f890b06c Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 15 Nov 2024 16:05:44 +0100 Subject: [PATCH 21/23] create a non-faulty `sigaltstack` for the GHA runner --- tests/fixtures/dotnet_signal/Program.cs | 12 ++++++++++++ tests/fixtures/dotnet_signal/crash.c | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/tests/fixtures/dotnet_signal/Program.cs b/tests/fixtures/dotnet_signal/Program.cs index 9f01df56c..951adaaf7 100644 --- a/tests/fixtures/dotnet_signal/Program.cs +++ b/tests/fixtures/dotnet_signal/Program.cs @@ -8,6 +8,9 @@ class Program [DllImport("crash", EntryPoint = "native_crash")] static extern void native_crash(); + [DllImport("crash", EntryPoint = "enable_sigaltstack")] + static extern void enable_sigaltstack(); + [DllImport("sentry", EntryPoint = "sentry_options_new")] static extern IntPtr sentry_options_new(); @@ -22,6 +25,15 @@ class Program static void Main(string[] args) { + var githubActions = Environment.GetEnvironmentVariable("GITHUB_ACTIONS") ?? string.Empty; + if (githubActions == "true") { + // Set up our own `sigaltstack` for this thread if we're running on GHA because of a failure to run any + // signal handler after the initial setup. This behavior is locally non-reproducible and likely runner-related. + // I ran this against .net7/8/9 on at least 10 different Linux setups, and it worked on all, but on GHA + // it only works if we __don't__ accept the already installed `sigaltstack`. + enable_sigaltstack(); + } + // setup minimal sentry-native var options = sentry_options_new(); sentry_options_set_handler_strategy(options, 1); diff --git a/tests/fixtures/dotnet_signal/crash.c b/tests/fixtures/dotnet_signal/crash.c index 510182144..c76fd1a4f 100644 --- a/tests/fixtures/dotnet_signal/crash.c +++ b/tests/fixtures/dotnet_signal/crash.c @@ -1,4 +1,19 @@ +#include +#include void native_crash(void) { *(int *)10 = 100; +} + +void enable_sigaltstack(void) +{ + const size_t signal_stack_size = 16384; + stack_t signal_stack; + signal_stack.ss_sp = malloc(signal_stack_size); + if (!signal_stack.ss_sp) { + return; + } + signal_stack.ss_size = signal_stack_size; + signal_stack.ss_flags = 0; + sigaltstack(&signal_stack, 0); } \ No newline at end of file From c7352709e371b95229c816e7cbb5c1eb01b6c9d1 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 15 Nov 2024 16:32:23 +0100 Subject: [PATCH 22/23] disable the test on ASAN runs since that would require an instrumented runtime :-) https://github.com/dotnet/runtime/issues/13458 --- tests/test_dotnet_signals.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_dotnet_signals.py b/tests/test_dotnet_signals.py index 5cc82ef81..2ea098013 100644 --- a/tests/test_dotnet_signals.py +++ b/tests/test_dotnet_signals.py @@ -54,7 +54,11 @@ def run_dotnet_native_crash(tmp_path): return run_dotnet(tmp_path, ["dotnet", "run", "native-crash"]) -skip_condition = sys.platform != "linux" or bool(os.environ.get("TEST_X86")) +skip_condition = ( + sys.platform != "linux" + or bool(os.environ.get("TEST_X86")) + or "asan" in os.environ.get("RUN_ANALYZER", "") +) @pytest.mark.skipif( From ca5e83a0f8d4f1e8082a565cee6492f2661b97de Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 15 Nov 2024 16:46:27 +0100 Subject: [PATCH 23/23] Add changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1110cdc71..33a30996e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ **Features**: - Provide version information for non-static Windows binaries. ([#1076](https://github.com/getsentry/sentry-native/pull/1076), [crashpad#110](https://github.com/getsentry/crashpad/pull/110)) +- Add an alternative handler strategy to `inproc` to support `.NET` on Linux and `Mono` on Android (specifically, [.NET MAUI](https://github.com/dotnet/android/issues/9055#issuecomment-2261347912)). ([#1027](https://github.com/getsentry/sentry-native/pull/1027)) **Fixes**: