mac: Add CrashpadClient::UseSystemDefaultHandler()

Chrome’s relauncher process needs a way to sever ties with the
crashpad_handler instance running from the disk image in order to cause
that instance to exit so that the disk image may be unmounted. This new
function is otherwise not thought to be interesting, and its use is not
recommended.

This comes with a small refactoring to create a
SystemCrashReporterHandler() function, and a fix for a minor port leak
in CrashReportExceptionHandler::CatchMachException().

BUG=chromium:538373
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/1375573005 .
This commit is contained in:
Mark Mentovai 2015-10-02 14:40:38 -04:00
parent ccd5ec6404
commit cd85c9f700
6 changed files with 119 additions and 56 deletions

View File

@ -122,6 +122,30 @@ class CrashpadClient {
//! \return `true` on success, `false` on failure with a message logged. //! \return `true` on success, `false` on failure with a message logged.
bool UseHandler(); bool UseHandler();
#if defined(OS_MACOSX) || DOXYGEN
//! \brief Configures the process to direct its crashes to the default handler
//! for the operating system.
//!
//! On OS X, this sets the tasks exception port as in UseHandler(), but the
//! exception handler used is obtained from SystemCrashReporterHandler(). If
//! the systems crash reporter handler cannot be determined, the tasks
//! exception ports for crash-type exceptions are cleared.
//!
//! Use of this function is strongly discouraged.
//!
//! \warning After a successful call to this function, Crashpad will no longer
//! monitor the process for crashes until a subsequent call to
//! UseHandler().
//!
//! \note This is provided as a static function to allow it to be used in
//! situations where a CrashpadClient object is not otherwise available.
//! This may be useful when a child process inherits its parents Crashpad
//! handler, but wants to sever this tie.
//!
//! \return `true` on success, `false` on failure with a message logged.
static bool UseSystemDefaultHandler();
#endif
private: private:
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
base::mac::ScopedMachSendRight exception_port_; base::mac::ScopedMachSendRight exception_port_;

View File

@ -26,6 +26,8 @@
#include "util/mach/mach_extensions.h" #include "util/mach/mach_extensions.h"
#include "util/posix/close_multiple.h" #include "util/posix/close_multiple.h"
namespace crashpad {
namespace { namespace {
std::string FormatArgumentString(const std::string& name, std::string FormatArgumentString(const std::string& name,
@ -37,9 +39,45 @@ std::string FormatArgumentInt(const std::string& name, int value) {
return base::StringPrintf("--%s=%d", name.c_str(), value); return base::StringPrintf("--%s=%d", name.c_str(), value);
} }
} // namespace // Set the exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD.
//
// EXC_CRASH is how most crashes are received. Most other exception types such
// as EXC_BAD_ACCESS are delivered to a host-level exception handler in the
// kernel where they are converted to POSIX signals. See 10.9.5
// xnu-2422.115.4/bsd/uxkern/ux_exception.c catch_mach_exception_raise(). If a
// core-generating signal (triggered through this hardware mechanism or a
// software mechanism such as abort() sending SIGABRT) is unhandled and the
// process exits, or if the process is killed with SIGKILL for code-signing
// reasons, an EXC_CRASH exception will be sent. See 10.9.5
// xnu-2422.115.4/bsd/kern/kern_exit.c proc_prepareexit().
//
// EXC_RESOURCE and EXC_GUARD do not become signals or EXC_CRASH exceptions. The
// host-level exception handler in the kernel does not receive these exception
// types, and even if it did, it would not map them to signals. Instead, the
// first Mach service loaded by the root (process ID 1) launchd with a boolean
// “ExceptionServer” property in its job dictionary (regardless of its value) or
// with any subdictionary property will become the host-level exception handler
// for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. See 10.9.5
// launchd-842.92.1/src/core.c job_setup_exception_port(). Normally, this job is
// com.apple.ReportCrash.Root, the systemwide Apple Crash Reporter. Since it is
// impossible to receive EXC_RESOURCE and EXC_GUARD exceptions through the
// EXC_CRASH mechanism, an exception handler must be registered for them by name
// if it is to receive these exception types. The default task-level handler for
// these exception types is set by launchd in a similar manner.
//
// EXC_MASK_RESOURCE and EXC_MASK_GUARD are not available on all systems, and
// the kernel will reject attempts to use them if it does not understand them,
// so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported.
bool SetCrashExceptionPorts(exception_handler_t exception_handler) {
ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
return exception_ports.SetExceptionPort(
(EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskValid(),
exception_handler,
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
MACHINE_THREAD_STATE);
}
namespace crashpad { } // namespace
CrashpadClient::CrashpadClient() CrashpadClient::CrashpadClient()
: exception_port_() { : exception_port_() {
@ -179,46 +217,17 @@ bool CrashpadClient::StartHandler(
bool CrashpadClient::UseHandler() { bool CrashpadClient::UseHandler() {
DCHECK_NE(exception_port_, kMachPortNull); DCHECK_NE(exception_port_, kMachPortNull);
// Set the exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. return SetCrashExceptionPorts(exception_port_);
// }
// EXC_CRASH is how most crashes are received. Most other exception types such
// as EXC_BAD_ACCESS are delivered to a host-level exception handler in the
// kernel where they are converted to POSIX signals. See 10.9.5
// xnu-2422.115.4/bsd/uxkern/ux_exception.c catch_mach_exception_raise(). If a
// core-generating signal (triggered through this hardware mechanism or a
// software mechanism such as abort() sending SIGABRT) is unhandled and the
// process exits, or if the process is killed with SIGKILL for code-signing
// reasons, an EXC_CRASH exception will be sent. See 10.9.5
// xnu-2422.115.4/bsd/kern/kern_exit.c proc_prepareexit().
//
// EXC_RESOURCE and EXC_GUARD do not become signals or EXC_CRASH exceptions.
// The host-level exception handler in the kernel does not receive these
// exception types, and even if it did, it would not map them to signals.
// Instead, the first Mach service loaded by the root (process ID 1) launchd
// with a boolean “ExceptionServer” property in its job dictionary (regardless
// of its value) or with any subdictionary property will become the host-level
// exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. See 10.9.5
// launchd-842.92.1/src/core.c job_setup_exception_port(). Normally, this job
// is com.apple.ReportCrash.Root, the systemwide Apple Crash Reporter. Since
// it is impossible to receive EXC_RESOURCE and EXC_GUARD exceptions through
// the EXC_CRASH mechanism, an exception handler must be registered for them
// by name if it is to receive these exception types. The default task-level
// handler for these exception types is set by launchd in a similar manner.
//
// EXC_MASK_RESOURCE and EXC_MASK_GUARD are not available on all systems, and
// the kernel will reject attempts to use them if it does not understand them,
// so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported.
ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
if (!exception_ports.SetExceptionPort(
(EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) &
ExcMaskValid(),
exception_port_,
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
MACHINE_THREAD_STATE)) {
return false;
}
return true; // static
bool CrashpadClient::UseSystemDefaultHandler() {
base::mac::ScopedMachSendRight
system_crash_reporter_handler(SystemCrashReporterHandler());
// Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL
// to clear the current exception ports.
return SetCrashExceptionPorts(system_crash_reporter_handler);
} }
} // namespace crashpad } // namespace crashpad

View File

@ -14,12 +14,11 @@
#include "handler/mac/crash_report_exception_handler.h" #include "handler/mac/crash_report_exception_handler.h"
#include <servers/bootstrap.h>
#include <vector> #include <vector>
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/mach_logging.h" #include "base/mac/mach_logging.h"
#include "base/mac/scoped_mach_port.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "client/settings.h" #include "client/settings.h"
#include "minidump/minidump_file_writer.h" #include "minidump/minidump_file_writer.h"
@ -202,15 +201,9 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
// Note that normally, EXC_RESOURCE and EXC_GUARD exceptions are sent to the // Note that normally, EXC_RESOURCE and EXC_GUARD exceptions are sent to the
// system-level com.apple.ReportCrash.Root job, and not to the user-level // system-level com.apple.ReportCrash.Root job, and not to the user-level
// job that they are forwarded to here. // job that they are forwarded to here.
mach_port_t system_crash_reporter_port; base::mac::ScopedMachSendRight
const char kSystemCrashReporterServiceName[] = "com.apple.ReportCrash"; system_crash_reporter_handler(SystemCrashReporterHandler());
kern_return_t kr = bootstrap_look_up(bootstrap_port, if (system_crash_reporter_handler) {
kSystemCrashReporterServiceName,
&system_crash_reporter_port);
if (kr != BOOTSTRAP_SUCCESS) {
BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up "
<< kSystemCrashReporterServiceName;
} else {
// Make copies of mutable out parameters so that the system crash reporter // Make copies of mutable out parameters so that the system crash reporter
// cant influence the state returned by this method. // cant influence the state returned by this method.
thread_state_flavor_t flavor_forward = *flavor; thread_state_flavor_t flavor_forward = *flavor;
@ -227,9 +220,9 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
// problems may arise if the state wasnt available and the system crash // problems may arise if the state wasnt available and the system crash
// reporter changes in the future to use it. However, normally, the state // reporter changes in the future to use it. However, normally, the state
// will be available. // will be available.
kr = UniversalExceptionRaise( kern_return_t kr = UniversalExceptionRaise(
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
system_crash_reporter_port, system_crash_reporter_handler,
thread, thread,
task, task,
exception, exception,
@ -240,8 +233,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
old_state_count, old_state_count,
new_state_forward_count ? &new_state_forward[0] : nullptr, new_state_forward_count ? &new_state_forward[0] : nullptr,
&new_state_forward_count); &new_state_forward_count);
MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "UniversalExceptionRaise";
<< "UniversalExceptionRaise " << kSystemCrashReporterServiceName;
} }
} }

View File

@ -16,6 +16,7 @@
#include <AvailabilityMacros.h> #include <AvailabilityMacros.h>
#include <pthread.h> #include <pthread.h>
#include <servers/bootstrap.h>
#include "base/mac/mach_logging.h" #include "base/mac/mach_logging.h"
#include "util/mac/mac_util.h" #include "util/mac/mac_util.h"
@ -96,4 +97,19 @@ exception_mask_t ExcMaskValid() {
return kExcMaskValid_10_11; return kExcMaskValid_10_11;
} }
base::mac::ScopedMachSendRight SystemCrashReporterHandler() {
const char kSystemCrashReporterServiceName[] = "com.apple.ReportCrash";
exception_handler_t system_crash_reporter_handler;
kern_return_t kr = bootstrap_look_up(bootstrap_port,
kSystemCrashReporterServiceName,
&system_crash_reporter_handler);
if (kr != BOOTSTRAP_SUCCESS) {
BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up "
<< kSystemCrashReporterServiceName;
system_crash_reporter_handler = MACH_PORT_NULL;
}
return base::mac::ScopedMachSendRight(system_crash_reporter_handler);
}
} // namespace crashpad } // namespace crashpad

View File

@ -17,6 +17,8 @@
#include <mach/mach.h> #include <mach/mach.h>
#include "base/mac/scoped_mach_port.h"
namespace crashpad { namespace crashpad {
//! \brief `MACH_PORT_NULL` with the correct type for a Mach port, //! \brief `MACH_PORT_NULL` with the correct type for a Mach port,
@ -115,6 +117,20 @@ exception_mask_t ExcMaskAll();
//! support is present. //! support is present.
exception_mask_t ExcMaskValid(); exception_mask_t ExcMaskValid();
//! \brief Obtains the systems default Mach exception handler for crash-type
//! exceptions.
//!
//! This is obtained by looking up `"com.apple.ReportCrash"` with the bootstrap
//! server. The service name comes from the first launch agent loaded by
//! `launchd` with a `MachServices` entry having `ExceptionServer` set. This
//! launch agent is normally loaded from
//! `/System/Library/LaunchAgents/com.apple.ReportCrash.plist`.
//!
//! \return On success, a send right to an `exception_handler_t` corresponding
//! to the systems default crash reporter. On failure, `MACH_PORT_NULL`,
//! with a message logged.
base::mac::ScopedMachSendRight SystemCrashReporterHandler();
} // namespace crashpad } // namespace crashpad
#endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_ #endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_

View File

@ -131,6 +131,12 @@ TEST(MachExtensions, ExcMaskValid) {
EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll()); EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll());
} }
TEST(MachExtensions, SystemCrashReporterHandler) {
base::mac::ScopedMachSendRight
system_crash_reporter_handler(SystemCrashReporterHandler());
EXPECT_TRUE(system_crash_reporter_handler);
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad