Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Crashlytics/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.

Expand Down
77 changes: 61 additions & 16 deletions Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -472,7 +477,7 @@ void FIRCLSMachExceptionNameLookup(exception_type_t number,
}

static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
MachExceptionMessage* message) {
MachExceptionProtectedMessage* message) {
if (!context || !message) {
return false;
}
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 8 additions & 3 deletions Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading