Don’t trigger EXC_CORPSE_NOTIFY on OS X 10.11

CrashReportExceptionHandler::CatchMachException() must always set a
valid new_state. Failing to do so appears to trigger corpse generation
on OS X 10.11. This is addressed by calling ExcServerCopyState().
Previously, this was not done for exceptions forwarded to the user
ReportCrash, under the apparent mistaken assumption that ReportCrash
would do it. However, ReportCrash is given copies of out-parameters like
new_state to explicitly prevent it from influencing Crashpad’s returned
state.

ExcServerSuccessfulReturnValue() must not return MACH_RCV_PORT_DIED for
an EXC_CRASH handler on OS X 10.11. This appears to trigger corpse
generation. This is addressed by always returning KERN_SUCCESS from
EXC_CRASH handlers on OS X 10.11.

This also adds generic EXC_CORPSE_NOTIFY support throughout Crashpad.
The crashpad_handler does not listen for this exception type, but it is
now possible to work with this exception type using tools like
exception_port_tool and catch_exception_tool.

BUG=crashpad:48
TEST=Crashes handled by crashpad_handler do not result in the generation
     of reports in the root /Library/Logs/DiagnosticReports.

R=kerrnel@chromium.org, rsesek@chromium.org

Review URL: https://codereview.chromium.org/1305893010 .
This commit is contained in:
Mark Mentovai 2015-09-04 14:29:12 -04:00
Родитель 5de461e8c8
Коммит 9086d25ce8
18 изменённых файлов: 282 добавлений и 86 удалений

Просмотреть файл

@ -207,13 +207,11 @@ bool CrashpadClient::UseHandler() {
//
// 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 ExcMaskAll(). EXC_MASK_CRASH is not present in
// ExcMaskAll() but is always supported. See the documentation for
// ExcMaskAll().
// 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) & ExcMaskAll()),
(EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) &
ExcMaskValid(),
exception_port_,
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
MACHINE_THREAD_STATE)) {

Просмотреть файл

@ -228,7 +228,7 @@ class TestSimulateCrashMac final : public MachMultiprocess,
ExcServerCopyState(
behavior, old_state, old_state_count, new_state, new_state_count);
return ExcServerSuccessfulReturnValue(behavior, true);
return ExcServerSuccessfulReturnValue(exception, behavior, true);
}
private:

Просмотреть файл

@ -41,4 +41,10 @@
#define MAC_OS_X_VERSION_10_10 101000
#endif
// 10.11 SDK
#ifndef MAC_OS_X_VERSION_10_11
#define MAC_OS_X_VERSION_10_11 101100
#endif
#endif // CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_

Просмотреть файл

@ -39,24 +39,33 @@
#define EXC_MASK_GUARD (1 << EXC_GUARD)
#endif
// Dont expose EXC_MASK_ALL or EXC_MASK_VALID at all, because their definitions
// vary with SDK, and older kernels will reject values that they dont
// understand. Instead, use crashpad::ExcMaskAll(), which computes the correct
// value of EXC_MASK_ALL for the running system.
// 10.11 SDK
#ifndef EXC_CORPSE_NOTIFY
#define EXC_CORPSE_NOTIFY 13
#endif
#ifndef EXC_MASK_CORPSE_NOTIFY
#define EXC_MASK_CORPSE_NOTIFY (1 << EXC_CORPSE_NOTIFY)
#endif
// Dont expose EXC_MASK_ALL at all, because its definition varies with SDK, and
// older kernels will reject values that they dont understand. Instead, use
// crashpad::ExcMaskAll(), which computes the correct value of EXC_MASK_ALL for
// the running system.
#undef EXC_MASK_ALL
#undef EXC_MASK_VALID
#if defined(__i386__) || defined(__x86_64__)
// <mach/i386/exception.h>
// 10.9 SDK
// 10.11 SDK
#if EXC_TYPES_COUNT > 13 // Definition varies with SDK
#if EXC_TYPES_COUNT > 14 // Definition varies with SDK
#error Update this file for new exception types
#elif EXC_TYPES_COUNT != 13
#elif EXC_TYPES_COUNT != 14
#undef EXC_TYPES_COUNT
#define EXC_TYPES_COUNT 13
#define EXC_TYPES_COUNT 14
#endif
// <mach/i386/thread_status.h>

