Add another iOS library path sinkhole.

Add another sinkhole for _UIGestureEnvironmentUpdate.

Bug: crashpad:31
Change-Id: Ic4a424da034249295b6e45f8fe0860a4d4696b93
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2145017
Commit-Queue: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
Reviewed-by: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
This commit is contained in:
Justin Cohen 2020-04-15 17:46:45 -04:00 committed by Commit Bot
parent 7fa69e7e43
commit ea4af71c2a
6 changed files with 167 additions and 15 deletions

View File

@ -45,7 +45,6 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
"handler:handler_test",
"minidump:minidump_test",
"snapshot:snapshot_test",
"util:util_test",
]
}
if (crashpad_is_in_fuchsia) {
@ -63,9 +62,7 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
"util/net/testdata/binary_http_body.dat",
]
outputs = [
"$root_out_dir/crashpad_test_data/{{source}}",
]
outputs = [ "$root_out_dir/crashpad_test_data/{{source}}" ]
}
deps += [ ":crashpad_test_data" ]
@ -222,9 +219,6 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
"test:gmock_main",
"util:util_test",
]
if (crashpad_is_ios) {
deps -= [ "util:util_test" ]
}
}
}

View File

@ -193,6 +193,21 @@
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
}
- (void)testCatchUIGestureEnvironmentNSException {
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
// Tap the button with the string UIGestureEnvironmentException.
[_app.buttons[@"UIGestureEnvironmentException"] tap];
// Confirm the app is not running.
XCTAssertTrue([_app waitForState:XCUIApplicationStateNotRunning timeout:15]);
XCTAssertTrue(_app.state == XCUIApplicationStateNotRunning);
// TODO: Query the app for crash data
[_app launch];
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);
}
- (void)testCatchNSException {
XCTAssertTrue(_app.state == XCUIApplicationStateRunningForeground);

View File

@ -22,6 +22,40 @@
- (void)loadView {
self.view = [[UIView alloc] init];
UIStackView* buttonStack = [[UIStackView alloc] init];
buttonStack.axis = UILayoutConstraintAxisVertical;
buttonStack.spacing = 6;
UIButton* button = [UIButton new];
[button setTitle:@"UIGestureEnvironmentException"
forState:UIControlStateNormal];
UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(throwUIGestureEnvironmentException)];
[button addGestureRecognizer:tapGesture];
[button setTranslatesAutoresizingMaskIntoConstraints:NO];
[button.widthAnchor constraintEqualToConstant:16.0].active = YES;
[button.heightAnchor constraintEqualToConstant:16.0].active = YES;
[buttonStack addArrangedSubview:button];
[self.view addSubview:buttonStack];
[buttonStack setTranslatesAutoresizingMaskIntoConstraints:NO];
[NSLayoutConstraint activateConstraints:@[
[buttonStack.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[buttonStack.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[buttonStack.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[buttonStack.trailingAnchor
constraintEqualToAnchor:self.view.trailingAnchor],
]];
}
- (void)throwUIGestureEnvironmentException {
NSArray* empty_array = @[];
[empty_array objectAtIndex:42];
}
- (void)viewDidLoad {

View File

@ -548,7 +548,7 @@ static_library("util") {
configs += [ "..:disable_ubsan" ]
}
if (!crashpad_is_android) {
if (!crashpad_is_android && !crashpad_is_ios) {
crashpad_executable("http_transport_test_server") {
testonly = true
sources = [ "net/http_transport_test_server.cc" ]
@ -639,13 +639,13 @@ source_set("util_test") {
"thread/worker_thread_test.cc",
]
if (!crashpad_is_android) {
if (!crashpad_is_android && !crashpad_is_ios) {
# Android requires an HTTPTransport implementation.
sources += [ "net/http_transport_test.cc" ]
}
if (crashpad_is_posix || crashpad_is_fuchsia) {
if (!crashpad_is_fuchsia) {
if (!crashpad_is_fuchsia && !crashpad_is_ios) {
sources += [
"posix/process_info_test.cc",
"posix/signals_test.cc",
@ -680,6 +680,27 @@ source_set("util_test") {
]
}
if (crashpad_is_ios) {
sources += [ "ios/exception_processor_test.mm" ]
sources -= [
"file/directory_reader_test.cc",
"file/file_io_test.cc",
"file/filesystem_test.cc",
"misc/capture_context_test.cc",
"misc/clock_test.cc",
"misc/paths_test.cc",
"net/http_body_test.cc",
"net/http_multipart_builder_test.cc",
"process/process_memory_range_test.cc",
"process/process_memory_test.cc",
"stream/file_encoder_test.cc",
"synchronization/semaphore_test.cc",
"thread/thread_test.cc",
"thread/worker_thread_test.cc",
]
}
if (crashpad_is_linux || crashpad_is_android) {
sources += [
"linux/auxiliary_vector_test.cc",
@ -739,7 +760,7 @@ source_set("util_test") {
deps += [ "../third_party/lss" ]
}
if (!crashpad_is_android) {
if (!crashpad_is_android && !crashpad_is_ios) {
data_deps = [ ":http_transport_test_server" ]
if (crashpad_use_boringssl_for_http_transport_socket) {

View File

@ -20,6 +20,7 @@
#include <dlfcn.h>
#include <libunwind.h>
#include <mach-o/loader.h>
#include <objc/message.h>
#include <objc/objc-exception.h>
#include <objc/objc.h>
#include <objc/runtime.h>
@ -109,6 +110,14 @@ bool ModulePathMatchesSinkhole(const char* path, const char* sinkhole) {
#endif
}
int LoggingUnwStep(unw_cursor_t* cursor) {
int rv = unw_step(cursor);
if (rv < 0) {
LOG(ERROR) << "unw_step: " << rv;
}
return rv;
}
id ObjcExceptionPreprocessor(id exception) {
// Unwind the stack looking for any exception handlers. If an exception
// handler is encountered, test to see if it is a function known to catch-
@ -152,7 +161,7 @@ id ObjcExceptionPreprocessor(id exception) {
exception_header->unwindHeader.exception_class = kOurExceptionClass;
bool handler_found = false;
while (unw_step(&cursor) > 0) {
while (LoggingUnwStep(&cursor) > 0) {
unw_proc_info_t frame_info;
if (unw_get_proc_info(&cursor, &frame_info) != UNW_ESUCCESS) {
continue;
@ -226,14 +235,14 @@ id ObjcExceptionPreprocessor(id exception) {
// Everything in this library is a sinkhole, specifically
// _dispatch_client_callout. Both are needed here depending on whether
// the debugger is attached (introspection only appears when a simulator
// is attached to a debugger.
// only).
// is attached to a debugger).
"/usr/lib/system/introspection/libdispatch.dylib",
"/usr/lib/system/libdispatch.dylib",
// __CFRunLoopDoTimers and __CFRunLoopRun are sinkholes. Consider also
// checking that a few frames up is CFRunLoopRunSpecific().
"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"};
"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation",
};
Dl_info dl_info;
if (dladdr(reinterpret_cast<const void*>(frame_info.start_ip), &dl_info) !=
@ -245,6 +254,44 @@ id ObjcExceptionPreprocessor(id exception) {
}
}
// Some <redacted> sinkholes are harder to find. _UIGestureEnvironmentUpdate
// in UIKitCore is an example. UIKitCore can't be added to
// kExceptionLibraryPathSinkholes because it uses Objective-C exceptions
// internally and also has has non-sinkhole handlers. Since
// _UIGestureEnvironmentUpdate is always called from
// -[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:],
// inspect the caller frame info to match the sinkhole.
constexpr const char* kUIKitCorePath =
"/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore";
if (ModulePathMatchesSinkhole(dl_info.dli_fname, kUIKitCorePath)) {
unw_proc_info_t caller_frame_info;
if (LoggingUnwStep(&cursor) > 0 &&
unw_get_proc_info(&cursor, &caller_frame_info) == UNW_ESUCCESS) {
static IMP uigesture_deliver_event_imp = [] {
IMP imp = class_getMethodImplementation(
NSClassFromString(@"UIGestureEnvironment"),
NSSelectorFromString(
@"_deliverEvent:toGestureRecognizers:usingBlock:"));
// From 10.15.0 objc4-779.1/runtime/objc-class.mm
// class_getMethodImplementation returns nil or _objc_msgForward on
// failure.
if (!imp || imp == _objc_msgForward) {
LOG(WARNING) << "Unable to find -[UIGestureEnvironment "
"_deliverEvent:toGestureRecognizers:usingBlock:]";
return reinterpret_cast<IMP>(NULL);
}
return imp;
}();
if (uigesture_deliver_event_imp ==
reinterpret_cast<IMP>(caller_frame_info.start_ip)) {
TerminatingFromUncaughtNSException(exception,
"_UIGestureEnvironmentUpdate");
}
}
}
handler_found = true;
break;

View File

@ -0,0 +1,41 @@
// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#import <Foundation/Foundation.h>
#include <objc/message.h>
#include <objc/runtime.h>
#include "gtest/gtest.h"
#include "testing/platform_test.h"
namespace crashpad {
namespace test {
namespace {
using IOSExceptionProcessor = PlatformTest;
TEST_F(IOSExceptionProcessor, SelectorExists) {
IMP uigesture_deliver_event_imp = class_getMethodImplementation(
NSClassFromString(@"UIGestureEnvironment"),
NSSelectorFromString(@"_deliverEvent:toGestureRecognizers:usingBlock:"));
// From 10.15.0 objc4-779.1/runtime/objc-class.mm
// class_getMethodImplementation returns nil or _objc_msgForward on failure.
ASSERT_TRUE(uigesture_deliver_event_imp);
ASSERT_NE(uigesture_deliver_event_imp, _objc_msgForward);
}
} // namespace
} // namespace test
} // namespace crashpad