- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 198
fix: AOT interop with managed .NET runtimes #1392
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c1bd96a
              45dccd6
              02ebf36
              81293d8
              10ba6bd
              d9eafcd
              1bf0f52
              060b18b
              d4dd399
              4da4c4a
              345ab4d
              e8576cf
              06c84e1
              e92bb00
              de6d67c
              4a73907
              72df376
              95c3c9b
              b6e37d1
              ac6877e
              8a5c5b6
              2ef7c5b
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -456,9 +456,48 @@ registers_from_uctx(const sentry_ucontext_t *uctx) | |
| return registers; | ||
| } | ||
|  | ||
| #ifdef SENTRY_PLATFORM_LINUX | ||
| static uintptr_t | ||
| get_stack_pointer(const sentry_ucontext_t *uctx) | ||
| { | ||
| # if defined(__i386__) | ||
| return uctx->user_context->uc_mcontext.gregs[REG_ESP]; | ||
| # elif defined(__x86_64__) | ||
| return uctx->user_context->uc_mcontext.gregs[REG_RSP]; | ||
| # elif defined(__arm__) | ||
| return uctx->user_context->uc_mcontext.arm_sp; | ||
| # elif defined(__aarch64__) | ||
| return uctx->user_context->uc_mcontext.sp; | ||
| # else | ||
| SENTRY_WARN("get_stack_pointer is not implemented for this architecture. " | ||
| "Signal chaining may not work as expected."); | ||
| return NULL; | ||
| # endif | ||
| } | ||
|  | ||
| static uintptr_t | ||
| get_instruction_pointer(const sentry_ucontext_t *uctx) | ||
| { | ||
| # if defined(__i386__) | ||
| return uctx->user_context->uc_mcontext.gregs[REG_EIP]; | ||
| # elif defined(__x86_64__) | ||
| return uctx->user_context->uc_mcontext.gregs[REG_RIP]; | ||
| # elif defined(__arm__) | ||
| return uctx->user_context->uc_mcontext.arm_pc; | ||
| # elif defined(__aarch64__) | ||
| return uctx->user_context->uc_mcontext.pc; | ||
| # else | ||
| SENTRY_WARN( | ||
| "get_instruction_pointer is not implemented for this architecture. " | ||
| "Signal chaining may not work as expected."); | ||
| return NULL; | ||
| # endif | ||
| } | ||
|         
                  jpnurmi marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| #endif | ||
