mac: Replace MacOSXMinorVersion with MacOSVersionNumber

MacOSXMinorVersion reported just the “y” value for an OS version 10.y.z.
This is no longer sufficient to identify OS versions accurately in macOS
11. A new MacOSVersionNumber function reports the full OS version as
“xxyyzz” for an OS version x.y.z. This is the same format used by
<Availability.h> __MAC_* macros since 10.10.

MacOSXVersion is also renamed to MacOSVersionComponents for
disambiguation and proper modern nomenclature.

Bug: crashpad:347
Test: crashpad_snapshot_test SystemSnapshotMacTest.OSVersion, crashpad_util_test MacUtil.MacOSVersionNumber
Change-Id: I66421954f021c0627095474cb26359970fcd9101
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2386386
Commit-Queue: Mark Mentovai <mark@chromium.org>
Reviewed-by: Robert Sesek <rsesek@chromium.org>
This commit is contained in:
Mark Mentovai 2020-09-01 17:09:37 -04:00 committed by Commit Bot
parent 3965bc7d78
commit 5412beb633
24 changed files with 341 additions and 137 deletions

View File

@ -14,6 +14,7 @@
#include "client/crashpad_client.h"
#include <Availability.h>
#include <errno.h>
#include <mach/mach.h>
#include <pthread.h>
@ -454,14 +455,15 @@ bool CrashpadClient::StartHandler(
// The “restartable” behavior can only be selected on OS X 10.10 and later. In
// previous OS versions, if the initial client were to crash while attempting
// to restart the handler, it would become an unkillable process.
base::mac::ScopedMachSendRight exception_port(
HandlerStarter::InitialStart(handler,
database,
metrics_dir,
url,
annotations,
arguments,
restartable && MacOSXMinorVersion() >= 10));
base::mac::ScopedMachSendRight exception_port(HandlerStarter::InitialStart(
handler,
database,
metrics_dir,
url,
annotations,
arguments,
restartable && (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||
MacOSVersionNumber() >= 10'10'00)));
if (!exception_port.is_valid()) {
return false;
}

View File

@ -16,6 +16,7 @@
#include <limits>
#include "base/check_op.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
@ -30,7 +31,7 @@
#include "util/numeric/in_range_cast.h"
#include "util/numeric/safe_assignment.h"
#if defined(OS_APPLE)
#if defined(OS_MAC)
#include <Availability.h>
#elif defined(OS_ANDROID)
#include <android/api-level.h>
@ -69,24 +70,31 @@ std::string BuildString(const SystemSnapshot* system_snapshot) {
#if defined(OS_MAC)
// Converts the value of the __MAC_OS_X_VERSION_MIN_REQUIRED or
// __MAC_OS_X_VERSION_MAX_ALLOWED macro from <Availability.h> to a number
// identifying the minor macOS version that it represents. For example, with an
// argument of __MAC_10_6, this function will return 6.
int AvailabilityVersionToMacOSXMinorVersion(int availability) {
// Through __MAC_10_9, the minor version is the tens digit.
if (availability >= 1000 && availability <= 1099) {
return (availability / 10) % 10;
}
// identifying the macOS version that it represents, in the same format used by
// MacOSVersionNumber(). For example, with an argument of __MAC_10_15, this
// function will return 10'15'00, which is incidentally the same as __MAC_10_15.
// With an argument of __MAC_10_9, this function will return 10'09'00, different
// from __MAC_10_9, which is 10'9'0.
int AvailabilityVersionToMacOSVersionNumber(int availability) {
#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_10
DCHECK_GE(availability, 10'0'0);
// After __MAC_10_9, the older format was insufficient to represent versions.
// Since then, the minor version is the thousands and hundreds digits.
if (availability >= 100000 && availability <= 109999) {
return (availability / 100) % 100;
// Until __MAC_10_10, the format is major * 1'0'0 + minor * 1'0 + bugfix.
if (availability >= 10'0'0 && availability <= 10'9'9) {
int minor = (availability / 1'0) % 1'0;
int bugfix = availability % 1'0;
return 10'00'00 + minor * 1'00 + bugfix;
}
return 0;
}
#endif
// Since __MAC_10_10, the format is major * 1'00'00 + minor * 1'00 + bugfix.
DCHECK_GE(availability, 10'10'00);
DCHECK_LE(availability, 99'99'99);
return availability;
}
#endif // OS_MAC
} // namespace
namespace internal {
@ -99,8 +107,10 @@ std::string MinidumpMiscInfoDebugBuildString() {
// Caution: the minidump file format only has room for 39 UTF-16 code units
// plus a UTF-16 NUL terminator. Dont let strings get longer than this, or
// they will be truncated and a message will be logged.
#if defined(OS_APPLE)
#if defined(OS_MAC)
static constexpr char kOS[] = "mac";
#elif defined(OS_IOS)
static constexpr char kOS[] = "ios";
#elif defined(OS_ANDROID)
static constexpr char kOS[] = "android";
#elif defined(OS_LINUX) || defined(OS_CHROMEOS)
@ -138,8 +148,8 @@ std::string MinidumpMiscInfoDebugBuildString() {
#if defined(OS_MAC)
debug_build_string += base::StringPrintf(
",%d,%d",
AvailabilityVersionToMacOSXMinorVersion(__MAC_OS_X_VERSION_MIN_REQUIRED),
AvailabilityVersionToMacOSXMinorVersion(__MAC_OS_X_VERSION_MAX_ALLOWED));
AvailabilityVersionToMacOSVersionNumber(__MAC_OS_X_VERSION_MIN_REQUIRED),
AvailabilityVersionToMacOSVersionNumber(__MAC_OS_X_VERSION_MAX_ALLOWED));
#elif defined(OS_ANDROID)
debug_build_string += base::StringPrintf(",%d", __ANDROID_API__);
#endif

View File

@ -124,7 +124,8 @@ class TestMachOImageAnnotationsReader final
// _dyld_fatal_error. This changed in 10.12 to use
// abort_with_payload(), which appears as SIGABRT to a waiting parent.
SetExpectedChildTermination(
kTerminationSignal, MacOSXMinorVersion() < 12 ? SIGTRAP : SIGABRT);
kTerminationSignal,
MacOSVersionNumber() < 10'12'00 ? SIGTRAP : SIGABRT);
break;
}
}
@ -186,8 +187,8 @@ class TestMachOImageAnnotationsReader final
// Mac OS X 10.6 doesnt have support for CrashReporter annotations
// (CrashReporterClient.h), so dont look for any special annotations in
// that version.
int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version > 7) {
const int macos_version_number = MacOSVersionNumber();
if (macos_version_number > 10'07'00) {
EXPECT_GE(all_annotations_vector.size(), 1u);
std::string expected_annotation;
@ -204,7 +205,7 @@ class TestMachOImageAnnotationsReader final
// exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c
// _libc_fork_child().
expected_annotation =
mac_os_x_minor_version <= 8
macos_version_number <= 10'08'00
? "abort() called"
: "crashed on child side of fork pre-exec";
break;

View File

@ -14,6 +14,7 @@
#include "snapshot/mac/mach_o_image_segment_reader.h"
#include <Availability.h>
#include <mach-o/loader.h>
#include <string.h>
@ -44,7 +45,8 @@ bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,
}
if (module_name == "cl_kernels") {
if (MacOSXMinorVersion() >= 10) {
if (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||
MacOSVersionNumber() >= 10'10'00) {
if (has_timestamp) {
*has_timestamp = false;
}
@ -57,7 +59,8 @@ bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type,
"/private/var/db/CVMS/cvmsCodeSignObj";
if (module_name.compare(
0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 &&
MacOSXMinorVersion() >= 14) {
(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14 ||
MacOSVersionNumber() >= 10'14'00)) {
if (has_timestamp) {
*has_timestamp = true;
}

View File

@ -712,11 +712,8 @@ class ScopedOpenCLNoOpKernel {
// OpenCL kernels that run on the CPU do not result in cl_kernels images
// appearing on that OS version.
bool ExpectCLKernels() {
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_7
return true;
#else
return MacOSXMinorVersion() >= 7;
#endif
return __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_7 ||
MacOSVersionNumber() >= 10'07'00;
}
TEST(ProcessReaderMac, SelfModules) {

View File

@ -22,6 +22,7 @@
#include <limits>
#include <type_traits>
#include "base/check_op.h"
#include "base/logging.h"
#include "base/numerics/safe_math.h"
#include "base/stl_util.h"
@ -161,13 +162,13 @@ size_t dyld_all_image_infos<Traits>::ExpectedSizeForVersion(
// The revised one in macOS 10.13 grew. Its safe to assume that the
// dyld_all_image_infos structure came from the same system thats now
// interpreting it, so use an OS version check.
int mac_os_x_minor_version = MacOSXMinorVersion();
if (mac_os_x_minor_version == 12) {
const int macos_version_number = MacOSVersionNumber();
if (macos_version_number / 1'00 == 10'12) {
return offsetof(dyld_all_image_infos<Traits>, end_v14);
}
DCHECK_GE(mac_os_x_minor_version, 13);
DCHECK_LE(mac_os_x_minor_version, 14);
DCHECK_GE(macos_version_number, 10'13'00);
DCHECK_LT(macos_version_number, 10'15'00);
return offsetof(dyld_all_image_infos<Traits>, platform);
}

View File

@ -48,17 +48,17 @@ namespace {
TEST(ProcessTypes, DyldImagesSelf) {
// Get the in-process view of dyld_all_image_infos, and check it for sanity.
const dyld_all_image_infos* self_image_infos = DyldGetAllImageInfos();
const int mac_os_x_minor_version = MacOSXMinorVersion();
const int macos_version_number = MacOSVersionNumber();
if (mac_os_x_minor_version >= 15) {
if (macos_version_number >= 10'15'00) {
EXPECT_GE(self_image_infos->version, 16u);
} else if (mac_os_x_minor_version >= 12) {
} else if (macos_version_number >= 10'12'00) {
EXPECT_GE(self_image_infos->version, 15u);
} else if (mac_os_x_minor_version >= 9) {
} else if (macos_version_number >= 10'09'00) {
EXPECT_GE(self_image_infos->version, 13u);
} else if (mac_os_x_minor_version >= 7) {
} else if (macos_version_number >= 10'07'00) {
EXPECT_GE(self_image_infos->version, 8u);
} else if (mac_os_x_minor_version >= 6) {
} else if (macos_version_number >= 10'06'00) {
EXPECT_GE(self_image_infos->version, 2u);
} else {
EXPECT_GE(self_image_infos->version, 1u);
@ -91,7 +91,7 @@ TEST(ProcessTypes, DyldImagesSelf) {
// This field is only present in the OS X 10.7 SDK (at build time) and kernel
// (at run time).
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7
if (MacOSXMinorVersion() >= 7) {
if (macos_version_number >= 10'07'00) {
#if !defined(ARCH_CPU_64_BITS)
EXPECT_EQ(dyld_info.all_image_info_format, TASK_DYLD_ALL_IMAGE_INFO_32);
#else
@ -130,11 +130,11 @@ TEST(ProcessTypes, DyldImagesSelf) {
bool test_expected_size_for_version_matches_sdk_sizeof;
#if __MAC_OS_X_VERSION_MAX_ALLOWED == __MAC_10_12
test_expected_size_for_version_matches_sdk_sizeof =
mac_os_x_minor_version == 12;
macos_version_number / 1'00 == 10'12;
#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_13 && \
__MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_15
test_expected_size_for_version_matches_sdk_sizeof =
mac_os_x_minor_version >= 13 && mac_os_x_minor_version <= 14;
macos_version_number >= 10'13'00 && macos_version_number < 10'15'00;
#else
test_expected_size_for_version_matches_sdk_sizeof = true;
#endif
@ -177,7 +177,7 @@ TEST(ProcessTypes, DyldImagesSelf) {
SCOPED_TRACE(base::StringPrintf("index %zu, version %u", index, version));
if (version == 15) {
if (mac_os_x_minor_version == 12) {
if (macos_version_number / 1'00 == 10'12) {
EXPECT_EQ(process_types::internal::dyld_all_image_infos<
process_types::internal::Traits32>::
ExpectedSizeForVersion(version),
@ -186,7 +186,8 @@ TEST(ProcessTypes, DyldImagesSelf) {
process_types::internal::Traits64>::
ExpectedSizeForVersion(version),
304u);
} else if (mac_os_x_minor_version >= 13 && mac_os_x_minor_version <= 14) {
} else if (macos_version_number >= 10'13'00 &&
macos_version_number < 10'15'00) {
EXPECT_EQ(process_types::internal::dyld_all_image_infos<
process_types::internal::Traits32>::
ExpectedSizeForVersion(version),
@ -362,7 +363,7 @@ TEST(ProcessTypes, DyldImagesSelf) {
#endif
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_13
if (proctype_image_infos.version >= 15 && mac_os_x_minor_version >= 13) {
if (proctype_image_infos.version >= 15 && macos_version_number >= 10'13'00) {
EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_addr,
self_image_infos->compact_dyld_image_info_addr);
EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_size,

View File

@ -31,6 +31,7 @@
#include "snapshot/mac/process_reader_mac.h"
#include "snapshot/posix/timezone.h"
#include "util/mac/mac_util.h"
#include "util/mac/sysctl.h"
#include "util/numeric/in_range_cast.h"
namespace crashpad {
@ -60,26 +61,6 @@ T CastIntSysctlByName(const char* name, T default_value) {
return InRangeCast<T>(int_value, default_value);
}
std::string ReadStringSysctlByName(const char* name) {
size_t buf_len;
if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) {
PLOG(WARNING) << "sysctlbyname (size) " << name;
return std::string();
}
if (buf_len == 0) {
return std::string();
}
std::string value(buf_len - 1, '\0');
if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) {
PLOG(WARNING) << "sysctlbyname " << name;
return std::string();
}
return value;
}
#if defined(ARCH_CPU_X86_FAMILY)
void CallCPUID(uint32_t leaf,
uint32_t* eax,
@ -119,15 +100,15 @@ void SystemSnapshotMac::Initialize(ProcessReaderMac* process_reader,
process_reader_ = process_reader;
snapshot_time_ = snapshot_time;
// MacOSXVersion() logs its own warnings if it cant figure anything out. Its
// not fatal if this happens. The default values are reasonable.
// MacOSVersionComponents() logs its own warnings if it cant figure anything
// out. Its not fatal if this happens. The default values are reasonable.
std::string os_version_string;
MacOSXVersion(&os_version_major_,
&os_version_minor_,
&os_version_bugfix_,
&os_version_build_,
&os_server_,
&os_version_string);
MacOSVersionComponents(&os_version_major_,
&os_version_minor_,
&os_version_bugfix_,
&os_version_build_,
&os_server_,
&os_version_string);
std::string uname_string;
utsname uts;
@ -194,9 +175,9 @@ std::string SystemSnapshotMac::CPUVendor() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return ReadStringSysctlByName("machdep.cpu.vendor");
return ReadStringSysctlByName("machdep.cpu.vendor", true);
#elif defined(ARCH_CPU_ARM64)
return ReadStringSysctlByName("machdep.cpu.brand_string");
return ReadStringSysctlByName("machdep.cpu.brand_string", true);
#else
#error port to your architecture
#endif
@ -368,7 +349,7 @@ bool SystemSnapshotMac::NXEnabled() const {
const bool nx_always_enabled = true;
#else // DT >= 10.14
base::ScopedClearLastError reset_errno;
const bool nx_always_enabled = MacOSXMinorVersion() >= 14;
const bool nx_always_enabled = MacOSVersionNumber() >= 10'14'00;
#endif // DT >= 10.14
if (nx_always_enabled) {
return true;

View File

@ -117,8 +117,10 @@ TEST_F(SystemSnapshotMacTest, OSVersion) {
std::string build;
system_snapshot().OSVersion(&major, &minor, &bugfix, &build);
EXPECT_EQ(major, 10);
EXPECT_EQ(minor, MacOSXMinorVersion());
const int macos_version_number = MacOSVersionNumber();
EXPECT_EQ(major * 1'00'00 + minor * 1'00 +
(macos_version_number >= 10'12'00 ? bugfix : 0),
macos_version_number);
EXPECT_FALSE(build.empty());
}

View File

@ -339,6 +339,8 @@ static_library("util") {
"mac/mac_util.h",
"mac/service_management.cc",
"mac/service_management.h",
"mac/sysctl.cc",
"mac/sysctl.h",
"mach/bootstrap.cc",
"mach/bootstrap.h",
"mach/child_port_handshake.cc",
@ -746,6 +748,7 @@ source_set("util_test") {
"mac/launchd_test.mm",
"mac/mac_util_test.mm",
"mac/service_management_test.mm",
"mac/sysctl_test.cc",
"mach/bootstrap_test.cc",
"mach/child_port_handshake_test.cc",
"mach/child_port_server_test.cc",

View File

@ -14,20 +14,24 @@
#include "util/mac/mac_util.h"
#include <Availability.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include "base/check_op.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_ioobject.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "util/mac/sysctl.h"
extern "C" {
// Private CoreFoundation internals. See 10.9.2 CF-855.14/CFPriv.h and
@ -56,9 +60,10 @@ extern const CFStringRef _kCFSystemVersionBuildVersionKey WEAK_IMPORT;
namespace {
#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12
// Returns the running systems Darwin major version. Dont call this, its an
// implementation detail and its result is meant to be cached by
// MacOSXMinorVersion().
// MacOSVersionNumber().
//
// This is very similar to Chromiums base/mac/mac_util.mm
// DarwinMajorVersionInternal().
@ -92,6 +97,7 @@ int DarwinMajorVersion() {
return darwin_major_version;
}
#endif // DT < 10.12
// Helpers for the weak-imported private CoreFoundation internals.
@ -118,9 +124,10 @@ const void* TryCFDictionaryGetValue(CFDictionaryRef dictionary,
}
// Converts |version| to a triplet of version numbers on behalf of
// MacOSXVersion(). Returns true on success. If |version| does not have the
// expected format, returns false. |version| must be in the form "10.9.2" or
// just "10.9". In the latter case, |bugfix| will be set to 0.
// MacOSVersionNumber() and MacOSVersionComponents(). Returns true on success.
// If |version| does not have the expected format, returns false. |version| must
// be in the form "10.9.2" or just "10.9". In the latter case, |bugfix| will be
// set to 0.
bool StringToVersionNumbers(const std::string& version,
int* major,
int* minor,
@ -180,20 +187,62 @@ std::string IORegistryEntryDataPropertyAsString(io_registry_entry_t entry,
namespace crashpad {
int MacOSXMinorVersion() {
// The Darwin major version is always 4 greater than the macOS minor version
// for Darwin versions beginning with 6, corresponding to Mac OS X 10.2.
static int mac_os_x_minor_version = DarwinMajorVersion() - 4;
DCHECK(mac_os_x_minor_version >= 2);
return mac_os_x_minor_version;
int MacOSVersionNumber() {
static int macos_version_number = []() {
// kern.osproductversion is a lightweight way to get the operating system
// version from the kernel without having to open any files or spin up any
// threads, but its only available in macOS 10.12 and later.
std::string macos_version_number_string =
ReadStringSysctlByName("kern.osproductversion",
__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_12);
if (!macos_version_number_string.empty()) {
int major;
int minor;
int bugfix;
if (StringToVersionNumbers(
macos_version_number_string, &major, &minor, &bugfix)) {
DCHECK_GE(major, 10);
DCHECK_LE(major, 99);
DCHECK_GE(minor, 0);
DCHECK_LE(minor, 99);
DCHECK_GE(bugfix, 0);
DCHECK_LE(bugfix, 99);
return major * 1'00'00 + minor * 1'00 + bugfix;
}
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_12
// On macOS 10.12 and later, the sysctlbyname above should have been
// successful.
NOTREACHED();
return -1;
#else // DT >= 10.12
// The Darwin major version is always 4 greater than the macOS minor version
// for Darwin versions beginning with 6, corresponding to Mac OS X 10.2,
// through Darwin 19, corresponding to macOS 10.15.
int darwin_major_version = DarwinMajorVersion();
DCHECK_GE(darwin_major_version, 6);
DCHECK_LE(darwin_major_version, 19);
int macos_version_number = 10'00'00 + (darwin_major_version - 4) * 1'00;
// On macOS 10.12 and later, the sysctlbyname above should have been
// successful.
DCHECK_LT(macos_version_number, 10'12'00);
return macos_version_number;
#endif // DT >= 10.12
}();
return macos_version_number;
}
bool MacOSXVersion(int* major,
int* minor,
int* bugfix,
std::string* build,
bool* server,
std::string* version_string) {
bool MacOSVersionComponents(int* major,
int* minor,
int* bugfix,
std::string* build,
bool* server,
std::string* version_string) {
base::ScopedCFTypeRef<CFDictionaryRef> dictionary(
TryCFCopyServerVersionDictionary());
if (dictionary) {
@ -217,7 +266,16 @@ bool MacOSXVersion(int* major,
success = false;
} else {
version = base::SysCFStringRefToUTF8(version_cf);
success &= StringToVersionNumbers(version, major, minor, bugfix);
if (!StringToVersionNumbers(version, major, minor, bugfix)) {
success = false;
} else {
DCHECK_GE(*major, 10);
DCHECK_LE(*major, 99);
DCHECK_GE(*minor, 0);
DCHECK_LE(*minor, 99);
DCHECK_GE(*bugfix, 0);
DCHECK_LE(*bugfix, 99);
}
}
CFStringRef build_cf = base::mac::CFCast<CFStringRef>(

View File

@ -21,14 +21,24 @@ namespace crashpad {
//! \brief Returns the version of the running operating system.
//!
//! \return The minor version of the operating system, such as `12` for macOS
//! 10.12.1.
//! \return The version of the operating system, such as `10'15'06` for macOS
//! 10.15.6.
//!
//! The format of the return value matches what is used by the <Availability.h>
//! `__MAC_OS_X_VERSION_MIN_REQUIRED`, `__MAC_OS_X_VERSION_MAX_ALLOWED`, and
//! per-version `__MAC_*` macros, for versions since OS X 10.10.
//!
//! On macOS 10.12 and later, this function will return the major, minor, and
//! bugfix components combined into a single number. On older OS versions, only
//! the major and minor components will be returned, and the bugfix component
//! will always be reported as 0. By contrast, MacOSVersionComponents() always
//! returns the bugfix component.
//!
//! \note This is similar to the base::mac::IsOS*() family of functions, but
//! is provided for situations where the caller needs to obtain version
//! information beyond what is provided by Chromiums base, or for when the
//! caller needs the actual minor version value.
int MacOSXMinorVersion();
int MacOSVersionNumber();
//! \brief Returns the version of the running operating system.
//!
@ -51,12 +61,12 @@ int MacOSXMinorVersion();
//! A failure is considered to have occurred if any element could not be
//! determined. When this happens, their values will be untouched, but other
//! values that could be determined will still be set properly.
bool MacOSXVersion(int* major,
int* minor,
int* bugfix,
std::string* build,
bool* server,
std::string* version_string);
bool MacOSVersionComponents(int* major,
int* minor,
int* bugfix,
std::string* build,
bool* server,
std::string* version_string);
//! \brief Returns the model name and board ID of the running system.
//!

View File

@ -71,15 +71,22 @@ void SwVers(NSString* argument, std::string* output) {
}
}
TEST(MacUtil, MacOSXVersion) {
TEST(MacUtil, MacOSVersionComponents) {
int major;
int minor;
int bugfix;
std::string build;
bool server;
std::string version_string;
ASSERT_TRUE(
MacOSXVersion(&major, &minor, &bugfix, &build, &server, &version_string));
ASSERT_TRUE(MacOSVersionComponents(
&major, &minor, &bugfix, &build, &server, &version_string));
EXPECT_GE(major, 10);
EXPECT_LE(major, 99);
EXPECT_GE(minor, 0);
EXPECT_LE(minor, 99);
EXPECT_GE(bugfix, 0);
EXPECT_LE(bugfix, 99);
std::string version;
if (bugfix) {
@ -108,20 +115,26 @@ TEST(MacUtil, MacOSXVersion) {
EXPECT_EQ(version_string.find(expected_product_name), 0u);
}
TEST(MacUtil, MacOSXMinorVersion) {
// Make sure that MacOSXMinorVersion() and MacOSXVersion() agree. The two have
// their own distinct implementations, and the latter was checked against
// sw_vers above.
TEST(MacUtil, MacOSVersionNumber) {
// Make sure that MacOSVersionNumber() and MacOSVersionComponents() agree. The
// two have their own distinct implementations, and the latter was checked
// against sw_vers above.
int macos_version_number = MacOSVersionNumber();
EXPECT_GE(macos_version_number, 10'00'00);
EXPECT_LE(macos_version_number, 99'99'99);
int major;
int minor;
int bugfix;
std::string build;
bool server;
std::string version_string;
ASSERT_TRUE(
MacOSXVersion(&major, &minor, &bugfix, &build, &server, &version_string));
ASSERT_TRUE(MacOSVersionComponents(
&major, &minor, &bugfix, &build, &server, &version_string));
EXPECT_EQ(MacOSXMinorVersion(), minor);
EXPECT_EQ(macos_version_number,
major * 1'00'00 + minor * 1'00 +
(macos_version_number >= 10'12'00 ? bugfix : 0));
}
TEST(MacUtil, MacModelAndBoard) {

47
util/mac/sysctl.cc Normal file
View File

@ -0,0 +1,47 @@
// 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/mac/sysctl.h"
#include <errno.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include "base/check_op.h"
#include "base/logging.h"
namespace crashpad {
std::string ReadStringSysctlByName(const char* name, bool may_log_enoent) {
size_t buf_len;
if (sysctlbyname(name, nullptr, &buf_len, nullptr, 0) != 0) {
PLOG_IF(WARNING, may_log_enoent || errno != ENOENT)
<< "sysctlbyname (size) " << name;
return std::string();
}
DCHECK_GE(buf_len, 1u);
std::string value(buf_len - 1, '\0');
if (sysctlbyname(name, &value[0], &buf_len, nullptr, 0) != 0) {
PLOG(WARNING) << "sysctlbyname " << name;
return std::string();
}
DCHECK_EQ(value[buf_len - 1], '\0');
return value;
}
} // namespace crashpad

36
util/mac/sysctl.h Normal file
View File

@ -0,0 +1,36 @@
// 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_MAC_SYSCTL_H_
#define CRASHPAD_UTIL_MAC_SYSCTL_H_
#include <string>
namespace crashpad {
//! \brief Calls `sysctlbyname` to read a string.
//!
//! \param[in] name The string name of the sysctl to raed.
//! \param[in] may_log_enoent If `true`, allows a warning to be logged if the
//! sysctl is not found, indicated by `sysctlbyname` setting `errno` to
//! `ENOENT`. If `false`, no warning will be logged if the sysctl is
//! missing, and an empty string will be returned silently.
//!
//! \return The value of the sysctl read on success. On failure, an empty string
//! with a warning logged, subject to \a may_log_enoent.
std::string ReadStringSysctlByName(const char* name, bool may_log_enoent);
} // namespace crashpad
#endif // CRASHPAD_UTIL_MAC_SYSCTL_H_

35
util/mac/sysctl_test.cc Normal file
View File

@ -0,0 +1,35 @@
// 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/mac/sysctl.h"
#include "gtest/gtest.h"
namespace crashpad {
namespace test {
namespace {
TEST(Sysctl, ReadStringSysctlByName) {
// kern.ostype is always provided by the kernel, and its a constant across
// all versions, so it makes for a good test.
EXPECT_EQ(ReadStringSysctlByName("kern.ostype", true), "Darwin");
// Names expected to not exist.
EXPECT_TRUE(ReadStringSysctlByName("kern.scheisskopf", true).empty());
EXPECT_TRUE(ReadStringSysctlByName("kern.sanders", false).empty());
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -683,7 +683,7 @@ kern_return_t ExcServerSuccessfulReturnValue(exception_type_t exception,
bool set_thread_state) {
if (exception == EXC_CRASH
#if defined(OS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_11
&& MacOSXMinorVersion() >= 11
&& MacOSVersionNumber() >= 10'11'00
#endif
) {
return KERN_SUCCESS;

View File

@ -1210,7 +1210,7 @@ TEST(ExcServerVariants, ExcServerSuccessfulReturnValue) {
const kern_return_t prefer_not_set_thread_state = KERN_SUCCESS;
#else
const kern_return_t prefer_not_set_thread_state =
MacOSXMinorVersion() < 11 ? MACH_RCV_PORT_DIED : KERN_SUCCESS;
MacOSVersionNumber() < 10'11'00 ? MACH_RCV_PORT_DIED : KERN_SUCCESS;
#endif
const struct {

View File

@ -259,7 +259,8 @@ bool IsExceptionNonfatalResource(exception_type_t exception,
// creation but can be made fatal by calling proc_rlimit_control() with
// RLIMIT_CPU_USAGE_MONITOR as the second argument and CPUMON_MAKE_FATAL set
// in the flags.
if (MacOSXMinorVersion() >= 10) {
if (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 ||
MacOSVersionNumber() >= 10'10'00) {
// In OS X 10.10, the exception code indicates whether the exception is
// fatal. See 10.10 xnu-2782.1.97/osfmk/kern/thread.c
// THIS_THREAD_IS_CONSUMING_TOO_MUCH_CPU__SENDING_EXC_RESOURCE().

View File

@ -269,7 +269,7 @@ TEST(ExceptionTypes, IsExceptionNonfatalResource) {
EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_CPU, FLAVOR_CPU_MONITOR);
EXPECT_TRUE(IsExceptionNonfatalResource(EXC_RESOURCE, code, pid));
if (MacOSXMinorVersion() >= 10) {
if (MacOSVersionNumber() >= 10'10'00) {
// FLAVOR_CPU_MONITOR_FATAL was introduced in OS X 10.10.
code = EXC_RESOURCE_ENCODE_TYPE_FLAVOR(RESOURCE_TYPE_CPU,
FLAVOR_CPU_MONITOR_FATAL);

View File

@ -50,7 +50,7 @@ exception_mask_t ExcMaskAll() {
#endif
#if defined(OS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9
const int mac_os_x_minor_version = MacOSXMinorVersion();
const int macos_version_number = MacOSVersionNumber();
#endif
// See 10.6.8 xnu-1504.15.3/osfmk/mach/exception_types.h. 10.7 uses the same
@ -67,7 +67,7 @@ exception_mask_t ExcMaskAll() {
EXC_MASK_RPC_ALERT |
EXC_MASK_MACHINE;
#if defined(OS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8
if (mac_os_x_minor_version < 8) {
if (macos_version_number < 10'08'00) {
return kExcMaskAll_10_6;
}
#endif
@ -77,7 +77,7 @@ exception_mask_t ExcMaskAll() {
constexpr exception_mask_t kExcMaskAll_10_8 =
kExcMaskAll_10_6 | EXC_MASK_RESOURCE;
#if defined(OS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_9
if (mac_os_x_minor_version < 9) {
if (macos_version_number < 10'09'00) {
return kExcMaskAll_10_8;
}
#endif
@ -97,7 +97,7 @@ exception_mask_t ExcMaskValid() {
#endif
#if defined(OS_MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_11
if (MacOSXMinorVersion() < 11) {
if (MacOSVersionNumber() < 10'11'00) {
return kExcMaskValid_10_6;
}
#endif

View File

@ -85,14 +85,14 @@ TEST(MachExtensions, ExcMaskAll) {
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) {
const int macos_version_number = MacOSVersionNumber();
if (macos_version_number >= 10'08'00) {
EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);
} else {
EXPECT_FALSE(exc_mask_all & EXC_MASK_RESOURCE);
}
if (mac_os_x_minor_version >= 9) {
if (macos_version_number >= 10'09'00) {
EXPECT_TRUE(exc_mask_all & EXC_MASK_GUARD);
} else {
EXPECT_FALSE(exc_mask_all & EXC_MASK_GUARD);
@ -118,20 +118,20 @@ TEST(MachExtensions, ExcMaskValid) {
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) {
const int macos_version_number = MacOSVersionNumber();
if (macos_version_number >= 10'08'00) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);
} else {
EXPECT_FALSE(exc_mask_valid & EXC_MASK_RESOURCE);
}
if (mac_os_x_minor_version >= 9) {
if (macos_version_number >= 10'09'00) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_GUARD);
} else {
EXPECT_FALSE(exc_mask_valid & EXC_MASK_GUARD);
}
if (mac_os_x_minor_version >= 11) {
if (macos_version_number >= 10'11'00) {
EXPECT_TRUE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
} else {
EXPECT_FALSE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);

View File

@ -102,6 +102,8 @@
'mac/mac_util.h',
'mac/service_management.cc',
'mac/service_management.h',
'mac/sysctl.cc',
'mac/sysctl.h',
'mac/xattr.cc',
'mac/xattr.h',
'mach/child_port.defs',

View File

@ -53,6 +53,7 @@
'mac/launchd_test.mm',
'mac/mac_util_test.mm',
'mac/service_management_test.mm',
'mac/sysctl_test.cc',
'mac/xattr_test.cc',
'mach/child_port_handshake_test.cc',
'mach/child_port_server_test.cc',