Skip to content

Commit 20cb2db

Browse files
committed
fix: interop with managed Mono/CoreCLR runtimes
1 parent 670b474 commit 20cb2db

File tree

8 files changed

+186
-10
lines changed

8 files changed

+186
-10
lines changed

Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,12 @@ installExceptionHandler(void)
486486
}
487487
}
488488

489+
if (sentrycrashcm_isManagedRuntime()) {
490+
SENTRY_ASYNC_SAFE_LOG_DEBUG("Detected managed Mono/CoreCLR runtime. Not registering "
491+
"exception port for EXC_BAD_ACCESS or EXC_MASK_ARITHMETIC.");
492+
mask &= ~(EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC);
493+
}
494+
489495
SENTRY_ASYNC_SAFE_LOG_DEBUG("Installing port as exception handler.");
490496
kr = task_set_exception_ports(thisTask, mask, g_exceptionPort,
491497
(int)(EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES), THREAD_STATE_NONE);
@@ -562,6 +568,23 @@ addContextualInfoToEvent(struct SentryCrash_MonitorContext *eventContext)
562568

563569
#endif // SENTRY_HAS_MACH
564570

571+
bool
572+
sentrycrashcm_isManagedRuntime(void)
573+
{
574+
#if SENTRY_HAS_MACH
575+
for (mach_msg_type_number_t i = 0; i < g_previousExceptionPorts.count; i++) {
576+
// https://github.com/dotnet/runtime/blob/6d96e28597e7da0d790d495ba834cc4908e442cd/src/mono/mono/mini/mini-darwin.c#L85-L90
577+
// https://github.com/dotnet/runtime/blob/6d96e28597e7da0d790d495ba834cc4908e442cd/src/coreclr/nativeaot/Runtime/unix/HardwareExceptions.cpp#L615-L621
578+
if (g_previousExceptionPorts.masks[i] == (EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC)
579+
&& g_previousExceptionPorts.behaviors[i] == EXCEPTION_STATE_IDENTITY
580+
&& g_previousExceptionPorts.flavors[i] == MACHINE_THREAD_STATE) {
581+
return true;
582+
}
583+
}
584+
#endif // SENTRY_HAS_MACH
585+
return false;
586+
}
587+
565588
SentryCrashMonitorAPI *
566589
sentrycrashcm_machexception_getAPI(void)
567590
{

Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_MachException.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ bool sentrycrashcm_isReservedThread(thread_t thread);
4949
*/
5050
bool sentrycrashcm_hasReservedThreads(void);
5151

52+
/** Detect managed Mono/CoreCLR runtime's Mach exception port.
53+
*/
54+
bool sentrycrashcm_isManagedRuntime(void);
55+
5256
#ifdef __cplusplus
5357
}
5458
#endif

Sources/SentryCrash/Recording/Monitors/SentryCrashMonitor_Signal.c

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@
2626
//
2727

2828
#include "SentryCrashMonitor_Signal.h"
29+
#include "SentryCrashCPU.h"
2930
#include "SentryCrashID.h"
3031
#include "SentryCrashMachineContext.h"
3132
#include "SentryCrashMonitorContext.h"
33+
#include "SentryCrashMonitor_MachException.h"
3234
#include "SentryCrashSignalInfo.h"
3335
#include "SentryCrashStackCursor_MachineContext.h"
3436
#include "SentryInternalCDefines.h"
@@ -49,6 +51,7 @@
4951

5052
static volatile bool g_isEnabled = false;
5153
static bool g_isSigtermReportingEnabled = false;
54+
static bool g_isManagedRuntime = false;
5255

5356
static SentryCrash_MonitorContext g_monitorContext;
5457
static SentryCrashStackCursor g_stackCursor;
@@ -67,6 +70,29 @@ static char g_eventID[37];
6770
# pragma mark - Callbacks -
6871
// ============================================================================
6972

