From aaae02b26fb9c905e05d3dc7dd7bd6dd2ede1d28 Mon Sep 17 00:00:00 2001 From: themisw Date: Tue, 16 Dec 2025 14:57:00 -0500 Subject: [PATCH] Conforming to Mach IPC security restrictions --- Crashlytics/CHANGELOG.md | 3 + .../Handlers/FIRCLSMachException.c | 77 +++++++++++++++---- .../Handlers/FIRCLSMachException.h | 11 ++- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/Crashlytics/CHANGELOG.md b/Crashlytics/CHANGELOG.md index 5f1d6c961df..2c7e625b1d9 100644 --- a/Crashlytics/CHANGELOG.md +++ b/Crashlytics/CHANGELOG.md @@ -1,3 +1,6 @@ +# Unrelased +- [fixed] Conformed to Mach IPC security restrictions. (#15393) + # 12.4.0 - [fixed] Make set development platform APIs to chain on Crashlytics context init promise. diff --git a/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c b/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c index 048a975180b..44a3e0227f0 100644 --- a/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c +++ b/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c @@ -33,18 +33,18 @@ static void* FIRCLSMachExceptionServer(void* argument); static bool FIRCLSMachExceptionThreadStart(FIRCLSMachExceptionReadContext* context); static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context, - MachExceptionMessage* message); + MachExceptionProtectedMessage* message); static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context, - MachExceptionMessage* message); + MachExceptionProtectedMessage* message); static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context, - MachExceptionMessage* message, + MachExceptionProtectedMessage* message, kern_return_t result); static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context); static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts, exception_mask_t mask); static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context, - MachExceptionMessage* message); - + MachExceptionProtectedMessage* message); +static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, thread_t* crashedThread); #pragma mark - Initialization void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context) { if (!FIRCLSUnlinkIfExists(context->path)) { @@ -166,7 +166,7 @@ static void* FIRCLSMachExceptionServer(void* argument) { pthread_setname_np("com.google.firebase.crashlytics.MachExceptionServer"); while (1) { - MachExceptionMessage message; + MachExceptionProtectedMessage message; // read the exception message if (!FIRCLSMachExceptionReadMessage(context, &message)) { @@ -188,12 +188,12 @@ static void* FIRCLSMachExceptionServer(void* argument) { } static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context, - MachExceptionMessage* message) { + MachExceptionProtectedMessage* message) { mach_msg_return_t r; - memset(message, 0, sizeof(MachExceptionMessage)); + memset(message, 0, sizeof(MachExceptionProtectedMessage)); - r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionMessage), + r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionProtectedMessage), context->port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (r != MACH_MSG_SUCCESS) { FIRCLSSDKLog("Error receiving mach_msg (%d)\n", r); @@ -206,14 +206,19 @@ static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* conte } static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context, - MachExceptionMessage* message) { + MachExceptionProtectedMessage* message) { FIRCLSSDKLog("Mach exception: 0x%x, count: %d, code: 0x%llx 0x%llx\n", message->exception, message->codeCnt, message->codeCnt > 0 ? message->code[0] : -1, message->codeCnt > 1 ? message->code[1] : -1); // This will happen if a child process raises an exception, as the exception ports are // inherited. - if (message->task.name != mach_task_self()) { + mach_port_t actual_port; + kern_return_t kr; + task_id_token_t token = message->task_id.name; + kr = task_identity_token_get_task_port(token, TASK_FLAVOR_CONTROL, &actual_port); + + if (kr || actual_port != mach_task_self()) { FIRCLSSDKLog("Mach exception task mis-match, returning failure\n"); return KERN_FAILURE; } @@ -240,7 +245,7 @@ static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadC } static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context, - MachExceptionMessage* message, + MachExceptionProtectedMessage* message, kern_return_t result) { MachExceptionReply reply; mach_msg_return_t r; @@ -296,7 +301,7 @@ static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context) // ORing with MACH_EXCEPTION_CODES will produce 64-bit exception data kr = task_swap_exception_ports(task, context->mask, context->port, - EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE, + EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, THREAD_STATE_NONE, context->originalPorts.masks, &context->originalPorts.count, context->originalPorts.ports, context->originalPorts.behaviors, context->originalPorts.flavors); @@ -333,7 +338,7 @@ static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* orig // Finally, mark any masks we registered for that do not have an original port as unused. kr = task_set_exception_ports(mach_task_self(), mask, MACH_PORT_NULL, - EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); + EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); if (kr != KERN_SUCCESS) { FIRCLSSDKLog("unable to unset unregistered mask: 0x%x", mask); return false; @@ -472,7 +477,7 @@ void FIRCLSMachExceptionNameLookup(exception_type_t number, } static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context, - MachExceptionMessage* message) { + MachExceptionProtectedMessage* message) { if (!context || !message) { return false; } @@ -520,13 +525,53 @@ static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context, FIRCLSFileWriteSectionEnd(&file); - FIRCLSHandler(&file, message->thread.name, NULL, true); + thread_t crashedThread; + FIRCLSCrashedThreadLookup(message, &crashedThread); + FIRCLSSDKLog("Crashed threads: %d\n", crashedThread); + FIRCLSHandler(&file, crashedThread, NULL, true); FIRCLSFileClose(&file); return true; } +static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, thread_t* crashedThread) { + thread_act_array_t threadList; + mach_msg_type_number_t threadCount; + + // last 64 bits include thread id info + MachExceptionProtectedThreadInfo protected_thread_info = *(MachExceptionProtectedThreadInfo *) &message->thread_id; + kern_return_t kr = task_threads(mach_task_self(), &threadList, &threadCount); + + if (kr != KERN_SUCCESS) { + FIRCLSSDKLogError("Failed to get threads: %d\n", kr); + return; + } + for (int i = 0; i < threadCount; i++) { + thread_t thread = threadList[i]; + + thread_basic_info_data_t basicInfo; + thread_identifier_info_data_t identifierInfo; + mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT; + + kr = thread_info(thread, THREAD_IDENTIFIER_INFO, (thread_info_t)&identifierInfo, &infoCount); + + if (kr == KERN_SUCCESS) { + FIRCLSSDKLog("Thread %d: Thread port: %d, thread id: %llx\n", i, thread, identifierInfo.thread_id); + + if (protected_thread_info.thread_id == identifierInfo.thread_id) { + FIRCLSSDKLog("Find crashed thread: %d\n", thread); + *crashedThread = thread; + } + } + + // Note: You must deallocate the send right for each thread port + // to prevent port leaks, as task_threads increments the ref count. + mach_port_deallocate(mach_task_self(), thread); + } + vm_deallocate(mach_task_self(), (vm_address_t)threadList, threadCount * sizeof(thread_t)); +} + #else INJECT_STRIP_SYMBOL(cls_mach_exception) diff --git a/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h b/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h index ca85501ec66..93453847a40 100644 --- a/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h +++ b/Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h @@ -31,15 +31,20 @@ typedef struct { mach_msg_header_t head; /* start of the kernel processed data */ mach_msg_body_t msgh_body; - mach_msg_port_descriptor_t thread; - mach_msg_port_descriptor_t task; + mach_msg_port_descriptor_t task_id; + mach_msg_port_descriptor_t thread_id; /* end of the kernel processed data */ NDR_record_t NDR; exception_type_t exception; mach_msg_type_number_t codeCnt; mach_exception_data_type_t code[EXCEPTION_CODE_MAX]; mach_msg_trailer_t trailer; -} MachExceptionMessage; +} MachExceptionProtectedMessage; + +typedef struct { + uint64_t pad1; + uint64_t thread_id; +} MachExceptionProtectedThreadInfo; typedef struct { mach_msg_header_t head;