Просмотреть файл

@ -36,6 +36,9 @@
#define EXC_CRASH 10
#define EXC_RESOURCE 11
#define EXC_GUARD 12
#define EXC_CORPSE_NOTIFY 13
#define EXC_TYPES_COUNT 14
//! \}
#endif // CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_

Просмотреть файл

@ -125,7 +125,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
// so.
ExcServerCopyState(
behavior, old_state, old_state_count, new_state, new_state_count);
return ExcServerSuccessfulReturnValue(behavior, false);
return ExcServerSuccessfulReturnValue(exception, behavior, false);
}
CrashpadInfoClientOptions client_options;
@ -186,7 +186,6 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
upload_thread_->ReportPending();
}
bool forwarded = false;
if (client_options.system_crash_reporter_forwarding != TriState::kDisabled &&
(exception == EXC_CRASH ||
exception == EXC_RESOURCE ||
@ -241,21 +240,15 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
old_state_count,
new_state_forward_count ? &new_state_forward[0] : nullptr,
&new_state_forward_count);
if (kr == KERN_SUCCESS) {
forwarded = true;
} else {
MACH_LOG(WARNING, kr)
<< "UniversalExceptionRaise " << kSystemCrashReporterServiceName;
}
MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr)
<< "UniversalExceptionRaise " << kSystemCrashReporterServiceName;
}
}
if (!forwarded) {
ExcServerCopyState(
behavior, old_state, old_state_count, new_state, new_state_count);
}
ExcServerCopyState(
behavior, old_state, old_state_count, new_state, new_state_count);
return ExcServerSuccessfulReturnValue(behavior, false);
return ExcServerSuccessfulReturnValue(exception, behavior, false);
}
} // namespace crashpad

Просмотреть файл

@ -218,7 +218,9 @@ class TestMachOImageAnnotationsReader final
}
}
return ExcServerSuccessfulReturnValue(behavior, false);
ExcServerCopyState(
behavior, old_state, old_state_count, new_state, new_state_count);
return ExcServerSuccessfulReturnValue(exception, behavior, false);
}
private:

Просмотреть файл

