diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 7095de04..9ed2aea5 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -122,6 +122,30 @@ class CrashpadClient { //! \return `true` on success, `false` on failure with a message logged. 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 task’s exception port as in UseHandler(), but the + //! exception handler used is obtained from SystemCrashReporterHandler(). If + //! the system’s crash reporter handler cannot be determined, the task’s + //! 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 parent’s Crashpad + //! handler, but wants to sever this tie. + //! + //! \return `true` on success, `false` on failure with a message logged. + static bool UseSystemDefaultHandler(); +#endif + private: #if defined(OS_MACOSX) base::mac::ScopedMachSendRight exception_port_; diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index 02067c9b..e7ad0642 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -26,6 +26,8 @@ #include "util/mach/mach_extensions.h" #include "util/posix/close_multiple.h" +namespace crashpad { + namespace { 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); } -} // 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() : exception_port_() { @@ -179,46 +217,17 @@ bool CrashpadClient::StartHandler( bool CrashpadClient::UseHandler() { DCHECK_NE(exception_port_, kMachPortNull); - // 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. - 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 SetCrashExceptionPorts(exception_port_); +} - 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 diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc index bad4c1c6..78710d69 100644 --- a/handler/mac/crash_report_exception_handler.cc +++ b/handler/mac/crash_report_exception_handler.cc @@ -14,12 +14,11 @@ #include "handler/mac/crash_report_exception_handler.h" -#include - #include #include "base/logging.h" #include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" #include "base/strings/stringprintf.h" #include "client/settings.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 // system-level com.apple.ReportCrash.Root job, and not to the user-level // job that they are forwarded to here. - mach_port_t system_crash_reporter_port; - const char kSystemCrashReporterServiceName[] = "com.apple.ReportCrash"; - kern_return_t kr = bootstrap_look_up(bootstrap_port, - kSystemCrashReporterServiceName, - &system_crash_reporter_port); - if (kr != BOOTSTRAP_SUCCESS) { - BOOTSTRAP_LOG(ERROR, kr) << "bootstrap_look_up " - << kSystemCrashReporterServiceName; - } else { + base::mac::ScopedMachSendRight + system_crash_reporter_handler(SystemCrashReporterHandler()); + if (system_crash_reporter_handler) { // Make copies of mutable out parameters so that the system crash reporter // can’t influence the state returned by this method. thread_state_flavor_t flavor_forward = *flavor; @@ -227,9 +220,9 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( // problems may arise if the state wasn’t available and the system crash // reporter changes in the future to use it. However, normally, the state // will be available. - kr = UniversalExceptionRaise( + kern_return_t kr = UniversalExceptionRaise( EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, - system_crash_reporter_port, + system_crash_reporter_handler, thread, task, exception, @@ -240,8 +233,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( old_state_count, new_state_forward_count ? &new_state_forward[0] : nullptr, &new_state_forward_count); - MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) - << "UniversalExceptionRaise " << kSystemCrashReporterServiceName; + MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "UniversalExceptionRaise"; } } diff --git a/util/mach/mach_extensions.cc b/util/mach/mach_extensions.cc index a3e147cc..c2343c21 100644 --- a/util/mach/mach_extensions.cc +++ b/util/mach/mach_extensions.cc @@ -16,6 +16,7 @@ #include #include +#include #include "base/mac/mach_logging.h" #include "util/mac/mac_util.h" @@ -96,4 +97,19 @@ exception_mask_t ExcMaskValid() { 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 diff --git a/util/mach/mach_extensions.h b/util/mach/mach_extensions.h index 4f2861bf..c8753035 100644 --- a/util/mach/mach_extensions.h +++ b/util/mach/mach_extensions.h @@ -17,6 +17,8 @@ #include +#include "base/mac/scoped_mach_port.h" + namespace crashpad { //! \brief `MACH_PORT_NULL` with the correct type for a Mach port, @@ -115,6 +117,20 @@ exception_mask_t ExcMaskAll(); //! support is present. exception_mask_t ExcMaskValid(); +//! \brief Obtains the system’s 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 system’s default crash reporter. On failure, `MACH_PORT_NULL`, +//! with a message logged. +base::mac::ScopedMachSendRight SystemCrashReporterHandler(); + } // namespace crashpad #endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_ diff --git a/util/mach/mach_extensions_test.cc b/util/mach/mach_extensions_test.cc index 9853944c..30013acb 100644 --- a/util/mach/mach_extensions_test.cc +++ b/util/mach/mach_extensions_test.cc @@ -131,6 +131,12 @@ TEST(MachExtensions, ExcMaskValid) { EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll()); } +TEST(MachExtensions, SystemCrashReporterHandler) { + base::mac::ScopedMachSendRight + system_crash_reporter_handler(SystemCrashReporterHandler()); + EXPECT_TRUE(system_crash_reporter_handler); +} + } // namespace } // namespace test } // namespace crashpad