mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 07:14:10 +08:00
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:
parent
5de461e8c8
commit
9086d25ce8
@ -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
|
||||
|
||||
// Don’t expose EXC_MASK_ALL or EXC_MASK_VALID at all, because their definitions
|
||||
// vary with SDK, and older kernels will reject values that they don’t
|
||||
// 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
|
||||
|
||||
// Don’t expose EXC_MASK_ALL at all, because its definition varies with SDK, and
|
||||
// older kernels will reject values that they don’t 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;
|
||||
// It’s 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 task’s 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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user