@ -79,20 +79,34 @@ class ExceptionServer : public UniversalMachExcServer::Interface {
++*exceptions_handled_;
fprintf(options_.file,
"%s: behavior %s, ",
"%s: behavior %s",
me_.c_str(),
ExceptionBehaviorToString(
behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str());
kern_return_t kr;
if (ExceptionBehaviorHasIdentity(behavior)) {
pid_t pid;
kr = pid_for_task(task, &pid);
if (kr != KERN_SUCCESS) {
MACH_LOG(ERROR, kr) << "pid_for_task";
return KERN_FAILURE;
// Its not possible to call pid_for_task() once EXC_CORPSE_NOTIFY has
// been generated. It is possible to obtain the process ID by mapping the
// corpse kcdata area from the tasks address space at code[0] (size
// code[1]) and locating TASK_CRASHINFO_PID within that area. This area
// also includes TASK_CRASHINFO_CRASHED_THREADID which could be used
// instead of thread_info() below, and TASK_CRASHINFO_EXCEPTION_CODES
// which could be used to recover the exception codes passed to the
// EXC_CRASH handler. None of this is currently done because corpses are a
// new 10.11-only feature. See 10.11 <corpses/task_corpse.h> and
// <kern/kern_cdata.h>.
if (exception != EXC_CORPSE_NOTIFY) {
pid_t pid;
kr = pid_for_task(task, &pid);
if (kr != KERN_SUCCESS) {
fprintf(options_.file, "\n");
fflush(options_.file);
MACH_LOG(ERROR, kr) << "pid_for_task";
return KERN_FAILURE;
}
fprintf(options_.file, ", pid %d", pid);
}
fprintf(options_.file, "pid %d, ", pid);
thread_identifier_info identifier_info;
mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
@ -101,23 +115,25 @@ class ExceptionServer : public UniversalMachExcServer::Interface {
reinterpret_cast<thread_info_t>(&identifier_info),
&count);
if (kr != KERN_SUCCESS) {
fprintf(options_.file, "\n");
fflush(options_.file);
MACH_LOG(ERROR, kr) << "thread_info";
return KERN_FAILURE;
}
fprintf(options_.file, "thread %lld, ", identifier_info.thread_id);
fprintf(options_.file, ", thread %lld", identifier_info.thread_id);
}
fprintf(
options_.file,
"exception %s, codes[%d] ",
", exception %s, codes[%d]",
ExceptionToString(exception, kUseFullName | kUnknownIsNumeric).c_str(),
code_count);
for (size_t index = 0; index < code_count; ++index) {
fprintf(options_.file,
"%#llx%s",
code[index],
index != code_count - 1 ? ", " : "");
"%s %#llx",
index != 0 ? "," : "",
code[index]);
}
if (exception == EXC_CRASH) {
@ -153,7 +169,7 @@ class ExceptionServer : public UniversalMachExcServer::Interface {
ExcServerCopyState(
behavior, old_state, old_state_count, new_state, new_state_count);
return ExcServerSuccessfulReturnValue(behavior, false);
return ExcServerSuccessfulReturnValue(exception, behavior, false);
}
private:

Просмотреть файл

@ -50,11 +50,11 @@ restricted to the superuser.
'MASK' defines the mask of exception types to handle, from
+<mach/exception_types.h>+. This can be *BAD_ACCESS*, *BAD_INSTRUCTION*,
*ARITHMETIC*, *EMULATION*, *SOFTWARE*, *BREAKPOINT*, *SYSCALL*, *MACH_SYSCALL*,
*RPC_ALERT*, *CRASH*, *RESOURCE*, or *GUARD*. Different exception types may be
combined by combining them with pipe characters (*|*). The special value *ALL*
includes each exception type except for *CRASH*. To truly specify all exception
types including *CRASH*, use *ALL|CRASH*. The default value of 'MASK' is
*CRASH*.
*RPC_ALERT*, *CRASH*, *RESOURCE*, *GUARD*, or *CORPSE_NOTIFY*. Different
exception types may be combined by combining them with pipe characters (*|*).
The special value *ALL* includes each exception type except for *CRASH*. To
truly specify all exception types including *CRASH*, use *ALL|CRASH*. The
default value of 'MASK' is *CRASH*.
+
*behavior*='BEHAVIOR':::
'BEHAVIOR' defines the specific exception handler routine to be called when an

Просмотреть файл

@ -211,8 +211,7 @@ void ShowExceptionPorts(const ExceptionPorts& exception_ports,
const char* target_name = exception_ports.TargetTypeName();
std::vector<ExceptionPorts::ExceptionHandler> handlers;
if (!exception_ports.GetExceptionPorts(ExcMaskAll() | EXC_MASK_CRASH,
&handlers)) {
if (!exception_ports.GetExceptionPorts(ExcMaskValid(), &handlers)) {
return;
}

Просмотреть файл

@ -14,12 +14,14 @@
#include "util/mach/exc_server_variants.h"
#include <AvailabilityMacros.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include "base/logging.h"
#include "util/mac/mac_util.h"
#include "util/mach/composite_mach_message_server.h"
#include "util/mach/exc.h"
#include "util/mach/exception_behaviors.h"
@ -767,8 +769,17 @@ mach_msg_size_t UniversalMachExcServer::MachMessageServerReplySize() {
return impl_->MachMessageServerReplySize();
}
kern_return_t ExcServerSuccessfulReturnValue(exception_behavior_t behavior,
kern_return_t ExcServerSuccessfulReturnValue(exception_type_t exception,
exception_behavior_t behavior,
bool set_thread_state) {
if (exception == EXC_CRASH
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
&& MacOSXMinorVersion() >= 11
#endif
) {
return KERN_SUCCESS;
}
if (!set_thread_state && ExceptionBehaviorHasState(behavior)) {
return MACH_RCV_PORT_DIED;
}

Просмотреть файл

@ -150,6 +150,17 @@ class UniversalMachExcServer final : public MachMessageServer::Interface {
//! schedulable, so there is no point in setting the states of any of its
//! threads.
//!
//! On OS X 10.11, the `MACH_RCV_PORT_DIED` mechanism cannot be used with an
//! `EXC_CRASH` handler without triggering an undesirable `EXC_CORPSE_NOTIFY`
//! exception. In that case, `KERN_SUCCESS` is always returned. Because this
//! function may return `KERN_SUCCESS` for a state-carrying exception, it is
//! important to ensure that the state returned by a state-carrying exception
//! handler is valid, because it will be passed to `thread_set_status()`.
//! ExcServerCopyState() may be used to achieve this.
//!
//! \param[in] exception The exception type passed to the exception handler.
//! This may be taken directly from the \a exception parameter of
//! internal::SimplifiedExcServer::Interface::CatchException(), for example.
//! \param[in] behavior The behavior of the exception handler as invoked. This
//! may be taken directly from the \a behavior parameter of
//! internal::SimplifiedExcServer::Interface::CatchException(), for example.
@ -160,10 +171,11 @@ class UniversalMachExcServer final : public MachMessageServer::Interface {
//!
//! \return `KERN_SUCCESS` or `MACH_RCV_PORT_DIED`. `KERN_SUCCESS` is used when
//! \a behavior is not a state-carrying behavior, or when it is a
//! state-carrying behavior and \a set_thread_state is `true`.
//! `MACH_RCV_PORT_DIED` is used when \a behavior is a state-carrying
//! behavior and \a set_thread_state is `false`.
kern_return_t ExcServerSuccessfulReturnValue(exception_behavior_t behavior,
//! state-carrying behavior and \a set_thread_state is `true`, or for
//! `EXC_CRASH` exceptions on OS X 10.11 and later. Otherwise,
//! `MACH_RCV_PORT_DIED` is used.
kern_return_t ExcServerSuccessfulReturnValue(exception_type_t exception,
exception_behavior_t behavior,
bool set_thread_state);
//! \brief Copies the old state to the new state for state-carrying exceptions.

Просмотреть файл

@ -22,6 +22,7 @@
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
#include "test/mac/mach_multiprocess.h"
#include "util/mac/mac_util.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/exception_types.h"
#include "util/mach/mach_message.h"
@ -1035,7 +1036,7 @@ class TestExcServerVariants : public MachMultiprocess,
ExcServerCopyState(
behavior, old_state, old_state_count, new_state, new_state_count);
return ExcServerSuccessfulReturnValue(behavior, false);
return ExcServerSuccessfulReturnValue(exception, behavior, false);
}
private:
@ -1186,26 +1187,64 @@ TEST(ExcServerVariants, ThreadStates) {
}
TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) {
const kern_return_t prefer_not_set_thread_state =
MacOSXMinorVersion() < 11 ? MACH_RCV_PORT_DIED : KERN_SUCCESS;
struct TestData {
exception_type_t exception;
exception_behavior_t behavior;
bool set_thread_state;
kern_return_t kr;
};
const TestData kTestData[] = {
{EXCEPTION_DEFAULT, false, KERN_SUCCESS},
{EXCEPTION_STATE, false, MACH_RCV_PORT_DIED},
{EXCEPTION_STATE_IDENTITY, false, MACH_RCV_PORT_DIED},
{kMachExceptionCodes | EXCEPTION_DEFAULT, false, KERN_SUCCESS},
{kMachExceptionCodes | EXCEPTION_STATE, false, MACH_RCV_PORT_DIED},
{kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
{EXC_CRASH, EXCEPTION_DEFAULT, false, KERN_SUCCESS},
{EXC_CRASH, EXCEPTION_STATE, false, prefer_not_set_thread_state},
{EXC_CRASH, EXCEPTION_STATE_IDENTITY, false, prefer_not_set_thread_state},
{EXC_CRASH, kMachExceptionCodes | EXCEPTION_DEFAULT, false, KERN_SUCCESS},
{EXC_CRASH,
kMachExceptionCodes | EXCEPTION_STATE,
false,
prefer_not_set_thread_state},
{EXC_CRASH,
kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
false,
prefer_not_set_thread_state},
{EXC_CRASH, EXCEPTION_DEFAULT, true, KERN_SUCCESS},
{EXC_CRASH, EXCEPTION_STATE, true, KERN_SUCCESS},
{EXC_CRASH, EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS},
{EXC_CRASH, kMachExceptionCodes | EXCEPTION_DEFAULT, true, KERN_SUCCESS},
{EXC_CRASH, kMachExceptionCodes | EXCEPTION_STATE, true, KERN_SUCCESS},
{EXC_CRASH,
kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
true,
KERN_SUCCESS},
{EXC_BAD_ACCESS, EXCEPTION_DEFAULT, false, KERN_SUCCESS},
{EXC_BAD_INSTRUCTION, EXCEPTION_STATE, false, MACH_RCV_PORT_DIED},
{EXC_ARITHMETIC, EXCEPTION_STATE_IDENTITY, false, MACH_RCV_PORT_DIED},
{EXC_EMULATION,
kMachExceptionCodes | EXCEPTION_DEFAULT,
false,
KERN_SUCCESS},
{EXC_SOFTWARE,
kMachExceptionCodes | EXCEPTION_STATE,
false,
MACH_RCV_PORT_DIED},
{EXCEPTION_DEFAULT, true, KERN_SUCCESS},
{EXCEPTION_STATE, true, KERN_SUCCESS},
{EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS},
{kMachExceptionCodes | EXCEPTION_DEFAULT, true, KERN_SUCCESS},
{kMachExceptionCodes | EXCEPTION_STATE, true, KERN_SUCCESS},
{kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS},
{EXC_BREAKPOINT,
kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
false,
MACH_RCV_PORT_DIED},
{EXC_SYSCALL, EXCEPTION_DEFAULT, true, KERN_SUCCESS},
{EXC_MACH_SYSCALL, EXCEPTION_STATE, true, KERN_SUCCESS},
{EXC_RPC_ALERT, EXCEPTION_STATE_IDENTITY, true, KERN_SUCCESS},
{EXC_RESOURCE,
kMachExceptionCodes | EXCEPTION_DEFAULT,
true,
KERN_SUCCESS},
{EXC_GUARD, kMachExceptionCodes | EXCEPTION_STATE, true, KERN_SUCCESS},
{EXC_CORPSE_NOTIFY,
kMachExceptionCodes | EXCEPTION_STATE_IDENTITY,
true,
KERN_SUCCESS},
};
for (size_t index = 0; index < arraysize(kTestData); ++index) {
@ -1217,7 +1256,8 @@ TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) {
test_data.set_thread_state ? "true" : "false"));
EXPECT_EQ(test_data.kr,
ExcServerSuccessfulReturnValue(test_data.behavior,
ExcServerSuccessfulReturnValue(test_data.exception,
test_data.behavior,
test_data.set_thread_state));
}
}

Просмотреть файл

@ -81,8 +81,7 @@ void TestGetExceptionPorts(const ExceptionPorts& exception_ports,
}
std::vector<ExceptionPorts::ExceptionHandler> handlers;
ASSERT_TRUE(exception_ports.GetExceptionPorts(
ExcMaskAll() | EXC_MASK_CRASH, &handlers));
ASSERT_TRUE(exception_ports.GetExceptionPorts(ExcMaskValid(), &handlers));
EXPECT_GE(handlers.size(), crash_handler.size());
bool found = false;
@ -200,7 +199,9 @@ class TestExceptionPorts : public MachMultiprocess,
EXPECT_EQ(0, AuditPIDFromMachMessageTrailer(trailer));
return ExcServerSuccessfulReturnValue(behavior, false);
ExcServerCopyState(
behavior, old_state, old_state_count, new_state, new_state_count);
return ExcServerSuccessfulReturnValue(exception, behavior, false);
}
private:
@ -590,8 +591,7 @@ TEST(ExceptionPorts, HostExceptionPorts) {
EXPECT_STREQ("host", explicit_host_ports.TargetTypeName());
std::vector<ExceptionPorts::ExceptionHandler> handlers;
bool rv = explicit_host_ports.GetExceptionPorts(
ExcMaskAll() | EXC_MASK_CRASH, &handlers);
bool rv = explicit_host_ports.GetExceptionPorts(ExcMaskValid(), &handlers);
if (geteuid() == 0) {
EXPECT_TRUE(rv);
} else {
@ -602,8 +602,7 @@ TEST(ExceptionPorts, HostExceptionPorts) {
HOST_NULL);
EXPECT_STREQ("host", implicit_host_ports.TargetTypeName());
rv = implicit_host_ports.GetExceptionPorts(
ExcMaskAll() | EXC_MASK_CRASH, &handlers);
rv = implicit_host_ports.GetExceptionPorts(ExcMaskValid(), &handlers);
if (geteuid() == 0) {
EXPECT_TRUE(rv);
} else {

Просмотреть файл

@ -44,18 +44,24 @@ exception_mask_t ExcMaskAll() {
// xnu-2422.110.17/osfmk/mach/ipc_tt.c.
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
int mac_os_x_minor_version = MacOSXMinorVersion();
const int mac_os_x_minor_version = MacOSXMinorVersion();
#endif
// See 10.6.8 xnu-1504.15.3/osfmk/mach/exception_types.h. 10.7 uses the same
// definition as 10.6. See 10.7.5 xnu-1699.32.7/osfmk/mach/exception_types.h
const exception_mask_t kExcMaskAll_10_6 =
EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC |
EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT |
EXC_MASK_SYSCALL | EXC_MASK_MACH_SYSCALL | EXC_MASK_RPC_ALERT |
EXC_MASK_BAD_ACCESS |
EXC_MASK_BAD_INSTRUCTION |
EXC_MASK_ARITHMETIC |
EXC_MASK_EMULATION |
EXC_MASK_SOFTWARE |
EXC_MASK_BREAKPOINT |
EXC_MASK_SYSCALL |
EXC_MASK_MACH_SYSCALL |
EXC_MASK_RPC_ALERT |
EXC_MASK_MACHINE;
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_7
if (mac_os_x_minor_version <= 7) {
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
if (mac_os_x_minor_version < 8) {
return kExcMaskAll_10_6;
}
#endif
@ -64,8 +70,8 @@ exception_mask_t ExcMaskAll() {
// xnu-2050.48.11/osfmk/mach/exception_types.h.
const exception_mask_t kExcMaskAll_10_8 =
kExcMaskAll_10_6 | EXC_MASK_RESOURCE;
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_8
if (mac_os_x_minor_version <= 8) {
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
if (mac_os_x_minor_version < 9) {
return kExcMaskAll_10_8;
}
#endif
@ -76,4 +82,18 @@ exception_mask_t ExcMaskAll() {
return kExcMaskAll_10_9;
}
exception_mask_t ExcMaskValid() {
const exception_mask_t kExcMaskValid_10_6 = ExcMaskAll() | EXC_MASK_CRASH;
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
if (MacOSXMinorVersion() < 11) {
return kExcMaskValid_10_6;
}
#endif
// 10.11 added EXC_MASK_CORPSE_NOTIFY. See 10.11 <mach/exception_types.h>.
const exception_mask_t kExcMaskValid_10_11 =
kExcMaskValid_10_6 | EXC_MASK_CORPSE_NOTIFY;
return kExcMaskValid_10_11;
}
} // namespace crashpad

Просмотреть файл

@ -94,11 +94,27 @@ mach_port_t NewMachPort(mach_port_right_t right);
//! recognize. Calling this function will return a value for `EXC_MASK_ALL`
//! appropriate for the system at run time.
//!
//! \note `EXC_MASK_ALL` does not include the value of `EXC_MASK_CRASH`.
//! Consumers that want `EXC_MASK_ALL` along with `EXC_MASK_CRASH` must use
//! ExcMaskAll() | `EXC_MASK_CRASH` explicitly.
//! \note `EXC_MASK_ALL` does not include the value of `EXC_MASK_CRASH` or
//! `EXC_MASK_CORPSE_NOTIFY`. Consumers that want `EXC_MASK_ALL` along with
//! `EXC_MASK_CRASH` may use ExcMaskAll() `| EXC_MASK_CRASH`. Consumers may
//! use ExcMaskValid() for `EXC_MASK_ALL` along with `EXC_MASK_CRASH`,
//! `EXC_MASK_CORPSE_NOTIFY`, and any values that come into existence in the
//! future.
exception_mask_t ExcMaskAll();
//! \brief An exception mask containing every possible exception understood by
//! the operating system at run time.
//!
//! `EXC_MASK_ALL`, and thus ExcMaskAll(), never includes the value of
//! `EXC_MASK_CRASH` or `EXC_MASK_CORPSE_NOTIFY`. For situations where an
//! exception mask corresponding to every possible exception understood by the
//! running kernel is desired, use this function instead.
//!
//! Should new exception types be introduced in the future, this function will
//! be updated to include their bits in the returned mask value when run time
//! support is present.
exception_mask_t ExcMaskValid();
} // namespace crashpad
#endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_

Просмотреть файл

@ -17,6 +17,7 @@
#include "base/mac/scoped_mach_port.h"
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
#include "util/mac/mac_util.h"
namespace crashpad {
namespace test {
@ -60,6 +61,76 @@ TEST(MachExtensions, NewMachPort_DeadName) {
EXPECT_EQ(MACH_PORT_TYPE_DEAD_NAME, type);
}
const exception_mask_t kExcMaskBasic =
EXC_MASK_BAD_ACCESS |
EXC_MASK_BAD_INSTRUCTION |
EXC_MASK_ARITHMETIC |
EXC_MASK_EMULATION |
EXC_MASK_SOFTWARE |
EXC_MASK_BREAKPOINT |
EXC_MASK_SYSCALL |
EXC_MASK_MACH_SYSCALL |
EXC_MASK_RPC_ALERT;
TEST(MachExtensions, ExcMaskAll) {
const exception_mask_t exc_mask_all = ExcMaskAll();
EXPECT_EQ(kExcMaskBasic, exc_mask_all & kExcMaskBasic);
EXPECT_FALSE(exc_mask_all & EXC_MASK_CRASH);
EXPECT_FALSE(exc_mask_all & EXC_MASK_CORPSE_NOTIFY);
const int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version >= 8) {
EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);
} else {
EXPECT_FALSE(exc_mask_all & EXC_MASK_RESOURCE);
}
if (mac_os_x_minor_version >= 9) {
EXPECT_TRUE(exc_mask_all & EXC_MASK_GUARD);
} else {
EXPECT_FALSE(exc_mask_all & EXC_MASK_GUARD);
}
// Bit 0 should not be set.
EXPECT_FALSE(ExcMaskAll() & 1);
// Every bit set in ExcMaskAll() must also be set in ExcMaskValid().
EXPECT_EQ(ExcMaskAll(), ExcMaskAll() & ExcMaskValid());
}
TEST(MachExtensions, ExcMaskValid) {
const exception_mask_t exc_mask_valid = ExcMaskValid();
EXPECT_EQ(kExcMaskBasic, exc_mask_valid & kExcMaskBasic);
EXPECT_TRUE(exc_mask_valid & EXC_MASK_CRASH);
const int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version >= 8) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);
} else {
EXPECT_FALSE(exc_mask_valid & EXC_MASK_RESOURCE);
}
if (mac_os_x_minor_version >= 9) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_GUARD);
} else {
EXPECT_FALSE(exc_mask_valid & EXC_MASK_GUARD);
}
if (mac_os_x_minor_version >= 11) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
} else {
EXPECT_FALSE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
}
// Bit 0 should not be set.
EXPECT_FALSE(ExcMaskValid() & 1);
// There must be bits set in ExcMaskValid() that are not set in ExcMaskAll().
EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll());
}
} // namespace
} // namespace test
} // namespace crashpad

Просмотреть файл

@ -41,6 +41,7 @@ const char* kExceptionNames[] = {
"CRASH",
"RESOURCE",
"GUARD",
"CORPSE_NOTIFY",
};
static_assert(arraysize(kExceptionNames) == EXC_TYPES_COUNT,
"kExceptionNames length");