Skip to content

Commit

Permalink
Update Crashpad to 25e67e285c01b2b0c7d654dc2238176f0f1a56c3
Browse files Browse the repository at this point in the history
4c85c466b00c ios: Fix test failure on M1 ARM64 machines
460dbdceaeb4 ios: Unblock all signals corresponding to Mach exceptions
             on crash
243dffb04573 ios: Stop prune and upload thread when app is inactive and
             may suspend
25e67e285c01 ios: Track last NSException in ObjcException preprocessor

Change-Id: If360f268af8180c3706c35ede6c4ddde9638843d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3546087
Auto-Submit: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/main@{#984839}
  • Loading branch information
Justin Cohen authored and Chromium LUCI CQ committed Mar 24, 2022
1 parent 76148b4 commit 12803e0
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 108 deletions.
2 changes: 1 addition & 1 deletion third_party/crashpad/README.chromium
Expand Up @@ -2,7 +2,7 @@ Name: Crashpad
Short Name: crashpad
URL: https://crashpad.chromium.org/
Version: unknown
Revision: cd13ea34ebca8f993d4d60fa40dc7fde3a95bee3
Revision: 25e67e285c01b2b0c7d654dc2238176f0f1a56c3
License: Apache 2.0
License File: crashpad/LICENSE
Security Critical: yes
Expand Down
31 changes: 28 additions & 3 deletions third_party/crashpad/crashpad/client/crashpad_client_ios.cc
Expand Up @@ -14,6 +14,7 @@

#include "client/crashpad_client.h"

#include <signal.h>
#include <unistd.h>

#include <ios>
Expand All @@ -24,6 +25,7 @@
#include "base/mac/scoped_mach_port.h"
#include "client/ios_handler/exception_processor.h"
#include "client/ios_handler/in_process_handler.h"
#include "util/ios/raw_logging.h"
#include "util/mach/exc_server_variants.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"
Expand Down Expand Up @@ -249,8 +251,7 @@ class CrashHandler : public Thread,
// inherit the task exception ports, and this process isn’t prepared to
// handle them
if (task != mach_task_self()) {
LOG(WARNING) << "task 0x" << std::hex << task << " != 0x"
<< mach_task_self();
CRASHPAD_RAW_LOG("MachException task != mach_task_self()");
return KERN_FAILURE;
}

Expand All @@ -264,7 +265,31 @@ class CrashHandler : public Thread,
old_state_count);

// Respond with KERN_FAILURE so the system will continue to handle this
// exception as a crash.
// exception. xnu will turn this Mach exception into a signal and take the
// default action to terminate the process. However, if sigprocmask is
// called before this Mach exception returns (such as by another thread
// calling abort, see: Libc-1506.40.4/stdlib/FreeBSD/abort.c), the Mach
// exception will be converted into a signal but delivery will be blocked.
// Since concurrent exceptions lead to the losing thread sleeping
// indefinitely, if the abort thread never returns, the thread that
// triggered this Mach exception will repeatedly trap and the process will
// never terminate. If the abort thread didn’t have a user-space signal
// handler that slept forever, the abort would terminate the process even if
// all other signals had been blocked. Instead, unblock all signals
// corresponding to all Mach exceptions Crashpad is registered for before
// returning KERN_FAILURE. There is still racy behavior possible with this
// call to sigprocmask, but the repeated calls to CatchMachException here
// will eventually lead to termination.
sigset_t unblock_set;
sigemptyset(&unblock_set);
sigaddset(&unblock_set, SIGILL); // EXC_BAD_INSTRUCTION
sigaddset(&unblock_set, SIGTRAP); // EXC_BREAKPOINT
sigaddset(&unblock_set, SIGFPE); // EXC_ARITHMETIC
sigaddset(&unblock_set, SIGBUS); // EXC_BAD_ACCESS
sigaddset(&unblock_set, SIGSEGV); // EXC_BAD_ACCESS
if (sigprocmask(SIG_UNBLOCK, &unblock_set, nullptr) != 0) {
CRASHPAD_RAW_LOG("sigprocmask");
}
return KERN_FAILURE;
}

