Skip to content

Commit 5266d89

Browse files
committed
make mach behavior configurable
1 parent c7a16af commit 5266d89

File tree

8 files changed

+84
-32
lines changed

8 files changed

+84
-32
lines changed

Crashlytics/CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# Unrelased
2-
- [fixed] Conformed to Mach IPC security restrictions. Note: This change would potentially change mach exception types we receive from kernel which might affect issue clustering result. (#15393)
1+
# Unreleased
2+
- [fixed] Added ability to conformed to Mach IPC security restrictions. User need to add FirebaseCrashlyticsMachProtectedEnabled=YES to their info.plist to enable this feature. Note: This change would potentially change mach exception types we receive from kernel which might affect issue clustering result. (#15393)
33

44
# 12.4.0
55
- [fixed] Make set development platform APIs to chain on Crashlytics context init promise.

Crashlytics/Crashlytics/Components/FIRCLSContext.m

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
initData.previouslyCrashedFileRootPath = [fileManager rootPath];
6666
initData.errorsEnabled = [settings errorReportingEnabled];
6767
initData.customExceptionsEnabled = [settings customExceptionsEnabled];
68+
initData.machProtectedEnabled = [settings machProtectedEnabled];
6869
initData.maxCustomExceptions = [settings maxCustomExceptions];
6970
initData.maxErrorLogSize = [settings errorLogBufferSize];
7071
initData.maxLogSize = [settings logBufferSize];
@@ -185,6 +186,12 @@
185186
_firclsContext.readonly->machException.path =
186187
FIRCLSContextAppendToRoot(rootPath, FIRCLSReportMachExceptionFile);
187188

189+
// from settings checkout behavior
190+
_firclsContext.readonly->machException.behavior = EXCEPTION_DEFAULT;
191+
if (initData.machProtectedEnabled) {
192+
_firclsContext.readonly->machException.behavior = EXCEPTION_IDENTITY_PROTECTED;
193+
}
194+
188195
FIRCLSMachExceptionInit(&_firclsContext.readonly->machException);
189196
});
190197
#endif

Crashlytics/Crashlytics/FIRCrashlytics.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,14 @@ - (instancetype)initWithApp:(FIRApp *)app
146146

147147
_fileManager = [[FIRCLSFileManager alloc] init];
148148
_googleAppID = app.options.googleAppID;
149+
// getting data collection state from info.plist, user defaults etc. disk I/O on main
149150
_dataArbiter = [[FIRCLSDataCollectionArbiter alloc] initWithApp:app withAppInfo:appInfo];
150151

151152
FIRCLSApplicationIdentifierModel *appModel = [[FIRCLSApplicationIdentifierModel alloc] init];
153+
// read settings cache, disk I/O on main
152154
FIRCLSSettings *settings = [[FIRCLSSettings alloc] initWithFileManager:_fileManager
153-
appIDModel:appModel];
155+
appIDModel:appModel
156+
appInfo:appInfo];
154157

155158
FIRCLSOnDemandModel *onDemandModel =
156159
[[FIRCLSOnDemandModel alloc] initWithFIRCLSSettings:settings fileManager:_fileManager];
@@ -200,6 +203,7 @@ - (instancetype)initWithApp:(FIRApp *)app
200203
}
201204

202205
_contextInitPromise =
206+
// setup with context init (binary image, register exception handler)
203207
[[[_reportManager startWithProfiling] then:^id _Nullable(NSNumber *_Nullable value) {
204208
if (![value boolValue]) {
205209
FIRCLSErrorLog(@"Crash reporting could not be initialized");

Crashlytics/Crashlytics/Handlers/FIRCLSMachException.c

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,19 @@
3333
static void* FIRCLSMachExceptionServer(void* argument);
3434
static bool FIRCLSMachExceptionThreadStart(FIRCLSMachExceptionReadContext* context);
3535
static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context,
36-
MachExceptionProtectedMessage* message);
36+
MachExceptionMessage* message);
3737
static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context,
38-
MachExceptionProtectedMessage* message);
38+
MachExceptionMessage* message);
3939
static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context,
40-
MachExceptionProtectedMessage* message,
40+
MachExceptionMessage* message,
4141
kern_return_t result);
4242
static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context);
4343
static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts,
44-
exception_mask_t mask);
44+
exception_mask_t mask,
45+
exception_behavior_t behavior);
4546
static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
46-
MachExceptionProtectedMessage* message);
47-
static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, thread_t* crashedThread);
47+
MachExceptionMessage* message);
48+
static void FIRCLSCrashedThreadLookup(MachExceptionMessage* message, thread_t* crashedThread);
4849
#pragma mark - Initialization
4950
void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context) {
5051
if (!FIRCLSUnlinkIfExists(context->path)) {
@@ -58,7 +59,7 @@ void FIRCLSMachExceptionInit(FIRCLSMachExceptionReadContext* context) {
5859

5960
if (!FIRCLSMachExceptionThreadStart(context)) {
6061
FIRCLSSDKLog("Unable to start thread\n");
61-
FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask);
62+
FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask, context->behavior);
6263
}
6364
}
6465

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