|  | ||
| static sentry_value_t | ||
| make_signal_event( | ||
| const struct signal_slot *sig_slot, const sentry_ucontext_t *uctx) | ||
| make_signal_event(const struct signal_slot *sig_slot, | ||
| const sentry_ucontext_t *uctx, sentry_handler_strategy_t strategy) | ||
| { | ||
| sentry_value_t event = sentry_value_new_event(); | ||
| sentry_value_set_by_key( | ||
|  | @@ -496,8 +535,10 @@ make_signal_event( | |
| "captured backtrace from ucontext with %lu frames", frame_count); | ||
| // if unwinding from a ucontext didn't yield any results, try again with a | ||
| // direct unwind. this is most likely the case when using `libbacktrace`, | ||
| // since that does not allow to unwind from a ucontext at all. | ||
| if (!frame_count) { | ||
| // since that does not allow to unwind from a ucontext at all. the fallback | ||
| // is skipped with the "chain at start" strategy because `libbacktrace` | ||
| // crashes, and would likely not provide helpful information anyway. | ||
| if (!frame_count && strategy != SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { | ||
|         
                  jpnurmi marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| frame_count = sentry_unwind_stack(NULL, &backtrace[0], MAX_FRAMES); | ||
| } | ||
| SENTRY_DEBUGF("captured backtrace with %lu frames", frame_count); | ||
|  | @@ -534,20 +575,7 @@ handle_ucontext(const sentry_ucontext_t *uctx) | |
|  | ||
| SENTRY_INFO("entering signal handler"); | ||
|  | ||
| const struct signal_slot *sig_slot = NULL; | ||
| for (int i = 0; i < SIGNAL_COUNT; ++i) { | ||
| #ifdef SENTRY_PLATFORM_UNIX | ||
| if (SIGNAL_DEFINITIONS[i].signum == uctx->signum) { | ||
| #elif defined SENTRY_PLATFORM_WINDOWS | ||
| if (SIGNAL_DEFINITIONS[i].signum | ||
| == uctx->exception_ptrs.ExceptionRecord->ExceptionCode) { | ||
| #else | ||
| # error Unsupported platform | ||
| #endif | ||
| sig_slot = &SIGNAL_DEFINITIONS[i]; | ||
| } | ||
| } | ||
|  | ||
| sentry_handler_strategy_t strategy = SENTRY_HANDLER_STRATEGY_DEFAULT; | ||
| #ifdef SENTRY_PLATFORM_UNIX | ||
| // 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 | ||
|  | @@ -556,42 +584,77 @@ handle_ucontext(const sentry_ucontext_t *uctx) | |
| #endif | ||
|  | ||
| SENTRY_WITH_OPTIONS (options) { | ||
| // Flush logs in a crash-safe manner before crash handling | ||
| if (options->enable_logs) { | ||
| sentry__logs_flush_crash_safe(); | ||
| } | ||
| #ifdef SENTRY_PLATFORM_LINUX | ||
| // 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 (sentry_options_get_handler_strategy(options) | ||
| == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { | ||
| strategy = sentry_options_get_handler_strategy(options); | ||
| if (strategy == SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { | ||
| SENTRY_DEBUG("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. | ||
| sentry__leave_signal_handler(); | ||
| if (!options->enable_logging_when_crashed) { | ||
| sentry__logger_enable(); | ||
| } | ||
|  | ||
| uintptr_t ip = get_instruction_pointer(uctx); | ||
| uintptr_t sp = get_stack_pointer(uctx); | ||
|  | ||
| // invoke the previous handler (typically the CLR/Mono | ||
| // signal-to-managed-exception handler) | ||
| invoke_signal_handler( | ||
| uctx->signum, uctx->siginfo, (void *)uctx->user_context); | ||
|  | ||
| // If the execution returns here in AOT mode, and the instruction | ||
| // or stack pointer were changed, it means CLR/Mono converted the | ||
| // signal into a managed exception and transferred execution to a | ||
| // managed exception handler. | ||
| // https://github.com/dotnet/runtime/blob/6d96e28597e7da0d790d495ba834cc4908e442cd/src/mono/mono/mini/exceptions-arm64.c#L538 | ||
| if (ip != get_instruction_pointer(uctx) | ||
| || sp != get_stack_pointer(uctx)) { | ||
| SENTRY_DEBUG("runtime converted the signal to a managed " | ||
| "exception, we do not handle the signal"); | ||
| return; | ||
| } | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Signal Chaining Logic Fails on Some ArchitecturesThe new signal chaining logic has two issues: on architectures where instruction and stack pointers aren't retrieved, it incorrectly proceeds with crash handling even after a runtime converts a signal. Additionally, when a signal conversion is detected and the function returns early, the internal signal handler state becomes unbalanced. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is absolutely correct, but the only side-effect currently visible is for the logging toggle. Similarly, to how we "leave" the signal handler before chaining, we must also re-enable logging immediately after "leaving" and disable it again before re-entering, because if it were a managed code exception, we want logging to remain enabled. We can also move the entire  However, I think both have a lower priority than figuring out the signaling sequence of both runtimes and how we can align them. | ||
|  | ||
| // let's re-enter because it means this was an actual native crash | ||
| if (!options->enable_logging_when_crashed) { | ||
| sentry__logger_disable(); | ||
| } | ||
| sentry__enter_signal_handler(); | ||
| SENTRY_DEBUG( | ||
| "return from runtime signal handler, we handle the signal"); | ||
| } | ||
| #endif | ||
|  | ||
| const struct signal_slot *sig_slot = NULL; | ||
| for (int i = 0; i < SIGNAL_COUNT; ++i) { | ||
| #ifdef SENTRY_PLATFORM_UNIX | ||
| if (SIGNAL_DEFINITIONS[i].signum == uctx->signum) { | ||
| #elif defined SENTRY_PLATFORM_WINDOWS | ||
| if (SIGNAL_DEFINITIONS[i].signum | ||
| == uctx->exception_ptrs.ExceptionRecord->ExceptionCode) { | ||
| #else | ||
| # error Unsupported platform | ||
| #endif | ||
| sig_slot = &SIGNAL_DEFINITIONS[i]; | ||
| } | ||
| } | ||
|  | ||
| #ifdef SENTRY_PLATFORM_UNIX | ||
| // use a signal-safe allocator before we tear down. | ||
| sentry__page_allocator_enable(); | ||
| #endif | ||
| // Flush logs in a crash-safe manner before crash handling | ||
| if (options->enable_logs) { | ||
| sentry__logs_flush_crash_safe(); | ||
| } | ||
|  | ||
| sentry_value_t event = make_signal_event(sig_slot, uctx); | ||
| sentry_value_t event = make_signal_event(sig_slot, uctx, strategy); | ||
| bool should_handle = true; | ||
| sentry__write_crash_marker(options); | ||
|  | ||
|  | @@ -647,8 +710,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 (strategy != SENTRY_HANDLER_STRATEGY_CHAIN_AT_START) { | ||
|         
                  jpnurmi marked this conversation as resolved.
              Show resolved
            Hide resolved | ||
| invoke_signal_handler( | ||
| uctx->signum, uctx->siginfo, (void *)uctx->user_context); | ||
| } | ||
| #endif | ||
| } | ||
|  | ||
|  | ||
Uh oh!
There was an error while loading. Please reload this page.