diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index c81dd543..02067c9b 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -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)) { diff --git a/client/simulate_crash_mac_test.cc b/client/simulate_crash_mac_test.cc index e585838a..7e0109a3 100644 --- a/client/simulate_crash_mac_test.cc +++ b/client/simulate_crash_mac_test.cc @@ -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: diff --git a/compat/mac/AvailabilityMacros.h b/compat/mac/AvailabilityMacros.h index c8e16d74..decaeb51 100644 --- a/compat/mac/AvailabilityMacros.h +++ b/compat/mac/AvailabilityMacros.h @@ -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_ diff --git a/compat/mac/mach/mach.h b/compat/mac/mach/mach.h index 0cf86d05..9cfe6b62 100644 --- a/compat/mac/mach/mach.h +++ b/compat/mac/mach/mach.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__) // -// 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 // diff --git a/compat/non_mac/mach/mach.h b/compat/non_mac/mach/mach.h index d7cc4905..f33bb10f 100644 --- a/compat/non_mac/mach/mach.h +++ b/compat/non_mac/mach/mach.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_ diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc index fced33c8..bad4c1c6 100644 --- a/handler/mac/crash_report_exception_handler.cc +++ b/handler/mac/crash_report_exception_handler.cc @@ -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 diff --git a/snapshot/mac/mach_o_image_annotations_reader_test.cc b/snapshot/mac/mach_o_image_annotations_reader_test.cc index 6609b059..32f3b5c0 100644 --- a/snapshot/mac/mach_o_image_annotations_reader_test.cc +++ b/snapshot/mac/mach_o_image_annotations_reader_test.cc @@ -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: diff --git a/tools/mac/catch_exception_tool.cc b/tools/mac/catch_exception_tool.cc index d3b2dd58..902010be 100644 --- a/tools/mac/catch_exception_tool.cc +++ b/tools/mac/catch_exception_tool.cc @@ -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 and + // . + 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(&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: diff --git a/tools/mac/exception_port_tool.ad b/tools/mac/exception_port_tool.ad index 3ef36f12..e102cc00 100644 --- a/tools/mac/exception_port_tool.ad +++ b/tools/mac/exception_port_tool.ad @@ -50,11 +50,11 @@ restricted to the superuser. 'MASK' defines the mask of exception types to handle, from ++. 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 diff --git a/tools/mac/exception_port_tool.cc b/tools/mac/exception_port_tool.cc index 3bb96524..9efff7cf 100644 --- a/tools/mac/exception_port_tool.cc +++ b/tools/mac/exception_port_tool.cc @@ -211,8 +211,7 @@ void ShowExceptionPorts(const ExceptionPorts& exception_ports, const char* target_name = exception_ports.TargetTypeName(); std::vector handlers; - if (!exception_ports.GetExceptionPorts(ExcMaskAll() | EXC_MASK_CRASH, - &handlers)) { + if (!exception_ports.GetExceptionPorts(ExcMaskValid(), &handlers)) { return; } diff --git a/util/mach/exc_server_variants.cc b/util/mach/exc_server_variants.cc index e5916c7d..c5a74a3b 100644 --- a/util/mach/exc_server_variants.cc +++ b/util/mach/exc_server_variants.cc @@ -14,12 +14,14 @@ #include "util/mach/exc_server_variants.h" +#include #include #include #include #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; } diff --git a/util/mach/exc_server_variants.h b/util/mach/exc_server_variants.h index aaac36ad..95d1c12d 100644 --- a/util/mach/exc_server_variants.h +++ b/util/mach/exc_server_variants.h @@ -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. diff --git a/util/mach/exc_server_variants_test.cc b/util/mach/exc_server_variants_test.cc index a9a30000..dfaf646b 100644 --- a/util/mach/exc_server_variants_test.cc +++ b/util/mach/exc_server_variants_test.cc @@ -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)); } } diff --git a/util/mach/exception_ports_test.cc b/util/mach/exception_ports_test.cc index 4ab92538..478dfe21 100644 --- a/util/mach/exception_ports_test.cc +++ b/util/mach/exception_ports_test.cc @@ -81,8 +81,7 @@ void TestGetExceptionPorts(const ExceptionPorts& exception_ports, } std::vector 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 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 { diff --git a/util/mach/mach_extensions.cc b/util/mach/mach_extensions.cc index 63aae31d..a3e147cc 100644 --- a/util/mach/mach_extensions.cc +++ b/util/mach/mach_extensions.cc @@ -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 . + const exception_mask_t kExcMaskValid_10_11 = + kExcMaskValid_10_6 | EXC_MASK_CORPSE_NOTIFY; + return kExcMaskValid_10_11; +} + } // namespace crashpad diff --git a/util/mach/mach_extensions.h b/util/mach/mach_extensions.h index d1e392ad..4f2861bf 100644 --- a/util/mach/mach_extensions.h +++ b/util/mach/mach_extensions.h @@ -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_ diff --git a/util/mach/mach_extensions_test.cc b/util/mach/mach_extensions_test.cc index aefe489d..9853944c 100644 --- a/util/mach/mach_extensions_test.cc +++ b/util/mach/mach_extensions_test.cc @@ -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 diff --git a/util/mach/symbolic_constants_mach.cc b/util/mach/symbolic_constants_mach.cc index f505d052..fee906ba 100644 --- a/util/mach/symbolic_constants_mach.cc +++ b/util/mach/symbolic_constants_mach.cc @@ -41,6 +41,7 @@ const char* kExceptionNames[] = { "CRASH", "RESOURCE", "GUARD", + "CORPSE_NOTIFY", }; static_assert(arraysize(kExceptionNames) == EXC_TYPES_COUNT, "kExceptionNames length");