Expand Down
153 changes: 111 additions & 42 deletions third_party/crashpad/crashpad/client/ios_handler/exception_processor.mm
Expand Up @@ -53,6 +53,8 @@
#include "client/annotation.h"
#include "client/simulate_crash_ios.h"

namespace crashpad {

namespace {

// From 10.15.0 objc4-779.1/runtime/objc-exception.mm.
Expand Down Expand Up @@ -132,32 +134,83 @@ int LoggingUnwStep(unw_cursor_t* cursor) {
return FormatStackTrace(addresses, 1024);
}

crashpad::ObjcExceptionDelegate* g_exception_delegate;
objc_exception_preprocessor g_next_preprocessor;
NSUncaughtExceptionHandler* g_next_uncaught_exception_handler;
//! \brief Helper class to own the complex types used by the Objective-C
//! exception preprocessor.
class ExceptionPreprocessorState {
public:
ExceptionPreprocessorState(const ExceptionPreprocessorState&) = delete;
ExceptionPreprocessorState& operator=(const ExceptionPreprocessorState&) =
delete;

static ExceptionPreprocessorState* Get() {
static ExceptionPreprocessorState* instance = []() {
return new ExceptionPreprocessorState();
}();
return instance;
}

// Inform the delegate of the uncaught exception and remove the global
// uncaught exception handler so we don't record this twice.
void HandleUncaughtException(NativeCPUContext* cpu_context) {
exception_delegate_->HandleUncaughtNSExceptionWithContext(cpu_context);

NSSetUncaughtExceptionHandler(next_uncaught_exception_handler_);
next_uncaught_exception_handler_ = nullptr;
}

id MaybeCallNextPreprocessor(id exception) {
return next_preprocessor_ ? next_preprocessor_(exception) : exception;
}

// Register the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler.
void Install(ObjcExceptionDelegate* delegate);

// Restore the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler.
void Uninstall();

NSException* last_exception() { return last_exception_; }
void set_last_exception(NSException* exception) {
[last_exception_ release];
last_exception_ = [exception retain];
}

ObjcExceptionDelegate* exception_delegate() { return exception_delegate_; }

private:
ExceptionPreprocessorState() = default;
~ExceptionPreprocessorState() = default;

// Recorded last NSException in case the exception is caught and thrown again
// (without using objc_exception_rethrow.)
NSException* last_exception_ = nil;

ObjcExceptionDelegate* exception_delegate_ = nullptr;
objc_exception_preprocessor next_preprocessor_ = nullptr;
NSUncaughtExceptionHandler* next_uncaught_exception_handler_ = nullptr;
};

static void SetNSExceptionAnnotations(NSException* exception,
std::string& name,
std::string& reason) {
@try {
name = base::SysNSStringToUTF8(exception.name);
static crashpad::StringAnnotation<256> nameKey("exceptionName");
static StringAnnotation<256> nameKey("exceptionName");
nameKey.Set(name);
} @catch (id name_exception) {
LOG(ERROR) << "Unable to read uncaught Objective-C exception name.";
}

@try {
reason = base::SysNSStringToUTF8(exception.reason);
static crashpad::StringAnnotation<512> reasonKey("exceptionReason");
static StringAnnotation<512> reasonKey("exceptionReason");
reasonKey.Set(reason);
} @catch (id reason_exception) {
LOG(ERROR) << "Unable to read uncaught Objective-C exception reason.";
}

@try {
if (exception.userInfo) {
static crashpad::StringAnnotation<512> userInfoKey("exceptionUserInfo");
static StringAnnotation<512> userInfoKey("exceptionUserInfo");
userInfoKey.Set(base::SysNSStringToUTF8(
[NSString stringWithFormat:@"%@", exception.userInfo]));
}
Expand All @@ -170,21 +223,24 @@ static void ObjcUncaughtExceptionHandler(NSException* exception) {
std::string name, reason;
SetNSExceptionAnnotations(exception, name, reason);
NSArray<NSNumber*>* addressArray = [exception callStackReturnAddresses];

ObjcExceptionDelegate* exception_delegate =
ExceptionPreprocessorState::Get()->exception_delegate();
if ([addressArray count] > 0) {
static crashpad::StringAnnotation<256> nameKey("UncaughtNSException");
static StringAnnotation<256> nameKey("UncaughtNSException");
nameKey.Set("true");
std::vector<uint64_t> addresses;
for (NSNumber* address in addressArray)
addresses.push_back([address unsignedLongLongValue]);
g_exception_delegate->HandleUncaughtNSException(&addresses[0],
addresses.size());
exception_delegate->HandleUncaughtNSException(&addresses[0],
addresses.size());
} else {
LOG(WARNING) << "Uncaught Objective-C exception name: " << name
<< " reason: " << reason << " with no "
<< " -callStackReturnAddresses.";
crashpad::NativeCPUContext cpu_context;
crashpad::CaptureContext(&cpu_context);
g_exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);
NativeCPUContext cpu_context;
CaptureContext(&cpu_context);
exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);
}
}

Expand All @@ -197,15 +253,13 @@ static __attribute__((noinline)) id HANDLE_UNCAUGHT_NSEXCEPTION(
SetNSExceptionAnnotations(exception, name, reason);
LOG(WARNING) << "Handling Objective-C exception name: " << name
<< " reason: " << reason << " with sinkhole: " << sinkhole;
crashpad::NativeCPUContext cpu_context;
crashpad::CaptureContext(&cpu_context);
g_exception_delegate->HandleUncaughtNSExceptionWithContext(&cpu_context);

// Remove the uncaught exception handler so we don't record this twice.
NSSetUncaughtExceptionHandler(g_next_uncaught_exception_handler);
g_next_uncaught_exception_handler = nullptr;
NativeCPUContext cpu_context;
CaptureContext(&cpu_context);

return g_next_preprocessor ? g_next_preprocessor(exception) : exception;
ExceptionPreprocessorState* preprocessor_state =
ExceptionPreprocessorState::Get();
preprocessor_state->HandleUncaughtException(&cpu_context);
return preprocessor_state->MaybeCallNextPreprocessor(exception);
}

// Returns true if |path| equals |sinkhole| on device. Simulator paths prepend
Expand All @@ -226,13 +280,23 @@ bool ModulePathMatchesSinkhole(const char* path, const char* sinkhole) {
}

id ObjcExceptionPreprocessor(id exception) {
// Some sinkholes don't use objc_exception_rethrow when they should, which
// would otherwise prevent the exception_preprocessor from getting called
// again. Because of this, track the most recently seen exception and
// ignore it.
ExceptionPreprocessorState* preprocessor_state =
ExceptionPreprocessorState::Get();
if ([preprocessor_state->last_exception() isEqual:exception]) {
return preprocessor_state->MaybeCallNextPreprocessor(exception);
}
preprocessor_state->set_last_exception(exception);

static bool seen_first_exception;

static crashpad::StringAnnotation<256> firstexception("firstexception");
static crashpad::StringAnnotation<256> lastexception("lastexception");
static crashpad::StringAnnotation<1024> firstexception_bt(
"firstexception_bt");
static crashpad::StringAnnotation<1024> lastexception_bt("lastexception_bt");
static StringAnnotation<256> firstexception("firstexception");
static StringAnnotation<256> lastexception("lastexception");
static StringAnnotation<1024> firstexception_bt("firstexception_bt");
static StringAnnotation<1024> lastexception_bt("lastexception_bt");
auto* key = seen_first_exception ? &lastexception : &firstexception;
auto* bt_key = seen_first_exception ? &lastexception_bt : &firstexception_bt;
NSString* value = [NSString
Expand Down Expand Up @@ -470,36 +534,41 @@ id ObjcExceptionPreprocessor(id exception) {
}

// Forward to the next preprocessor.
return g_next_preprocessor ? g_next_preprocessor(exception) : exception;
return preprocessor_state->MaybeCallNextPreprocessor(exception);
}

} // namespace

namespace crashpad {

void InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate) {
DCHECK(!g_next_preprocessor);
void ExceptionPreprocessorState::Install(ObjcExceptionDelegate* delegate) {
DCHECK(!next_preprocessor_);
exception_delegate_ = delegate;

// Preprocessor.
g_next_preprocessor =
next_preprocessor_ =
objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor);

// Uncaught processor.
g_exception_delegate = delegate;
g_next_uncaught_exception_handler = NSGetUncaughtExceptionHandler();
next_uncaught_exception_handler_ = NSGetUncaughtExceptionHandler();
NSSetUncaughtExceptionHandler(&ObjcUncaughtExceptionHandler);
}

void UninstallObjcExceptionPreprocessor() {
DCHECK(g_next_preprocessor);
void ExceptionPreprocessorState::Uninstall() {
DCHECK(next_preprocessor_);
objc_setExceptionPreprocessor(next_preprocessor_);
next_preprocessor_ = nullptr;

NSSetUncaughtExceptionHandler(next_uncaught_exception_handler_);
next_uncaught_exception_handler_ = nullptr;

exception_delegate_ = nullptr;
}

objc_setExceptionPreprocessor(g_next_preprocessor);
g_exception_delegate = nullptr;
} // namespace

NSSetUncaughtExceptionHandler(g_next_uncaught_exception_handler);
g_next_uncaught_exception_handler = nullptr;
void InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate) {
ExceptionPreprocessorState::Get()->Install(delegate);
}

g_next_preprocessor = nullptr;
void UninstallObjcExceptionPreprocessor() {
ExceptionPreprocessorState::Get()->Uninstall();
}

} // namespace crashpad
Expand Up @@ -62,10 +62,7 @@ namespace internal {
InProcessHandler::InProcessHandler() = default;

InProcessHandler::~InProcessHandler() {
if (upload_thread_started_ && upload_thread_) {
upload_thread_->Stop();
}
prune_thread_->Stop();
UpdatePruneAndUploadThreads(false);
}

bool InProcessHandler::Initialize(
Expand Down Expand Up @@ -103,13 +100,20 @@ bool InProcessHandler::Initialize(
if (!CreateDirectory(base_dir_))
return false;

bool is_app_extension = system_data_.IsExtension();
prune_thread_.reset(new PruneIntermediateDumpsAndCrashReportsThread(
database_.get(),
PruneCondition::GetDefault(),
base_dir_,
bundle_identifier_and_seperator_,
system_data_.IsExtension()));
prune_thread_->Start();
is_app_extension));
if (is_app_extension || system_data_.IsApplicationActive())
prune_thread_->Start();

if (!is_app_extension) {
system_data_.SetActiveApplicationCallback(
[this](bool active) { UpdatePruneAndUploadThreads(active); });
}

base::FilePath cached_writer_path = NewLockedFilePath();
cached_writer_ = CreateWriterWithPath(cached_writer_path);
Expand Down Expand Up @@ -284,9 +288,29 @@ void InProcessHandler::ProcessIntermediateDump(
}

void InProcessHandler::StartProcessingPendingReports() {
if (!upload_thread_started_ && upload_thread_) {
upload_thread_->Start();
upload_thread_started_ = true;
if (!upload_thread_)
return;

upload_thread_enabled_ = true;
UpdatePruneAndUploadThreads(true);
}

void InProcessHandler::UpdatePruneAndUploadThreads(bool active) {
base::AutoLock lock_owner(prune_and_upload_lock_);
// TODO(crbug.com/crashpad/400): Consider moving prune and upload thread to
// BackgroundTasks and/or NSURLSession. This might allow uploads to continue
// in the background.
if (active) {
if (!prune_thread_->is_running())
prune_thread_->Start();
if (upload_thread_enabled_ && !upload_thread_->is_running()) {
upload_thread_->Start();
}
} else {
if (prune_thread_->is_running())
prune_thread_->Stop();
if (upload_thread_enabled_ && upload_thread_->is_running())
upload_thread_->Stop();
}
}

Expand Down

0 comments on commit 12803e0

Please sign in to comment.