73+
/**
74+
*
75+
*/
76+
static void
77+
invokePreviousSignalHandlers(int sigNum, siginfo_t *signalInfo, void *userContext)
78+
{
79+
const int *fatalSignals = sentrycrashsignal_fatalSignals();
80+
int fatalSignalsCount = sentrycrashsignal_numFatalSignals();
81+
82+
for (int i = 0; i < fatalSignalsCount; ++i) {
83+
if (fatalSignals[i] == sigNum) {
84+
struct sigaction *handler = &g_previousSignalHandlers[i];
85+
if (handler->sa_flags & SA_SIGINFO) {
86+
handler->sa_sigaction(sigNum, signalInfo, userContext);
87+
} else if (handler->sa_handler != SIG_DFL && handler->sa_handler != SIG_IGN) {
88+
// This handler can only handle to signal number (ANSI C)
89+
void (*func)(int) = handler->sa_handler;
90+
func(sigNum);
91+
}
92+
}
93+
}
94+
}
95+
7096
/** Our custom signal handler.
7197
* Restore the default signal handlers, record the signal information, and
7298
* write a crash report.
@@ -83,6 +109,29 @@ static void
83109
handleSignal(int sigNum, siginfo_t *signalInfo, void *userContext)
84110
{
85111
SENTRY_ASYNC_SAFE_LOG_DEBUG("Trapped signal %d", sigNum);
112+
113+
// Let managed Mono/CoreCLR runtime handle the signal first,
114+
// as they may convert it into a managed exception.
115+
if (g_isManagedRuntime) {
116+
SENTRY_ASYNC_SAFE_LOG_DEBUG(
117+
"Detected managed Mono/CoreCLR runtime. Passing signal to previous handlers.");
118+
119+
uintptr_t sp = sentrycrashcpu_stackPointerFromUserContext(userContext);
120+
uintptr_t ip = sentrycrashcpu_instructionAddressFromUserContext(userContext);
121+
122+
invokePreviousSignalHandlers(sigNum, signalInfo, userContext);
123+
124+
// If the stack or instruction pointer changed, the managed runtime
125+
// converted the signal into a managed exception and changed the context.
126+
// https://github.com/dotnet/runtime/blob/6d96e28597e7da0d790d495ba834cc4908e442cd/src/mono/mono/mini/exceptions-arm64.c#L538
127+
if (sp != sentrycrashcpu_stackPointerFromUserContext(userContext)
128+
|| ip != sentrycrashcpu_instructionAddressFromUserContext(userContext)) {
129+
SENTRY_ASYNC_SAFE_LOG_DEBUG(
130+
"Signal converted to a managed exception. Aborting signal handling.");
131+
return;
132+
}
133+
}
134+
86135
if (g_isEnabled) {
87136
thread_act_array_t threads = NULL;
88137
mach_msg_type_number_t numThreads = 0;
@@ -110,9 +159,13 @@ handleSignal(int sigNum, siginfo_t *signalInfo, void *userContext)
110159
sentrycrashmc_resumeEnvironment(threads, numThreads);
111160
}
112161

113-
SENTRY_ASYNC_SAFE_LOG_DEBUG("Re-raising signal for regular handlers to catch.");
114-
// This is technically not allowed, but it works in OSX and iOS.
115-
raise(sigNum);
162+
// Re-raise the signal to invoke the previous handlers unless already called
163+
// above for managed runtimes.
164+
if (!g_isManagedRuntime) {
165+
SENTRY_ASYNC_SAFE_LOG_DEBUG("Re-raising signal for regular handlers to catch.");
166+
// This is technically not allowed, but it works in OSX and iOS.
167+
raise(sigNum);
168+
}
116169
}
117170

118171
// ============================================================================
@@ -124,6 +177,8 @@ installSignalHandler(void)
124177
{
125178
SENTRY_ASYNC_SAFE_LOG_DEBUG("Installing signal handler.");
126179

180+
g_isManagedRuntime = sentrycrashcm_isManagedRuntime();
181+
127182
# if SENTRY_HAS_SIGNAL_STACK
128183

129184
if (g_signalStack.ss_size == 0) {

Sources/SentryCrash/Recording/Tools/SentryCrashCPU.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const char *sentrycrashcpu_currentArch(void);
5151
* @return The context's frame pointer.
5252
*/
5353
uintptr_t sentrycrashcpu_framePointer(const struct SentryCrashMachineContext *const context);
54+
uintptr_t sentrycrashcpu_framePointerFromUserContext(const void *const userContext);
5455

