Skip to content

Commit 098de4d

Browse files
authored
Conforming to Mach IPC security restrictions (#15612)
1 parent 09dcec1 commit 098de4d

File tree

3 files changed

+77
-20
lines changed

3 files changed

+77
-20
lines changed

Crashlytics/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Unreleased
2+
- [fixed] Conformed to Mach IPC security restrictions. (#15393)
3+
14
# 12.4.0
25
- [fixed] Make set development platform APIs to chain on Crashlytics context init promise.
36

Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c

Lines changed: 69 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@
3333
static void* FIRCLSMachExceptionServer(void* argument);
3434
static bool FIRCLSMachExceptionThreadStart(FIRCLSMachExceptionReadContext* context);
3535
static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context,
36-
MachExceptionMessage* message);
36+
MachExceptionProtectedMessage* message);
3737
static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context,
38-
MachExceptionMessage* message);
38+
MachExceptionProtectedMessage* message);
3939
static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context,
40-
MachExceptionMessage* message,
40+
MachExceptionProtectedMessage* message,
4141
kern_return_t result);
4242
static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context);
4343
static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts,
4444
exception_mask_t mask);
4545
static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
46-
MachExceptionMessage* message);
47-
46+
MachExceptionProtectedMessage* message);
47+
static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, thread_t* crashedThread);
4848
#pragma mark - Initialization
4949
void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context) {
5050
if (!FIRCLSUnlinkIfExists(context->path)) {
@@ -166,7 +166,7 @@ static void* FIRCLSMachExceptionServer(void* argument) {
166166
pthread_setname_np("com.google.firebase.crashlytics.MachExceptionServer");
167167

168168
while (1) {
169-
MachExceptionMessage message;
169+
MachExceptionProtectedMessage message;
170170

171171
// read the exception message
172172
if (!FIRCLSMachExceptionReadMessage(context, &message)) {
@@ -188,12 +188,12 @@ static void* FIRCLSMachExceptionServer(void* argument) {
188188
}
189189

190190
static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context,
191-
MachExceptionMessage* message) {
191+
MachExceptionProtectedMessage* message) {
192192
mach_msg_return_t r;
193193

194-
memset(message, 0, sizeof(MachExceptionMessage));
194+
memset(message, 0, sizeof(MachExceptionProtectedMessage));
195195

196-
r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionMessage),
196+
r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionProtectedMessage),
197197
context->port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
198198
if (r != MACH_MSG_SUCCESS) {
199199
FIRCLSSDKLog("Error receiving mach_msg (%d)\n", r);
@@ -206,14 +206,27 @@ static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* conte
206206
}
207207

208208
static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context,
209-
MachExceptionMessage* message) {
209+
MachExceptionProtectedMessage* message) {
210210
FIRCLSSDKLog("Mach exception: 0x%x, count: %d, code: 0x%llx 0x%llx\n", message->exception,
211211
message->codeCnt, message->codeCnt > 0 ? message->code[0] : -1,
212212
message->codeCnt > 1 ? message->code[1] : -1);
213213

214214
// This will happen if a child process raises an exception, as the exception ports are
215215
// inherited.
216-
if (message->task.name != mach_task_self()) {
216+
mach_port_t actual_port;
217+
kern_return_t kr;
218+
task_id_token_t token = message->task_id_token_t.name;
219+
kr = task_identity_token_get_task_port(token, TASK_FLAVOR_CONTROL, &actual_port);
220+
221+
if (kr != KERN_SUCCESS) {
222+
FIRCLSSDKLog("Could not find task from task id token. returning failure\n");
223+
return KERN_FAILURE;
224+
}
225+
226+
const bool is_mismatch = (actual_port != mach_task_self());
227+
mach_port_deallocate(mach_task_self(), actual_port);
228+
229+
if (is_mismatch) {
217230
FIRCLSSDKLog("Mach exception task mis-match, returning failure\n");
218231
return KERN_FAILURE;
219232
}
@@ -240,7 +253,7 @@ static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadC
240253
}
241254

242255
static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context,
243-
MachExceptionMessage* message,
256+
MachExceptionProtectedMessage* message,
244257
kern_return_t result) {
245258
MachExceptionReply reply;
246259
mach_msg_return_t r;
@@ -296,7 +309,7 @@ static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context)
296309

297310
// ORing with MACH_EXCEPTION_CODES will produce 64-bit exception data
298311
kr = task_swap_exception_ports(task, context->mask, context->port,
299-
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
312+
EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
300313
context->originalPorts.masks, &context->originalPorts.count,
301314
context->originalPorts.ports, context->originalPorts.behaviors,
302315
context->originalPorts.flavors);
@@ -333,7 +346,7 @@ static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* orig
333346

334347
// Finally, mark any masks we registered for that do not have an original port as unused.
335348
kr = task_set_exception_ports(mach_task_self(), mask, MACH_PORT_NULL,
336-
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
349+
EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
337350
if (kr != KERN_SUCCESS) {
338351
FIRCLSSDKLog("unable to unset unregistered mask: 0x%x", mask);
339352
return false;
@@ -472,7 +485,7 @@ void FIRCLSMachExceptionNameLookup(exception_type_t number,
472485
}
473486

474487
static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
475-
MachExceptionMessage* message) {
488+
MachExceptionProtectedMessage* message) {
476489
if (!context || !message) {
477490
return false;
478491
}
@@ -520,13 +533,52 @@ static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
520533

521534
FIRCLSFileWriteSectionEnd(&file);
522535

523-
FIRCLSHandler(&file, message->thread.name, NULL, true);
524-
536+
thread_t crashedThread = THREAD_NULL;
537+
FIRCLSCrashedThreadLookup(message, &crashedThread);
538+
FIRCLSSDKLog("Crashed threads: %d\n", crashedThread);
539+
FIRCLSHandler(&file, crashedThread, NULL, true);
540+
if (crashedThread != THREAD_NULL) {
541+
mach_port_deallocate(mach_task_self(), crashedThread);
542+
}
525543
FIRCLSFileClose(&file);
526544

527545
return true;
528546
}
529547

548+
static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, thread_t* crashedThread) {
549+
thread_act_array_t threadList;
550+
mach_msg_type_number_t threadCount;
551+
552+
kern_return_t kr = task_threads(mach_task_self(), &threadList, &threadCount);
553+
if (kr != KERN_SUCCESS) {
554+
FIRCLSSDKLogError("Failed to get threads: %d\n", kr);
555+
return;
556+
}
557+
558+
// Find the crashed thread.
559+
for (int i = 0; i < threadCount; i++) {
560+
thread_identifier_info_data_t identifierInfo;
561+
mach_msg_type_number_t infoCount = THREAD_IDENTIFIER_INFO_COUNT;
562+
563+
kr = thread_info(threadList[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&identifierInfo, &infoCount);
564+
565+
if (kr == KERN_SUCCESS) {
566+
FIRCLSSDKLog("Thread %d: Thread port: %d, thread id: %llx\n", i, threadList[i], identifierInfo.thread_id);
567+
if (message->thread_id == identifierInfo.thread_id) {
568+
FIRCLSSDKLog("Find crashed thread: %d\n", threadList[i]);
569+
*crashedThread = threadList[i];
570+
break;
571+
}
572+
}
573+
}
574+
for (int i = 0; i < threadCount; i++) {
575+
if (threadList[i] != *crashedThread) {
576+
mach_port_deallocate(mach_task_self(), threadList[i]);
577+
}
578+
}
579+
vm_deallocate(mach_task_self(), (vm_address_t)threadList, threadCount * sizeof(thread_t));
580+
}
581+
530582
#else
531583

532584
INJECT_STRIP_SYMBOL(cls_mach_exception)

Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,21 @@
2727

2828
#pragma mark Structures
2929
#pragma pack(push, 4)
30+
// run `mig -DMACH_EXC_SERVER_TASKIDTOKEN_STATE=1 mach/mach_exc.defs`
31+
// check mach_exc.h
3032
typedef struct {
3133
mach_msg_header_t head;
3234
/* start of the kernel processed data */
3335
mach_msg_body_t msgh_body;
34-
mach_msg_port_descriptor_t thread;
35-
mach_msg_port_descriptor_t task;
36+
mach_msg_port_descriptor_t task_id_token_t;
3637
/* end of the kernel processed data */
3738
NDR_record_t NDR;
39+
uint64_t thread_id;
3840
exception_type_t exception;
3941
mach_msg_type_number_t codeCnt;
4042
mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
4143
mach_msg_trailer_t trailer;
42-
} MachExceptionMessage;
44+
} MachExceptionProtectedMessage;
4345

4446
typedef struct {
4547
mach_msg_header_t head;

0 commit comments

Comments
 (0)