diff --git a/CHANGELOG.md b/CHANGELOG.md index 504f1b061e..2573681bff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixes - Fix potential app launch hang caused by the SentrySDK (#6181) Fixed by removing the call to `_dyld_get_image_header` on the main thread. +- Fix interop with managed Mono/CoreCLR runtimes (#6193) ## 8.56.0 diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c index 35da952692..57d7cfc84a 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c @@ -486,6 +486,12 @@ installExceptionHandler(void) } } + if (sentrycrashcm_isManagedRuntime()) { + SENTRY_ASYNC_SAFE_LOG_DEBUG("Detected managed Mono/CoreCLR runtime. Not registering " + "exception port for EXC_BAD_ACCESS or EXC_MASK_ARITHMETIC."); + mask &= ~(EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC); + } + SENTRY_ASYNC_SAFE_LOG_DEBUG("Installing port as exception handler."); kr = task_set_exception_ports(thisTask, mask, g_exceptionPort, (int)(EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES), THREAD_STATE_NONE); @@ -562,6 +568,23 @@ addContextualInfoToEvent(struct SentryCrash_MonitorContext *eventContext) #endif // SENTRY_HAS_MACH +bool +sentrycrashcm_isManagedRuntime(void) +{ +#if SENTRY_HAS_MACH + for (mach_msg_type_number_t i = 0; i < g_previousExceptionPorts.count; i++) { + // https://github.com/dotnet/runtime/blob/6d96e28597e7da0d790d495ba834cc4908e442cd/src/mono/mono/mini/mini-darwin.c#L85-L90 + // https://github.com/dotnet/runtime/blob/6d96e28597e7da0d790d495ba834cc4908e442cd/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp#L615-L621 + if (g_previousExceptionPorts.masks[i] == (EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC) + && g_previousExceptionPorts.behaviors[i] == EXCEPTION_STATE_IDENTITY + && g_previousExceptionPorts.flavors[i] == MACHINE_THREAD_STATE) { + return true; + } + } +#endif // SENTRY_HAS_MACH + return false; +} + SentryCrashMonitorAPI * sentrycrashcm_machexception_getAPI(void) { diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h index e72d29422c..f9b94cf6af 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h @@ -49,6 +49,10 @@ bool sentrycrashcm_isReservedThread(thread_t thread); */ bool sentrycrashcm_hasReservedThreads(void); +/** Detect managed Mono/CoreCLR runtime's Mach exception port. + */ +bool sentrycrashcm_isManagedRuntime(void); + #ifdef __cplusplus } #endif diff --git a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c index 7cfeeb3c94..9804e3ab2d 100644 --- a/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c +++ b/Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c @@ -26,9 +26,11 @@ // #include "SentryCrashMonitor_Signal.h" +#include "SentryCrashCPU.h" #include "SentryCrashID.h" #include "SentryCrashMachineContext.h" #include "SentryCrashMonitorContext.h" +#include "SentryCrashMonitor_MachException.h" #include "SentryCrashSignalInfo.h" #include "SentryCrashStackCursor_MachineContext.h" #include "SentryInternalCDefines.h" @@ -49,6 +51,7 @@ static volatile bool g_isEnabled = false; static bool g_isSigtermReportingEnabled = false; +static bool g_isManagedRuntime = false; static SentryCrash_MonitorContext g_monitorContext; static SentryCrashStackCursor g_stackCursor; @@ -67,6 +70,29 @@ static char g_eventID[37]; # pragma mark - Callbacks - // ============================================================================ +/** + * + */ +static void +invokePreviousSignalHandlers(int sigNum, siginfo_t *signalInfo, void *userContext) +{ + const int *fatalSignals = sentrycrashsignal_fatalSignals(); + int fatalSignalsCount = sentrycrashsignal_numFatalSignals(); + + for (int i = 0; i < fatalSignalsCount; ++i) { + if (fatalSignals[i] == sigNum) { + struct sigaction *handler = &g_previousSignalHandlers[i]; + if (handler->sa_flags & SA_SIGINFO) { + handler->sa_sigaction(sigNum, signalInfo, userContext); + } else if (handler->sa_handler != SIG_DFL && handler->sa_handler != SIG_IGN) { + // This handler can only handle to signal number (ANSI C) + void (*func)(int) = handler->sa_handler; + func(sigNum); + } + } + } +} + /** Our custom signal handler. * Restore the default signal handlers, record the signal information, and * write a crash report. @@ -83,6 +109,29 @@ static void handleSignal(int sigNum, siginfo_t *signalInfo, void *userContext) { SENTRY_ASYNC_SAFE_LOG_DEBUG("Trapped signal %d", sigNum); + + // Let managed Mono/CoreCLR runtime handle the signal first, + // as they may convert it into a managed exception. + if (g_isManagedRuntime) { + SENTRY_ASYNC_SAFE_LOG_DEBUG( + "Detected managed Mono/CoreCLR runtime. Passing signal to previous handlers."); + + uintptr_t sp = sentrycrashcpu_stackPointerFromUserContext(userContext); + uintptr_t ip = sentrycrashcpu_instructionAddressFromUserContext(userContext); + + invokePreviousSignalHandlers(sigNum, signalInfo, userContext); + + // If the stack or instruction pointer changed, the managed runtime + // converted the signal into a managed exception and changed the context. + // https://github.com/dotnet/runtime/blob/6d96e28597e7da0d790d495ba834cc4908e442cd/src/mono/mono/mini/exceptions-arm64.c#L538 + if (sp != sentrycrashcpu_stackPointerFromUserContext(userContext) + || ip != sentrycrashcpu_instructionAddressFromUserContext(userContext)) { + SENTRY_ASYNC_SAFE_LOG_DEBUG( + "Signal converted to a managed exception. Aborting signal handling."); + return; + } + } + if (g_isEnabled) { thread_act_array_t threads = NULL; mach_msg_type_number_t numThreads = 0; @@ -110,9 +159,13 @@ handleSignal(int sigNum, siginfo_t *signalInfo, void *userContext) sentrycrashmc_resumeEnvironment(threads, numThreads); } - SENTRY_ASYNC_SAFE_LOG_DEBUG("Re-raising signal for regular handlers to catch."); - // This is technically not allowed, but it works in OSX and iOS. - raise(sigNum); + // Re-raise the signal to invoke the previous handlers unless already called + // above for managed runtimes. + if (!g_isManagedRuntime) { + SENTRY_ASYNC_SAFE_LOG_DEBUG("Re-raising signal for regular handlers to catch."); + // This is technically not allowed, but it works in OSX and iOS. + raise(sigNum); + } } // ============================================================================ @@ -124,6 +177,8 @@ installSignalHandler(void) { SENTRY_ASYNC_SAFE_LOG_DEBUG("Installing signal handler."); + g_isManagedRuntime = sentrycrashcm_isManagedRuntime(); + # if SENTRY_HAS_SIGNAL_STACK if (g_signalStack.ss_size == 0) { diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h index 1ec4a85d3b..c3b24af533 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h @@ -51,6 +51,7 @@ const char *sentrycrashcpu_currentArch(void); * @return The context's frame pointer. */ uintptr_t sentrycrashcpu_framePointer(const struct SentryCrashMachineContext *const context); +uintptr_t sentrycrashcpu_framePointerFromUserContext(const void *const userContext); /** Get the current stack pointer for a machine context. * @@ -59,6 +60,7 @@ uintptr_t sentrycrashcpu_framePointer(const struct SentryCrashMachineContext *co * @return The context's stack pointer. */ uintptr_t sentrycrashcpu_stackPointer(const struct SentryCrashMachineContext *const context); +uintptr_t sentrycrashcpu_stackPointerFromUserContext(const void *const userContext); /** Get the address of the instruction about to be, or being executed by a * machine context. @@ -68,6 +70,7 @@ uintptr_t sentrycrashcpu_stackPointer(const struct SentryCrashMachineContext *co * @return The context's next instruction address. */ uintptr_t sentrycrashcpu_instructionAddress(const struct SentryCrashMachineContext *const context); +uintptr_t sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext); /** Get the address stored in the link register (arm only). This may * contain the first return address of the stack. diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c index 0e6626ed29..fe6d189ff2 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c @@ -49,18 +49,36 @@ sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context) return context->machineContext.__ss.__r[7]; } +uintptr_t +sentrycrashcpu_framePointerFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__r[7]; +} + uintptr_t sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context) { return context->machineContext.__ss.__sp; } +uintptr_t +sentrycrashcpu_stackPointerFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__sp; +} + uintptr_t sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context) { return context->machineContext.__ss.__pc; } +uintptr_t +sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__pc; +} + uintptr_t sentrycrashcpu_linkRegister(const SentryCrashMachineContext *const context) { diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c index 8c18a24ccd..eeff45c05f 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c @@ -32,6 +32,7 @@ # include "SentryCrashMachineContext.h" # include "SentryCrashMachineContext_Apple.h" # include +# include # include "SentryAsyncSafeLog.h" @@ -46,36 +47,72 @@ static const char *g_exceptionRegisterNames[] = { "exception", "esr", "far" }; static const int g_exceptionRegisterNamesCount = sizeof(g_exceptionRegisterNames) / sizeof(*g_exceptionRegisterNames); -uintptr_t -sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context) +static inline uintptr_t +getFramePointer(const _STRUCT_MCONTEXT64 *const context) { // We don't want this from stopping us to enable warnings as errors. This needs to be fixed. # pragma clang diagnostic push # pragma GCC diagnostic ignored "-Wshorten-64-to-32" - return arm_thread_state64_get_fp(context->machineContext.__ss); + return arm_thread_state64_get_fp(context->__ss); # pragma clang diagnostic pop } uintptr_t -sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context) +sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context) +{ + return getFramePointer(&context->machineContext); +} + +uintptr_t +sentrycrashcpu_framePointerFromUserContext(const void *const userContext) +{ + return getFramePointer(((const ucontext64_t *)userContext)->uc_mcontext64); +} + +static inline uintptr_t +getStackPointer(const _STRUCT_MCONTEXT64 *const context) { // We don't want this from stopping us to enable warnings as errors. This needs to be fixed. # pragma clang diagnostic push # pragma GCC diagnostic ignored "-Wshorten-64-to-32" - return arm_thread_state64_get_sp(context->machineContext.__ss); + return arm_thread_state64_get_sp(context->__ss); # pragma clang diagnostic pop } uintptr_t -sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context) +sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context) +{ + return getStackPointer(&context->machineContext); +} + +uintptr_t +sentrycrashcpu_stackPointerFromUserContext(const void *const userContext) +{ + return getStackPointer(((const ucontext64_t *)userContext)->uc_mcontext64); +} + +static inline uintptr_t +getInstructionAddress(const _STRUCT_MCONTEXT64 *const context) { // We don't want this from stopping us to enable warnings as errors. This needs to be fixed. # pragma clang diagnostic push # pragma GCC diagnostic ignored "-Wshorten-64-to-32" - return arm_thread_state64_get_pc(context->machineContext.__ss); + return arm_thread_state64_get_pc(context->__ss); # pragma clang diagnostic pop } +uintptr_t +sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context) +{ + return getInstructionAddress(&context->machineContext); +} + +uintptr_t +sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext) +{ + return getInstructionAddress(((const ucontext64_t *)userContext)->uc_mcontext64); +} + uintptr_t sentrycrashcpu_linkRegister(const SentryCrashMachineContext *const context) { diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c index c3e2496da8..65926849c7 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c @@ -65,18 +65,36 @@ sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context) return context->machineContext.__ss.__ebp; } +uintptr_t +sentrycrashcpu_framePointerFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__ebp; +} + uintptr_t sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context) { return context->machineContext.__ss.__esp; } +uintptr_t +sentrycrashcpu_stackPointerFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__esp; +} + uintptr_t sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context) { return context->machineContext.__ss.__eip; } +uintptr_t +sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__eip; +} + uintptr_t sentrycrashcpu_linkRegister(__unused const SentryCrashMachineContext *const context) { diff --git a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c index 08a294fa84..c869ccc4cc 100644 --- a/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c +++ b/Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c @@ -50,18 +50,36 @@ sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context) return context->machineContext.__ss.__rbp; } +uintptr_t +sentrycrashcpu_framePointerFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__rbp; +} + uintptr_t sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context) { return context->machineContext.__ss.__rsp; } +uintptr_t +sentrycrashcpu_stackPointerFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__rsp; +} + uintptr_t sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context) { return context->machineContext.__ss.__rip; } +uintptr_t +sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext) +{ + return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__rip; +} + uintptr_t sentrycrashcpu_linkRegister(__unused const SentryCrashMachineContext *const context) { diff --git a/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m b/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m index 5e96e36ea4..b19b4e1e41 100644 --- a/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m +++ b/Tests/SentryTests/SentryCrash/SentryCrashCPU_Tests.m @@ -32,6 +32,9 @@ #import "TestThread.h" #import +#if defined(__arm64__) +# include +#endif @interface SentryCrashCPU_Tests : XCTestCase @end @@ -81,12 +84,22 @@ - (void)testCPUState XCTAssertTrue(value == 0, @""); uintptr_t address; +#if defined(__arm64__) + ucontext64_t userContext; + userContext.uc_mcontext64 = &machineContext->machineContext; +#else + ucontext_t userContext; + userContext.uc_mcontext = &machineContext->machineContext; +#endif address = sentrycrashcpu_framePointer(machineContext); XCTAssertTrue(address != 0, @""); + XCTAssertEqual(sentrycrashcpu_framePointerFromUserContext(&userContext), address); address = sentrycrashcpu_stackPointer(machineContext); XCTAssertTrue(address != 0, @""); + XCTAssertEqual(sentrycrashcpu_stackPointerFromUserContext(&userContext), address); address = sentrycrashcpu_instructionAddress(machineContext); XCTAssertTrue(address != 0, @""); + XCTAssertEqual(sentrycrashcpu_instructionAddressFromUserContext(&userContext), address); numRegisters = sentrycrashcpu_numExceptionRegisters(); for (int i = 0; i < numRegisters; i++) {