5556
/** Get the current stack pointer for a machine context.
5657
*
@@ -59,6 +60,7 @@ uintptr_t sentrycrashcpu_framePointer(const struct SentryCrashMachineContext *co
5960
* @return The context's stack pointer.
6061
*/
6162
uintptr_t sentrycrashcpu_stackPointer(const struct SentryCrashMachineContext *const context);
63+
uintptr_t sentrycrashcpu_stackPointerFromUserContext(const void *const userContext);
6264

6365
/** Get the address of the instruction about to be, or being executed by a
6466
* machine context.
@@ -68,6 +70,7 @@ uintptr_t sentrycrashcpu_stackPointer(const struct SentryCrashMachineContext *co
6870
* @return The context's next instruction address.
6971
*/
7072
uintptr_t sentrycrashcpu_instructionAddress(const struct SentryCrashMachineContext *const context);
73+
uintptr_t sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext);
7174

7275
/** Get the address stored in the link register (arm only). This may
7376
* contain the first return address of the stack.

Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,36 @@ sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context)
4949
return context->machineContext.__ss.__r[7];
5050
}
5151

52+
uintptr_t
53+
sentrycrashcpu_framePointerFromUserContext(const void *const userContext)
54+
{
55+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__r[7];
56+
}
57+
5258
uintptr_t
5359
sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context)
5460
{
5561
return context->machineContext.__ss.__sp;
5662
}
5763

64+
uintptr_t
65+
sentrycrashcpu_stackPointerFromUserContext(const void *const context)
66+
{
67+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__sp;
68+
}
69+
5870
uintptr_t
5971
sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context)
6072
{
6173
return context->machineContext.__ss.__pc;
6274
}
6375

76+
uintptr_t
77+
sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext)
78+
{
79+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__pc;
80+
}
81+
6482
uintptr_t
6583
sentrycrashcpu_linkRegister(const SentryCrashMachineContext *const context)
6684
{

Sources/SentryCrash/Recording/Tools/SentryCrashCPU_arm64.c

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
# include "SentryCrashMachineContext.h"
3333
# include "SentryCrashMachineContext_Apple.h"
3434
# include <stdlib.h>
35+
# include <sys/_types/_ucontext64.h>
3536

3637
# include "SentryAsyncSafeLog.h"
3738

@@ -46,36 +47,72 @@ static const char *g_exceptionRegisterNames[] = { "exception", "esr", "far" };
4647
static const int g_exceptionRegisterNamesCount
4748
= sizeof(g_exceptionRegisterNames) / sizeof(*g_exceptionRegisterNames);
4849

49-
uintptr_t
50-
sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context)
50+
static inline uintptr_t
51+
getFramePointer(const _STRUCT_MCONTEXT64 *const context)
5152
{
5253
// We don't want this from stopping us to enable warnings as errors. This needs to be fixed.
5354
# pragma clang diagnostic push
5455
# pragma GCC diagnostic ignored "-Wshorten-64-to-32"
55-
return arm_thread_state64_get_fp(context->machineContext.__ss);
56+
return arm_thread_state64_get_fp(context->__ss);
5657
# pragma clang diagnostic pop
5758
}
5859

5960
uintptr_t
60-
sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context)
61+
sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context)
62+
{
63+
return getFramePointer(&context->machineContext);
64+
}
65+
66+
uintptr_t
67+
sentrycrashcpu_framePointerFromUserContext(const void *const userContext)
68+
{
69+
return getFramePointer(((const ucontext64_t *)userContext)->uc_mcontext64);
70+
}
71+
72+
static inline uintptr_t
73+
getStackPointer(const _STRUCT_MCONTEXT64 *const context)
6174
{
6275
// We don't want this from stopping us to enable warnings as errors. This needs to be fixed.
6376
# pragma clang diagnostic push
6477
# pragma GCC diagnostic ignored "-Wshorten-64-to-32"
65-
return arm_thread_state64_get_sp(context->machineContext.__ss);
78+
return arm_thread_state64_get_sp(context->__ss);
6679
# pragma clang diagnostic pop
6780
}
6881

6982
uintptr_t
70-
sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context)
83+
sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context)
84+
{
85+
return getStackPointer(&context->machineContext);
86+
}
87+
88+
uintptr_t
89+
sentrycrashcpu_stackPointerFromUserContext(const void *const userContext)
90+
{
91+
return getStackPointer(((const ucontext64_t *)userContext)->uc_mcontext64);
92+
}
93+
94+
static inline uintptr_t
95+
getInstructionAddress(const _STRUCT_MCONTEXT64 *const context)
7196
{
7297
// We don't want this from stopping us to enable warnings as errors. This needs to be fixed.
7398
# pragma clang diagnostic push
7499
# pragma GCC diagnostic ignored "-Wshorten-64-to-32"
75-
return arm_thread_state64_get_pc(context->machineContext.__ss);
100+
return arm_thread_state64_get_pc(context->__ss);
76101
# pragma clang diagnostic pop
77102
}
78103

104+
uintptr_t
105+
sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context)
106+
{
107+
return getInstructionAddress(&context->machineContext);
108+
}
109+
110+
uintptr_t
111+
sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext)
112+
{
113+
return getInstructionAddress(((const ucontext64_t *)userContext)->uc_mcontext64);
114+
}
115+
79116
uintptr_t
80117
sentrycrashcpu_linkRegister(const SentryCrashMachineContext *const context)
81118
{

Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_32.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,18 +65,36 @@ sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context)
6565
return context->machineContext.__ss.__ebp;
6666
}
6767

68+
uintptr_t
69+
sentrycrashcpu_framePointerFromUserContext(const void *const userContext)
70+
{
71+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__ebp;
72+
}
73+
6874
uintptr_t
6975
sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context)
7076
{
7177
return context->machineContext.__ss.__esp;
7278
}
7379

80+
uintptr_t
81+
sentrycrashcpu_stackPointerFromUserContext(const void *const userContext)
82+
{
83+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__esp;
84+
}
85+
7486
uintptr_t
7587
sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context)
7688
{
7789
return context->machineContext.__ss.__eip;
7890
}
7991

92+
uintptr_t
93+
sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext)
94+
{
95+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__eip;
96+
}
97+
8098
uintptr_t
8199
sentrycrashcpu_linkRegister(__unused const SentryCrashMachineContext *const context)
82100
{

Sources/SentryCrash/Recording/Tools/SentryCrashCPU_x86_64.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,36 @@ sentrycrashcpu_framePointer(const SentryCrashMachineContext *const context)
5050
return context->machineContext.__ss.__rbp;
5151
}
5252

53+
uintptr_t
54+
sentrycrashcpu_framePointerFromUserContext(const void *const userContext)
55+
{
56+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__rbp;
57+
}
58+
5359
uintptr_t
5460
sentrycrashcpu_stackPointer(const SentryCrashMachineContext *const context)
5561
{
5662
return context->machineContext.__ss.__rsp;
5763
}
5864

65+
uintptr_t
66+
sentrycrashcpu_stackPointerFromUserContext(const void *const userContext)
67+
{
68+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__rsp;
69+
}
70+
5971
uintptr_t
6072
sentrycrashcpu_instructionAddress(const SentryCrashMachineContext *const context)
6173
{
6274
return context->machineContext.__ss.__rip;
6375
}
6476

77+
uintptr_t
78+
sentrycrashcpu_instructionAddressFromUserContext(const void *const userContext)
79+
{
80+
return ((const ucontext_t *)userContext)->uc_mcontext->__ss.__rip;
81+
}
82+
6583
uintptr_t
6684
sentrycrashcpu_linkRegister(__unused const SentryCrashMachineContext *const context)
6785
{

0 commit comments

Comments
 (0)