168169
while (1) {
169-
MachExceptionProtectedMessage message;
170+
MachExceptionMessage message;
170171

171172
// read the exception message
172173
if (!FIRCLSMachExceptionReadMessage(context, &message)) {
@@ -188,12 +189,12 @@ static void* FIRCLSMachExceptionServer(void* argument) {
188189
}
189190

190191
static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* context,
191-
MachExceptionProtectedMessage* message) {
192+
MachExceptionMessage* message) {
192193
mach_msg_return_t r;
193194

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

196-
r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionProtectedMessage),
197+
r = mach_msg(&message->head, MACH_RCV_MSG | MACH_RCV_LARGE, 0, sizeof(MachExceptionMessage),
197198
context->port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
198199
if (r != MACH_MSG_SUCCESS) {
199200
FIRCLSSDKLog("Error receiving mach_msg (%d)\n", r);
@@ -206,25 +207,30 @@ static bool FIRCLSMachExceptionReadMessage(FIRCLSMachExceptionReadContext* conte
206207
}
207208

208209
static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadContext* context,
209-
MachExceptionProtectedMessage* message) {
210+
MachExceptionMessage* message) {
210211
FIRCLSSDKLog("Mach exception: 0x%x, count: %d, code: 0x%llx 0x%llx\n", message->exception,
211212
message->codeCnt, message->codeCnt > 0 ? message->code[0] : -1,
212213
message->codeCnt > 1 ? message->code[1] : -1);
213214

214215
// This will happen if a child process raises an exception, as the exception ports are
215216
// inherited.
216217
mach_port_t actual_port;
217-
kern_return_t kr;
218-
task_id_token_t token = message->task_id.name;
219-
kr = task_identity_token_get_task_port(token, TASK_FLAVOR_CONTROL, &actual_port);
218+
kern_return_t kr = KERN_SUCCESS;
219+
if (context->behavior == EXCEPTION_DEFAULT){
220+
actual_port = message->payload_2.name;
221+
} else {
222+
// EXCEPTION_IDENTITY_PROTECTED
223+
task_id_token_t token = message->payload_1.name;
224+
kr = task_identity_token_get_task_port(token, TASK_FLAVOR_CONTROL, &actual_port);
225+
}
220226

221227
if (kr || actual_port != mach_task_self()) {
222228
FIRCLSSDKLog("Mach exception task mis-match, returning failure\n");
223229
return KERN_FAILURE;
224230
}
225231

226232
FIRCLSSDKLog("Unregistering handler\n");
227-
if (!FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask)) {
233+
if (!FIRCLSMachExceptionUnregister(&context->originalPorts, context->mask, context->behavior)) {
228234
FIRCLSSDKLog("Failed to unregister\n");
229235
return KERN_FAILURE;
230236
}
@@ -245,7 +251,7 @@ static kern_return_t FIRCLSMachExceptionDispatchMessage(FIRCLSMachExceptionReadC
245251
}
246252

247253
static bool FIRCLSMachExceptionReply(FIRCLSMachExceptionReadContext* context,
248-
MachExceptionProtectedMessage* message,
254+
MachExceptionMessage* message,
249255
kern_return_t result) {
250256
MachExceptionReply reply;
251257
mach_msg_return_t r;
@@ -301,7 +307,7 @@ static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context)
301307

302308
// ORing with MACH_EXCEPTION_CODES will produce 64-bit exception data
303309
kr = task_swap_exception_ports(task, context->mask, context->port,
304-
EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
310+
context->behavior | MACH_EXCEPTION_CODES, THREAD_STATE_NONE,
305311
context->originalPorts.masks, &context->originalPorts.count,
306312
context->originalPorts.ports, context->originalPorts.behaviors,
307313
context->originalPorts.flavors);
@@ -320,7 +326,8 @@ static bool FIRCLSMachExceptionRegister(FIRCLSMachExceptionReadContext* context)
320326
}
321327

322328
static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* originalPorts,
323-
exception_mask_t mask) {
329+
exception_mask_t mask,
330+
exception_behavior_t behavior) {
324331
kern_return_t kr;
325332

326333
// Re-register all the old ports.
@@ -338,7 +345,7 @@ static bool FIRCLSMachExceptionUnregister(FIRCLSMachExceptionOriginalPorts* orig
338345

339346
// Finally, mark any masks we registered for that do not have an original port as unused.
340347
kr = task_set_exception_ports(mach_task_self(), mask, MACH_PORT_NULL,
341-
EXCEPTION_IDENTITY_PROTECTED | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
348+
behavior | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
342349
if (kr != KERN_SUCCESS) {
343350
FIRCLSSDKLog("unable to unset unregistered mask: 0x%x", mask);
344351
return false;
@@ -477,7 +484,7 @@ void FIRCLSMachExceptionNameLookup(exception_type_t number,
477484
}
478485

479486
static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
480-
MachExceptionProtectedMessage* message) {
487+
MachExceptionMessage* message) {
481488
if (!context || !message) {
482489
return false;
483490
}
@@ -526,7 +533,12 @@ static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
526533
FIRCLSFileWriteSectionEnd(&file);
527534

528535
thread_t crashedThread;
529-
FIRCLSCrashedThreadLookup(message, &crashedThread);
536+
if (context->behavior == EXCEPTION_DEFAULT) {
537+
crashedThread = message->payload_1.name;
538+
} else {
539+
// EXCEPTION_IDENTITY_PROTECTED
540+
FIRCLSCrashedThreadLookup(message, &crashedThread);
541+
}
530542
FIRCLSSDKLog("Crashed threads: %d\n", crashedThread);
531543
FIRCLSHandler(&file, crashedThread, NULL, true);
532544

@@ -535,12 +547,12 @@ static bool FIRCLSMachExceptionRecord(FIRCLSMachExceptionReadContext* context,
535547
return true;
536548
}
537549

538-
static void FIRCLSCrashedThreadLookup(MachExceptionProtectedMessage* message, thread_t* crashedThread) {
550+
static void FIRCLSCrashedThreadLookup(MachExceptionMessage* message, thread_t* crashedThread) {
539551
thread_act_array_t threadList;
540552
mach_msg_type_number_t threadCount;
541553

542554
// last 64 bits include thread id info
543-
MachExceptionProtectedThreadInfo protected_thread_info = *(MachExceptionProtectedThreadInfo *) &message->thread_id;
555+
MachExceptionProtectedThreadInfo protected_thread_info = *(MachExceptionProtectedThreadInfo *) &message->payload_2;
544556
kern_return_t kr = task_threads(mach_task_self(), &threadList, &threadCount);
545557

546558
if (kr != KERN_SUCCESS) {

Crashlytics/Crashlytics/Handlers/FIRCLSMachException.h

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

2828
#pragma mark Structures
2929
#pragma pack(push, 4)
30+
// When set Mach port with default behavior:
31+
// payload_1 including thread port
32+
// payload_2 including task port
33+
// When set Mach port with identity protected behavior:
34+
// payload_1 including task_id_token
35+
// payload_2 including thread_id
3036
typedef struct {
3137
mach_msg_header_t head;
3238
/* start of the kernel processed data */
3339
mach_msg_body_t msgh_body;
34-
mach_msg_port_descriptor_t task_id;
35-
mach_msg_port_descriptor_t thread_id;
40+
mach_msg_port_descriptor_t payload_1;
41+
mach_msg_port_descriptor_t payload_2;
3642
/* end of the kernel processed data */
3743
NDR_record_t NDR;
3844
exception_type_t exception;
3945
mach_msg_type_number_t codeCnt;
4046
mach_exception_data_type_t code[EXCEPTION_CODE_MAX];
4147
mach_msg_trailer_t trailer;
42-
} MachExceptionProtectedMessage;
48+
} MachExceptionMessage;
4349

4450
typedef struct {
4551
uint64_t pad1;
@@ -68,6 +74,7 @@ typedef struct {
6874

6975
exception_mask_t mask;
7076
FIRCLSMachExceptionOriginalPorts originalPorts;
77+
exception_behavior_t behavior;
7178
} FIRCLSMachExceptionReadContext;
7279

7380
#pragma mark - API

Crashlytics/Crashlytics/Helpers/FIRCLSContextInitData.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
2828
@property(nonatomic, copy) NSString* betaToken;
2929
@property(nonatomic) BOOL errorsEnabled;
3030
@property(nonatomic) BOOL customExceptionsEnabled;
31+
@property(nonatomic) BOOL machProtectedEnabled;
3132
@property(nonatomic) uint32_t maxCustomExceptions;
3233
@property(nonatomic) uint32_t maxErrorLogSize;
3334
@property(nonatomic) uint32_t maxLogSize;

Crashlytics/Crashlytics/Models/FIRCLSSettings.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ NS_ASSUME_NONNULL_BEGIN
3030
- (instancetype)init NS_UNAVAILABLE;
3131
+ (instancetype)new NS_UNAVAILABLE;
3232
- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
33-
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel;
33+
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
34+
appInfo:(NSDictionary *)appInfo;
3435

3536
/**
3637
* Recreates the settings dictionary by re-reading the settings file from persistent storage. This
@@ -78,6 +79,12 @@ NS_ASSUME_NONNULL_BEGIN
7879
*/
7980
@property(nonatomic, readonly) BOOL customExceptionsEnabled;
8081

82+
/**
83+
* When this is true, Crashlytics will use EXCEPTION_IDENTITY_PROTECTED
84+
* for mach exception handler instead of EXCEPTION_DEFAULT for default
85+
*/
86+
@property(nonatomic) BOOL machProtectedEnabled;
87+
8188
/**
8289
* When this is true, Crashlytics will collect data from MetricKit
8390
*/

Crashlytics/Crashlytics/Models/FIRCLSSettings.m

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
NSString *const GoogleAppIDKey = @"google_app_id";
3131
NSString *const BuildInstanceID = @"build_instance_id";
3232
NSString *const AppVersion = @"app_version";
33+
NSString *const FirebaseCrashlyticsMachProtectedEnabledKey =
34+
@"FirebaseCrashlyticsMachProtectedEnabled";
3335

3436
@interface FIRCLSSettings ()
3537

@@ -47,21 +49,33 @@ @interface FIRCLSSettings ()
4749
@implementation FIRCLSSettings
4850

4951
- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
50-
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel {
52+
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
53+
appInfo:(NSDictionary *)appInfo {
5154
return
5255
[self initWithFileManager:fileManager
5356
appIDModel:appIDModel
57+
appInfo:(NSDictionary *)appInfo
5458
deletionQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)];
5559
}
5660

5761
- (instancetype)initWithFileManager:(FIRCLSFileManager *)fileManager
5862
appIDModel:(FIRCLSApplicationIdentifierModel *)appIDModel
63+
appInfo:(NSDictionary *)appInfo
5964
deletionQueue:(dispatch_queue_t)deletionQueue {
6065
self = [super init];
6166
if (!self) {
6267
return nil;
6368
}
6469

70+
// config the mach exception message receiving behavior
71+
self.machProtectedEnabled = false;
72+
id crashlyticsMachProtectedEnabled =
73+
[appInfo objectForKey:FirebaseCrashlyticsMachProtectedEnabledKey];
74+
if ([crashlyticsMachProtectedEnabled isKindOfClass:[NSString class]] ||
75+
[crashlyticsMachProtectedEnabled isKindOfClass:[NSNumber class]]) {
76+
self.machProtectedEnabled = [crashlyticsMachProtectedEnabled boolValue];
77+
}
78+
6579
_fileManager = fileManager;
6680
_appIDModel = appIDModel;
6781

0 commit comments

Comments
 (0)