diff --git a/client/ios_handler/exception_processor.mm b/client/ios_handler/exception_processor.mm index c54dd589..ce1f2682 100644 --- a/client/ios_handler/exception_processor.mm +++ b/client/ios_handler/exception_processor.mm @@ -377,6 +377,24 @@ id ObjcExceptionPreprocessor(id exception) { return HANDLE_UNCAUGHT_NSEXCEPTION(exception, sinkhole); } } + + // Another set of iOS redacted sinkholes appear in CoreAutoLayout. + // However, this is often called by client code, so it's unsafe to simply + // handle an uncaught nsexception here. Instead, skip the frame and + // continue searching for either a handler that belongs to us, or another + // sinkhole. See: + // -[NSISEngine + // performModifications:withUnsatisfiableConstraintsHandler:]: + // -[NSISEngine withBehaviors:performModifications:] + // +[NSLayoutConstraintParser + // constraintsWithVisualFormat:options:metrics:views:]: + static constexpr const char* kCoreAutoLayoutSinkhole = + "/System/Library/PrivateFrameworks/CoreAutoLayout.framework/" + "CoreAutoLayout"; + if (ModulePathMatchesSinkhole(dl_info.dli_fname, + kCoreAutoLayoutSinkhole)) { + continue; + } } // Some sinkholes are harder to find. _UIGestureEnvironmentUpdate diff --git a/test/ios/crash_type_xctest.mm b/test/ios/crash_type_xctest.mm index c9317965..529ca13e 100644 --- a/test/ios/crash_type_xctest.mm +++ b/test/ios/crash_type_xctest.mm @@ -179,6 +179,16 @@ XCTAssertEqual([rootObject_ pendingReportCount], 0); } +- (void)testCrashCoreAutoLayoutSinkhole { + [rootObject_ crashCoreAutoLayoutSinkhole]; + [self verifyCrashReportException:crashpad::kMachExceptionFromNSException]; + NSDictionary* dict = [rootObject_ getAnnotations]; + XCTAssertTrue([[dict[@"objects"][0] valueForKeyPath:@"exceptionReason"] + containsString:@"Unable to activate constraint with anchors"]); + XCTAssertTrue([[dict[@"objects"][1] valueForKeyPath:@"exceptionName"] + isEqualToString:@"NSGenericException"]); +} + - (void)testRecursion { [rootObject_ crashRecursion]; [self verifyCrashReportException:SIGHUP]; diff --git a/test/ios/host/cptest_application_delegate.mm b/test/ios/host/cptest_application_delegate.mm index 6ff8378a..44a7d88a 100644 --- a/test/ios/host/cptest_application_delegate.mm +++ b/test/ios/host/cptest_application_delegate.mm @@ -289,6 +289,18 @@ OperationStatus GetPendingReports(std::vector* pending_reports) { } } +- (void)crashCoreAutoLayoutSinkhole { + // EDO has its own sinkhole, so dispatch this away. + dispatch_async(dispatch_get_main_queue(), ^{ + UIView* unattachedView = [[UIView alloc] init]; + UIWindow* window = [UIApplication sharedApplication].windows[0]; + [NSLayoutConstraint activateConstraints:@[ + [window.rootViewController.view.bottomAnchor + constraintEqualToAnchor:unattachedView.bottomAnchor], + ]]; + }); +} + - (void)crashRecursion { recurse(0); } diff --git a/test/ios/host/cptest_shared_object.h b/test/ios/host/cptest_shared_object.h index 4a781933..bcd75547 100644 --- a/test/ios/host/cptest_shared_object.h +++ b/test/ios/host/cptest_shared_object.h @@ -73,6 +73,9 @@ // Trigger a caught NSException, this will not crash - (void)catchNSException; +// Trigger an NSException with sinkholes in CoreAutoLayout. +- (void)crashCoreAutoLayoutSinkhole; + // Trigger a crash with an infinite recursion. - (void)crashRecursion; @@ -84,6 +87,7 @@ // Trigger a crash after writing various annotations. - (void)crashWithAnnotations; + @end #endif // CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_