ios: Split bootstrap out from mach_extensions

mach_extensions is sensible on iOS, but bootstrap is not available
outside of macOS. To allow mach_extensions to be used cleanly on iOS,
the bootstrap code is moved into its own macOS-specific file.

Bug: crashpad:31
Change-Id: I7bf9d5194253b563954a1e55fbf67a16f686e8ff
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2154529
Reviewed-by: Justin Cohen <justincohen@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Mark Mentovai 2020-04-17 11:29:09 -04:00 committed by Commit Bot
parent 122a400d7b
commit ba24acb86c
18 changed files with 299 additions and 227 deletions

View File

@ -26,6 +26,7 @@
#include "base/mac/mach_logging.h"
#include "base/strings/stringprintf.h"
#include "util/mac/mac_util.h"
#include "util/mach/bootstrap.h"
#include "util/mach/child_port_handshake.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"

View File

@ -74,8 +74,8 @@
#include "handler/mac/crash_report_exception_handler.h"
#include "handler/mac/exception_handler_server.h"
#include "handler/mac/file_limit_annotation.h"
#include "util/mach/bootstrap.h"
#include "util/mach/child_port_handshake.h"
#include "util/mach/mach_extensions.h"
#include "util/posix/close_stdio.h"
#include "util/posix/signals.h"
#elif defined(OS_WIN)

View File

@ -28,6 +28,7 @@
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/mac/process_snapshot_mac.h"
#include "util/file/file_writer.h"
#include "util/mach/bootstrap.h"
#include "util/mach/exc_client_variants.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/exception_types.h"

View File

@ -57,14 +57,19 @@ static_library("test") {
}
}
if (crashpad_is_mac || crashpad_is_ios) {
sources += [
"mac/mach_errors.cc",
"mac/mach_errors.h",
]
}
if (crashpad_is_mac) {
sources += [
"mac/dyld.cc",
"mac/dyld.h",
"mac/exception_swallower.cc",
"mac/exception_swallower.h",
"mac/mach_errors.cc",
"mac/mach_errors.h",
"mac/mach_multiprocess.cc",
"mac/mach_multiprocess.h",
]

View File

@ -24,6 +24,7 @@
#include "base/mac/scoped_mach_port.h"
#include "base/strings/stringprintf.h"
#include "handler/mac/exception_handler_server.h"
#include "util/mach/bootstrap.h"
#include "util/mach/exc_server_variants.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"

View File

@ -14,8 +14,6 @@
#include "test/mac/mach_errors.h"
#include <servers/bootstrap.h>
#include "base/strings/stringprintf.h"
namespace {
@ -50,28 +48,5 @@ std::string MachErrorMessage(mach_error_t mach_err, const std::string& base) {
FormatMachErrorNumber(mach_err).c_str());
}
std::string BootstrapErrorMessage(kern_return_t bootstrap_err,
const std::string& base) {
switch (bootstrap_err) {
case BOOTSTRAP_SUCCESS:
case BOOTSTRAP_NOT_PRIVILEGED:
case BOOTSTRAP_NAME_IN_USE:
case BOOTSTRAP_UNKNOWN_SERVICE:
case BOOTSTRAP_SERVICE_ACTIVE:
case BOOTSTRAP_BAD_COUNT:
case BOOTSTRAP_NO_MEMORY:
case BOOTSTRAP_NO_CHILDREN:
// Show known bootstrap errors in decimal because that's how they're
// defined in <servers/bootstrap.h>.
return base::StringPrintf("%s%s (%d)",
FormatBase(base).c_str(),
bootstrap_strerror(bootstrap_err),
bootstrap_err);
default:
return MachErrorMessage(bootstrap_err, base);
}
}
} // namespace test
} // namespace crashpad

View File

