Skip to content
Merged
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
7 changes: 7 additions & 0 deletions Crashlytics/Crashlytics/Components/FIRCLSContext.m
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@
_firclsContext.readonly->machException.path =
FIRCLSContextAppendToRoot(rootPath, FIRCLSReportMachExceptionFile);

// MacOS12 below does not support identity protected behavior
// check releases here: https://opensource.apple.com/releases/
// TODO: remove EXCEPTION_DEFAULT support when we bump min MacOS support to 12+
_firclsContext.readonly->machException.behavior = EXCEPTION_DEFAULT;
if (@available(macOS 12, *)) {
_firclsContext.readonly->machException.behavior = EXCEPTION_IDENTITY_PROTECTED;
}
FIRCLSMachExceptionInit(&_firclsContext.readonly->machException);
});
#endif
Expand Down
125 changes: 78 additions & 47 deletions Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,19 @@
static void* FIRCLSMachExceptionServer(void* argument);
static bool FIRCLSMachExceptionThreadStart(FIRCLSMachExceptionReadContext* context);
static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context,
MachExceptionProtectedMessage* message);
union MachExceptionMessage* message);
static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context,
MachExceptionProtectedMessage* message);
union MachExceptionMessage* message);
static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context,
MachExceptionProtectedMessage* message,
union MachExceptionMessage* message,
kern_return_t result);
static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context);
static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts,
exception_mask_t mask);
exception_mask_t mask,
exception_behavior_t behavior);
static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
MachExceptionProtectedMessage* message);
static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, thread_t* crashedThread);
union MachExceptionMessage* message);
static void FIRCLSCrashedThreadLookup(union MachExceptionMessage* message, thread_t* crashedThread);
#pragma mark - Initialization
void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context) {
if (!FIRCLSUnlinkIfExists(context->path)) {
Expand All @@ -58,7 +59,7 @@ void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context) {

if (!FIRCLSMachExceptionThreadStart(context)) {
FIRCLSSDKLog("Unable to start thread\n");
FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask);
FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask, context->behavior);
}
}

Expand Down Expand Up @@ -166,7 +167,7 @@ static void* FIRCLSMachExceptionServer(void* argument) {
pthread_setname_np("com.google.firebase.crashlytics.MachExceptionServer");

while (1) {
MachExceptionProtectedMessage message;
union MachExceptionMessage message;

// read the exception message
if (!FIRCLSMachExceptionReadMessage(context, &message)) {
Expand All @@ -188,13 +189,15 @@ static void* FIRCLSMachExceptionServer(void* argument) {
}

static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context,
MachExceptionProtectedMessage* message) {
union MachExceptionMessage* message) {
mach_msg_return_t r;

memset(message, 0, sizeof(MachExceptionProtectedMessage));
memset(message, 0, sizeof(union MachExceptionMessage));

r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionProtectedMessage),
// no need if condition here since head structure for union MachExceptionMessage is the same.
r = mach_msg(&message->default_message.head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(union MachExceptionMessage),
context->port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);

if (r != MACH_MSG_SUCCESS) {
FIRCLSSDKLog("Error receiving mach_msg (%d)\n", r);
return false;
Expand All @@ -206,33 +209,43 @@ static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* conte
}

static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context,
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.
mach_port_t actual_port;
union MachExceptionMessage* message) {
kern_return_t kr;
task_id_token_t token = message->task_id_token_t.name;
kr = task_identity_token_get_task_port(token, TASK_FLAVOR_CONTROL, &actual_port);

if (kr != KERN_SUCCESS) {
FIRCLSSDKLog("Could not find task from task id token. returning failure\n");
return KERN_FAILURE;
}
if (context->behavior == EXCEPTION_DEFAULT) {
FIRCLSSDKLog("Mach exception: 0x%x, count: %d, code: 0x%llx 0x%llx\n", message->default_message.exception,
message->default_message.codeCnt, message->default_message.codeCnt > 0 ? message->default_message.code[0] : -1,
message->default_message.codeCnt > 1 ? message->default_message.code[1] : -1);
if(message->default_message.task.name != mach_task_self()) {
FIRCLSSDKLog("Mach exception task mis-match, returning failure\n");
return KERN_FAILURE;
}
} else {
FIRCLSSDKLog("Mach exception: 0x%x, count: %d, code: 0x%llx 0x%llx\n", message->protected_message.exception,
message->protected_message.codeCnt, message->protected_message.codeCnt > 0 ? message->protected_message.code[0] : -1,
message->protected_message.codeCnt > 1 ? message->protected_message.code[1] : -1);

const bool is_mismatch = (actual_port != mach_task_self());
mach_port_deallocate(mach_task_self(), actual_port);
// This will happen if a child process raises an exception, as the exception ports are
// inherited.
mach_port_t actual_port;
task_id_token_t token = message->protected_message.task_id_token_t.name;
kr = task_identity_token_get_task_port(token, TASK_FLAVOR_CONTROL, &actual_port);

if (is_mismatch) {
FIRCLSSDKLog("Mach exception task mis-match, returning failure\n");
return KERN_FAILURE;
if (kr != KERN_SUCCESS) {
FIRCLSSDKLog("Could not find task from task id token. returning failure\n");
return KERN_FAILURE;
}
const bool is_mismatch = (actual_port != mach_task_self());
mach_port_deallocate(mach_task_self(), actual_port);

if (is_mismatch) {
FIRCLSSDKLog("Mach exception task mis-match, returning failure\n");
return KERN_FAILURE;
}
}

FIRCLSSDKLog("Unregistering handler\n");
if (!FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask)) {
if (!FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask, context->behavior)) {
FIRCLSSDKLog("Failed to unregister\n");
return KERN_FAILURE;
}
Expand All @@ -253,17 +266,17 @@ static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadC
}

static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context,
MachExceptionProtectedMessage* message,
union MachExceptionMessage* message,
kern_return_t result) {
MachExceptionReply reply;
mach_msg_return_t r;

// prepare the reply
reply.head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(message->head.msgh_bits), 0);
reply.head.msgh_remote_port = message->head.msgh_remote_port;
reply.head.msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(message->default_message.head.msgh_bits), 0);
reply.head.msgh_remote_port = message->default_message.head.msgh_remote_port;
reply.head.msgh_size = (mach_msg_size_t)sizeof(MachExceptionReply);
reply.head.msgh_local_port = MACH_PORT_NULL;
reply.head.msgh_id = message->head.msgh_id + 100;
reply.head.msgh_id = message->default_message.head.msgh_id + 100;