@ -22,9 +22,9 @@
namespace crashpad {
namespace test {
// These functions format messages in a similar way to the logging macros in
// base/mac/mach_logging.h. They exist to interoperate with gtest assertions,
// which dont interoperate with logging but can be streamed to.
// This function formats messages in a similar way to the Mach error logging
// macros in base/mac/mach_logging.h. It exists to interoperate with gtest
// assertions, which dont interoperate with logging but can be streamed to.
//
// Where non-test code could do:
// MACH_CHECK(kr == KERN_SUCCESS, kr) << "vm_deallocate";
@ -47,23 +47,6 @@ namespace test {
std::string MachErrorMessage(mach_error_t mach_err,
const std::string& base = std::string());
//! \brief Formats a bootstrap error message.
//!
//! The returned string will combine the \a base string, if supplied, with a
//! textual and numeric description of the error.
//!
//! \param[in] bootstrap_err The bootstrap error code.
//! \param[in] base A string to prepend to the error description.
//!
//! \return A string of the format `"Permission denied (1100)"` if \a
//! bootstrap_err has the value `BOOTSTRAP_NOT_PRIVILEGED` on a system where
//! this is defined to be 1100. If \a base is not empty, it will be
//! prepended to this string, separated by a colon. If \a bootstrap_err is
//! not a valid bootstrap error code, it will be interpreted as a Mach error
//! code in the manner of MachErrorMessage().
std::string BootstrapErrorMessage(kern_return_t bootstrap_err,
const std::string& base = std::string());
} // namespace test
} // namespace crashpad

View File

@ -27,6 +27,7 @@
#include "test/errors.h"
#include "test/mac/mach_errors.h"
#include "util/file/file_io.h"
#include "util/mach/bootstrap.h"
#include "util/mach/mach_extensions.h"
#include "util/mach/mach_message.h"
#include "util/misc/implicit_cast.h"

View File

@ -28,6 +28,7 @@
#include "base/logging.h"
#include "base/mac/mach_logging.h"
#include "tools/tool_support.h"
#include "util/mach/bootstrap.h"
#include "util/mach/exc_server_variants.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/exception_types.h"

View File

@ -30,6 +30,7 @@
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "tools/tool_support.h"
#include "util/mach/bootstrap.h"
#include "util/mach/exception_ports.h"
#include "util/mach/mach_extensions.h"
#include "util/mach/symbolic_constants_mach.h"

View File

@ -248,6 +248,8 @@ static_library("util") {
sources += [
"mac/xattr.cc",
"mac/xattr.h",
"mach/mach_extensions.cc",
"mach/mach_extensions.h",
"misc/clock_mac.cc",
"misc/paths_mac.cc",
"synchronization/semaphore_mac.cc",
@ -263,6 +265,8 @@ static_library("util") {
"mac/mac_util.h",
"mac/service_management.cc",
"mac/service_management.h",
"mach/bootstrap.cc",
"mach/bootstrap.h",
"mach/child_port_handshake.cc",
"mach/child_port_handshake.h",
"mach/child_port_server.cc",
@ -280,8 +284,6 @@ static_library("util") {
"mach/exception_ports.h",
"mach/exception_types.cc",
"mach/exception_types.h",
"mach/mach_extensions.cc",
"mach/mach_extensions.h",
"mach/mach_message.cc",
"mach/mach_message.h",
"mach/mach_message_server.cc",
@ -659,7 +661,10 @@ source_set("util_test") {
}
if (crashpad_is_mac || crashpad_is_ios) {
sources += [ "mac/xattr_test.cc" ]
sources += [
"mac/xattr_test.cc",
"mach/mach_extensions_test.cc",
]
}
if (crashpad_is_mac) {
@ -667,6 +672,7 @@ source_set("util_test") {
"mac/launchd_test.mm",
"mac/mac_util_test.mm",
"mac/service_management_test.mm",
"mach/bootstrap_test.cc",
"mach/child_port_handshake_test.cc",
"mach/child_port_server_test.cc",
"mach/composite_mach_message_server_test.cc",
@ -675,7 +681,6 @@ source_set("util_test") {
"mach/exception_behaviors_test.cc",
"mach/exception_ports_test.cc",
"mach/exception_types_test.cc",
"mach/mach_extensions_test.cc",
"mach/mach_message_server_test.cc",
"mach/mach_message_test.cc",
"mach/notify_server_test.cc",

109
util/mach/bootstrap.cc Normal file
View File

@ -0,0 +1,109 @@
// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/mach/bootstrap.h"
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include "base/mac/mach_logging.h"
namespace {
// This forms the internal implementation for BootstrapCheckIn() and
// BootstrapLookUp(), which follow the same logic aside from the routine called
// and the right type returned.
struct BootstrapCheckInTraits {
using Type = base::mac::ScopedMachReceiveRight;
static kern_return_t Call(mach_port_t bootstrap_port,
const char* service_name,
mach_port_t* service_port) {
return bootstrap_check_in(bootstrap_port, service_name, service_port);
}
static constexpr char kName[] = "bootstrap_check_in";
};
constexpr char BootstrapCheckInTraits::kName[];
struct BootstrapLookUpTraits {
using Type = base::mac::ScopedMachSendRight;
static kern_return_t Call(mach_port_t bootstrap_port,
const char* service_name,
mach_port_t* service_port) {
return bootstrap_look_up(bootstrap_port, service_name, service_port);
}
static constexpr char kName[] = "bootstrap_look_up";
};
constexpr char BootstrapLookUpTraits::kName[];
template <typename Traits>
typename Traits::Type BootstrapCheckInOrLookUp(
const std::string& service_name) {
// bootstrap_check_in() and bootstrap_look_up() silently truncate service
// names longer than BOOTSTRAP_MAX_NAME_LEN. This check ensures that the name
// will not be truncated.
if (service_name.size() >= BOOTSTRAP_MAX_NAME_LEN) {
LOG(ERROR) << Traits::kName << " " << service_name << ": name too long";
return typename Traits::Type(MACH_PORT_NULL);
}
mach_port_t service_port;
kern_return_t kr =
Traits::Call(bootstrap_port, service_name.c_str(), &service_port);
if (kr != BOOTSTRAP_SUCCESS) {
BOOTSTRAP_LOG(ERROR, kr) << Traits::kName << " " << service_name;
service_port = MACH_PORT_NULL;
}
return typename Traits::Type(service_port);
}
} // namespace
namespace crashpad {
base::mac::ScopedMachReceiveRight BootstrapCheckIn(
const std::string& service_name) {
return BootstrapCheckInOrLookUp<BootstrapCheckInTraits>(service_name);
}
base::mac::ScopedMachSendRight BootstrapLookUp(
const std::string& service_name) {
base::mac::ScopedMachSendRight send(
BootstrapCheckInOrLookUp<BootstrapLookUpTraits>(service_name));
// Its possible to race the bootstrap server when the receive right
// corresponding to the looked-up send right is destroyed immediately before
// the bootstrap_look_up() call. If the bootstrap server believes that
// |service_name| is still registered before processing the port-destroyed
// notification sent to it by the kernel, it will respond to a
// bootstrap_look_up() request with a send right that has become a dead name,
// which will be returned to the bootstrap_look_up() caller, translated into
// the callers IPC port name space, as the special MACH_PORT_DEAD port name.
// Check for that and return MACH_PORT_NULL in its place, as though the
// bootstrap server had fully processed the port-destroyed notification before
// responding to bootstrap_look_up().
if (send.get() == MACH_PORT_DEAD) {
LOG(ERROR) << "bootstrap_look_up " << service_name << ": service is dead";
send.reset();
}
return send;
}
base::mac::ScopedMachSendRight SystemCrashReporterHandler() {
return BootstrapLookUp("com.apple.ReportCrash");
}
} // namespace crashpad

63
util/mach/bootstrap.h Normal file
View File

@ -0,0 +1,63 @@
// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CRASHPAD_UTIL_MACH_BOOTSTRAP_H_
#define CRASHPAD_UTIL_MACH_BOOTSTRAP_H_
#include <string>
#include "base/mac/scoped_mach_port.h"
namespace crashpad {
//! \brief Makes a `boostrap_check_in()` call to the process bootstrap server.
//!
//! This function is provided to make it easier to call `bootstrap_check_in()`
//! while avoiding accidental leaks of the returned receive right.
//!
//! \param[in] service_name The service name to check in.
//!
//! \return On success, a receive right to the service port. On failure,
//! `MACH_PORT_NULL` with a message logged.
base::mac::ScopedMachReceiveRight BootstrapCheckIn(
const std::string& service_name);
//! \brief Makes a `boostrap_look_up()` call to the process bootstrap server.
//!
//! This function is provided to make it easier to call `bootstrap_look_up()`
//! while avoiding accidental leaks of the returned send right.
//!
//! \param[in] service_name The service name to look up.
//!
//! \return On success, a send right to the service port. On failure,
//! `MACH_PORT_NULL` with a message logged.
base::mac::ScopedMachSendRight BootstrapLookUp(const std::string& service_name);
//! \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
#endif // CRASHPAD_UTIL_MACH_BOOTSTRAP_H_

View File

@ -0,0 +1,70 @@
// Copyright 2020 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/mach/bootstrap.h"
#include "base/mac/scoped_mach_port.h"
#include "gtest/gtest.h"
#include "util/mach/mach_extensions.h"
#include "util/misc/random_string.h"
namespace crashpad {
namespace test {
namespace {
TEST(Bootstrap, BootstrapCheckInAndLookUp) {
// This should always exist.
base::mac::ScopedMachSendRight report_crash(
BootstrapLookUp("com.apple.ReportCrash"));
EXPECT_NE(report_crash, kMachPortNull);
std::string service_name = "org.chromium.crashpad.test.bootstrap_check_in.";
service_name.append(RandomString());
{
// The new service hasnt checked in yet, so this should fail.
base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
EXPECT_EQ(send, kMachPortNull);
// Check it in.
base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
EXPECT_NE(receive, kMachPortNull);
// Now it should be possible to look up the new service.
send = BootstrapLookUp(service_name);
EXPECT_NE(send, kMachPortNull);
// It shouldnt be possible to check the service in while its active.
base::mac::ScopedMachReceiveRight receive_2(BootstrapCheckIn(service_name));
EXPECT_EQ(receive_2, kMachPortNull);
}
// The new service should be gone now.
base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
EXPECT_EQ(send, kMachPortNull);
// It should be possible to check it in again.
base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
EXPECT_NE(receive, kMachPortNull);
}
TEST(Bootstrap, SystemCrashReporterHandler) {
base::mac::ScopedMachSendRight system_crash_reporter_handler(
SystemCrashReporterHandler());
EXPECT_TRUE(system_crash_reporter_handler.is_valid());
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -34,6 +34,7 @@
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "util/file/file_io.h"
#include "util/mach/bootstrap.h"
#include "util/mach/child_port.h"
#include "util/mach/child_port_server.h"
#include "util/mach/mach_extensions.h"

View File

@ -14,66 +14,14 @@
#include "util/mach/mach_extensions.h"
#include <Availability.h>
#include <AvailabilityMacros.h>
#include <pthread.h>
#include <servers/bootstrap.h>
#include "base/mac/mach_logging.h"
#include "build/build_config.h"
#include "util/mac/mac_util.h"
namespace {
// This forms the internal implementation for BootstrapCheckIn() and
// BootstrapLookUp(), which follow the same logic aside from the routine called
// and the right type returned.
struct BootstrapCheckInTraits {
using Type = base::mac::ScopedMachReceiveRight;
static kern_return_t Call(mach_port_t bootstrap_port,
const char* service_name,
mach_port_t* service_port) {
return bootstrap_check_in(bootstrap_port, service_name, service_port);
}
static constexpr char kName[] = "bootstrap_check_in";
};
constexpr char BootstrapCheckInTraits::kName[];
struct BootstrapLookUpTraits {
using Type = base::mac::ScopedMachSendRight;
static kern_return_t Call(mach_port_t bootstrap_port,
const char* service_name,
mach_port_t* service_port) {
return bootstrap_look_up(bootstrap_port, service_name, service_port);
}
static constexpr char kName[] = "bootstrap_look_up";
};
constexpr char BootstrapLookUpTraits::kName[];
template <typename Traits>
typename Traits::Type BootstrapCheckInOrLookUp(
const std::string& service_name) {
// bootstrap_check_in() and bootstrap_look_up() silently truncate service
// names longer than BOOTSTRAP_MAX_NAME_LEN. This check ensures that the name
// will not be truncated.
if (service_name.size() >= BOOTSTRAP_MAX_NAME_LEN) {
LOG(ERROR) << Traits::kName << " " << service_name << ": name too long";
return typename Traits::Type(MACH_PORT_NULL);
}
mach_port_t service_port;
kern_return_t kr = Traits::Call(bootstrap_port,
service_name.c_str(),
&service_port);
if (kr != BOOTSTRAP_SUCCESS) {
BOOTSTRAP_LOG(ERROR, kr) << Traits::kName << " " << service_name;
service_port = MACH_PORT_NULL;
}
return typename Traits::Type(service_port);
}
} // namespace
namespace crashpad {
thread_t MachThreadSelf() {
@ -97,7 +45,12 @@ exception_mask_t ExcMaskAll() {
// 10.9.4 xnu-2422.110.17/osfmk/mach/ipc_host.c and
// xnu-2422.110.17/osfmk/mach/ipc_tt.c.
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
#if defined(OS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_7_0
// iOS 7 ≅ macOS 10.9.
#error This code was not ported to iOS versions older than 7
#endif
#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
const int mac_os_x_minor_version = MacOSXMinorVersion();
#endif
@ -114,7 +67,7 @@ exception_mask_t ExcMaskAll() {
EXC_MASK_MACH_SYSCALL |
EXC_MASK_RPC_ALERT |
EXC_MASK_MACHINE;
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8
if (mac_os_x_minor_version < 8) {
return kExcMaskAll_10_6;
}
@ -124,7 +77,7 @@ exception_mask_t ExcMaskAll() {
// xnu-2050.48.11/osfmk/mach/exception_types.h.
constexpr exception_mask_t kExcMaskAll_10_8 =
kExcMaskAll_10_6 | EXC_MASK_RESOURCE;
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_9
if (mac_os_x_minor_version < 9) {
return kExcMaskAll_10_8;
}
@ -139,7 +92,12 @@ exception_mask_t ExcMaskAll() {
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 defined(OS_IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0
// iOS 9 ≅ macOS 10.11.
#error This code was not ported to iOS versions older than 9
#endif
#if !defined(OS_IOS) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
if (MacOSXMinorVersion() < 11) {
return kExcMaskValid_10_6;
}
@ -151,37 +109,4 @@ exception_mask_t ExcMaskValid() {
return kExcMaskValid_10_11;
}
base::mac::ScopedMachReceiveRight BootstrapCheckIn(
const std::string& service_name) {
return BootstrapCheckInOrLookUp<BootstrapCheckInTraits>(service_name);
}
base::mac::ScopedMachSendRight BootstrapLookUp(
const std::string& service_name) {
base::mac::ScopedMachSendRight send(
BootstrapCheckInOrLookUp<BootstrapLookUpTraits>(service_name));
// Its possible to race the bootstrap server when the receive right
// corresponding to the looked-up send right is destroyed immediately before
// the bootstrap_look_up() call. If the bootstrap server believes that
// |service_name| is still registered before processing the port-destroyed
// notification sent to it by the kernel, it will respond to a
// bootstrap_look_up() request with a send right that has become a dead name,
// which will be returned to the bootstrap_look_up() caller, translated into
// the callers IPC port name space, as the special MACH_PORT_DEAD port name.
// Check for that and return MACH_PORT_NULL in its place, as though the
// bootstrap server had fully processed the port-destroyed notification before
// responding to bootstrap_look_up().
if (send.get() == MACH_PORT_DEAD) {
LOG(ERROR) << "bootstrap_look_up " << service_name << ": service is dead";
send.reset();
}
return send;
}
base::mac::ScopedMachSendRight SystemCrashReporterHandler() {
return BootstrapLookUp("com.apple.ReportCrash");
}
} // namespace crashpad

View File

@ -17,10 +17,6 @@
#include <mach/mach.h>
#include <string>
#include "base/mac/scoped_mach_port.h"
namespace crashpad {
//! \brief `MACH_PORT_NULL` with the correct type for a Mach port,
@ -119,43 +115,6 @@ exception_mask_t ExcMaskAll();
//! support is present.
exception_mask_t ExcMaskValid();
//! \brief Makes a `boostrap_check_in()` call to the process bootstrap server.
//!
//! This function is provided to make it easier to call `bootstrap_check_in()`
//! while avoiding accidental leaks of the returned receive right.
//!
//! \param[in] service_name The service name to check in.
//!
//! \return On success, a receive right to the service port. On failure,
//! `MACH_PORT_NULL` with a message logged.
base::mac::ScopedMachReceiveRight BootstrapCheckIn(
const std::string& service_name);
//! \brief Makes a `boostrap_look_up()` call to the process bootstrap server.
//!
//! This function is provided to make it easier to call `bootstrap_look_up()`
//! while avoiding accidental leaks of the returned send right.
//!
//! \param[in] service_name The service name to look up.
//!
//! \return On success, a send right to the service port. On failure,
//! `MACH_PORT_NULL` with a message logged.
base::mac::ScopedMachSendRight BootstrapLookUp(const std::string& service_name);
//! \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
#endif // CRASHPAD_UTIL_MACH_MACH_EXTENSIONS_H_

View File

@ -15,10 +15,10 @@
#include "util/mach/mach_extensions.h"
#include "base/mac/scoped_mach_port.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/mac/mach_errors.h"
#include "util/mac/mac_util.h"
#include "util/misc/random_string.h"
namespace crashpad {
namespace test {
@ -80,6 +80,11 @@ TEST(MachExtensions, ExcMaskAll) {
EXPECT_FALSE(exc_mask_all & EXC_MASK_CRASH);
EXPECT_FALSE(exc_mask_all & EXC_MASK_CORPSE_NOTIFY);
#if defined(OS_IOS)
// Assume at least iOS 7 (≅ macOS 10.9).
EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);
EXPECT_TRUE(exc_mask_all & EXC_MASK_GUARD);
#else // OS_IOS
const int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version >= 8) {
EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);
@ -92,6 +97,7 @@ TEST(MachExtensions, ExcMaskAll) {
} else {
EXPECT_FALSE(exc_mask_all & EXC_MASK_GUARD);
}
#endif // OS_IOS
// Bit 0 should not be set.
EXPECT_FALSE(ExcMaskAll() & 1);
@ -106,6 +112,12 @@ TEST(MachExtensions, ExcMaskValid) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_CRASH);
#if defined(OS_IOS)
// Assume at least iOS 9 (≅ macOS 10.11).
EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);
EXPECT_TRUE(exc_mask_valid & EXC_MASK_GUARD);
EXPECT_TRUE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
#else // OS_IOS
const int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version >= 8) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);
@ -124,6 +136,7 @@ TEST(MachExtensions, ExcMaskValid) {
} else {
EXPECT_FALSE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
}
#endif // OS_IOS
// Bit 0 should not be set.
EXPECT_FALSE(ExcMaskValid() & 1);
@ -132,48 +145,6 @@ TEST(MachExtensions, ExcMaskValid) {
EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll());
}
TEST(MachExtensions, BootstrapCheckInAndLookUp) {
// This should always exist.
base::mac::ScopedMachSendRight
report_crash(BootstrapLookUp("com.apple.ReportCrash"));
EXPECT_NE(report_crash, kMachPortNull);
std::string service_name = "org.chromium.crashpad.test.bootstrap_check_in.";
service_name.append(RandomString());
{
// The new service hasnt checked in yet, so this should fail.
base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
EXPECT_EQ(send, kMachPortNull);
// Check it in.
base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
EXPECT_NE(receive, kMachPortNull);
// Now it should be possible to look up the new service.
send = BootstrapLookUp(service_name);
EXPECT_NE(send, kMachPortNull);
// It shouldnt be possible to check the service in while its active.
base::mac::ScopedMachReceiveRight receive_2(BootstrapCheckIn(service_name));
EXPECT_EQ(receive_2, kMachPortNull);
}
// The new service should be gone now.
base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
EXPECT_EQ(send, kMachPortNull);
// It should be possible to check it in again.
base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
EXPECT_NE(receive, kMachPortNull);
}
TEST(MachExtensions, SystemCrashReporterHandler) {
base::mac::ScopedMachSendRight
system_crash_reporter_handler(SystemCrashReporterHandler());
EXPECT_TRUE(system_crash_reporter_handler.is_valid());
}
} // namespace
} // namespace test
} // namespace crashpad