reply.NDR = NDR_record;

Expand Down Expand Up @@ -309,7 +322,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_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
context->behavior | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
context->originalPorts.masks, &context->originalPorts.count,
context->originalPorts.ports, context->originalPorts.behaviors,
context->originalPorts.flavors);
Expand All @@ -328,7 +341,8 @@ static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context)
}

static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts,
exception_mask_t mask) {
exception_mask_t mask,
exception_behavior_t behavior) {
kern_return_t kr;

// Re-register all the old ports.
Expand All @@ -346,7 +360,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_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
behavior | 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 @@ -485,7 +499,7 @@ void FIRCLSMachExceptionNameLookup(exception_type_t number,
}

static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
MachExceptionProtectedMessage* message) {
union MachExceptionMessage* message) {
if (!context || !message) {
return false;
}
Expand All @@ -507,21 +521,34 @@ static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,

FIRCLSFileWriteHashStart(&file);

FIRCLSFileWriteHashEntryUint64(&file, "exception", message->exception);
exception_type_t exception;
mach_msg_type_number_t codeCnt;
mach_exception_data_type_t* code;

if (context->behavior == EXCEPTION_DEFAULT) {
exception = message->default_message.exception;
codeCnt = message->default_message.codeCnt;
code = message->default_message.code;
} else {
exception = message->protected_message.exception;
codeCnt = message->protected_message.codeCnt;
code = message->protected_message.code;
}

FIRCLSFileWriteHashEntryUint64(&file, "exception", exception);

// record the codes
FIRCLSFileWriteHashKey(&file, "codes");
FIRCLSFileWriteArrayStart(&file);
for (mach_msg_type_number_t i = 0; i < message->codeCnt; ++i) {
FIRCLSFileWriteArrayEntryUint64(&file, message->code[i]);
for (mach_msg_type_number_t i = 0; i < codeCnt; ++i) {
FIRCLSFileWriteArrayEntryUint64(&file, code[i]);
}
FIRCLSFileWriteArrayEnd(&file);

const char* name = NULL;
const char* codeName = NULL;

FIRCLSMachExceptionNameLookup(message->exception, message->codeCnt > 0 ? message->code[0] : 0,
&name, &codeName);
FIRCLSMachExceptionNameLookup(exception, codeCnt > 0 ? code[0] : 0, &name, &codeName);

FIRCLSFileWriteHashEntryString(&file, "name", name);
FIRCLSFileWriteHashEntryString(&file, "code_name", codeName);
Expand All @@ -534,7 +561,11 @@ static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
FIRCLSFileWriteSectionEnd(&file);

thread_t crashedThread = THREAD_NULL;
FIRCLSCrashedThreadLookup(message, &crashedThread);
if (context->behavior == EXCEPTION_DEFAULT) {
crashedThread = message->default_message.thread.name;
} else {
FIRCLSCrashedThreadLookup(message, &crashedThread);
}
FIRCLSSDKLog("Crashed threads: %d\n", crashedThread);
FIRCLSHandler(&file, crashedThread, NULL, true);
if (crashedThread != THREAD_NULL) {
Expand All @@ -545,7 +576,7 @@ static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
return true;
}

static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, thread_t* crashedThread) {
static void FIRCLSCrashedThreadLookup(union MachExceptionMessage* message, thread_t* crashedThread) {
thread_act_array_t threadList;
mach_msg_type_number_t threadCount;

Expand All @@ -564,7 +595,7 @@ static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, th

if (kr == KERN_SUCCESS) {
FIRCLSSDKLog("Thread %d: Thread port: %d, thread id: %llx\n", i, threadList[i], identifierInfo.thread_id);
if (message->thread_id == identifierInfo.thread_id) {
if (message->protected_message.thread_id == identifierInfo.thread_id) {
FIRCLSSDKLog("Find crashed thread: %d\n", threadList[i]);
*crashedThread = threadList[i];
break;
Expand Down
20 changes: 20 additions & 0 deletions Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,25 @@ typedef struct {
mach_msg_trailer_t trailer;
} MachExceptionProtectedMessage;

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;
/* 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;
} MachExceptionDefaultMessage;

union MachExceptionMessage {
MachExceptionProtectedMessage protected_message;
MachExceptionDefaultMessage default_message;
};

typedef struct {
mach_msg_header_t head;
NDR_record_t NDR;
Expand All @@ -65,6 +84,7 @@ typedef struct {

exception_mask_t mask;
FIRCLSMachExceptionOriginalPorts originalPorts;
exception_behavior_t behavior;
} FIRCLSMachExceptionReadContext;

#pragma mark - API
Expand Down
Loading