1// Copyright 2012 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "partition_alloc/partition_alloc_base/mac/mac_util.h"
6
7#include <sys/sysctl.h>
8#include <sys/types.h>
9#include <sys/utsname.h>
10
11#include <cstddef>
12#include <cstring>
13
14#include "partition_alloc/partition_alloc_base/check.h"
15#include "partition_alloc/partition_alloc_base/logging.h"
16
17// This is a simplified version of base::mac. Because
18// "base/strings/string_split.h" is unavailable, only provide access to the
19// macOS major version number via direct string work on the Darwin version.
20
21namespace partition_alloc::internal::base::mac {
22
23namespace {
24
25// Returns the running system's Darwin major version. Don't call this, it's an
26// implementation detail and its result is meant to be cached by
27// MacOSMajorVersion().
28int DarwinMajorVersion() {
29  // base::OperatingSystemVersionNumbers() at one time called Gestalt(), which
30  // was observed to be able to spawn threads (see https://crbug.com/53200).
31  // Nowadays that function calls -[NSProcessInfo operatingSystemVersion], whose
32  // current implementation does things like hit the file system, which is
33  // possibly a blocking operation. Either way, it's overkill for what needs to
34  // be done here.
35  //
36  // uname, on the other hand, is implemented as a simple series of sysctl
37  // system calls to obtain the relevant data from the kernel. The data is
38  // compiled right into the kernel, so no threads or blocking or other
39  // funny business is necessary.
40
41  struct utsname uname_info;
42  if (uname(&uname_info) != 0) {
43    PA_DPLOG(ERROR) << "uname";
44    return 0;
45  }
46
47  if (strcmp(uname_info.sysname, "Darwin") != 0) {
48    PA_DLOG(ERROR) << "unexpected uname sysname " << uname_info.sysname;
49    return 0;
50  }
51
52  const char* dot = strchr(uname_info.release, '.');
53  if (!dot || uname_info.release == dot ||
54      // Darwin version should be 1 or 2 digits, it's unlikely to be more than
55      // 4 digits.
56      dot - uname_info.release > 4) {
57    PA_DLOG(ERROR) << "could not parse uname release " << uname_info.release;
58    return 0;
59  }
60
61  int darwin_major_version = 0;
62  constexpr int base = 10;
63  for (const char* p = uname_info.release; p < dot; ++p) {
64    if (!('0' <= *p && *p < '0' + base)) {
65      PA_DLOG(ERROR) << "could not parse uname release " << uname_info.release;
66      return 0;
67    }
68
69    // Since we checked the number of digits is 4 at most (see above), there is
70    // no chance to overflow.
71    darwin_major_version *= base;
72    darwin_major_version += *p - '0';
73  }
74
75  return darwin_major_version;
76}
77
78}  // namespace
79
80int MacOSMajorVersion() {
81  static int macos_major_version = [] {
82    int darwin_major_version = DarwinMajorVersion();
83
84    // Darwin major versions 6 through 19 corresponded to macOS versions 10.2
85    // through 10.15.
86    PA_BASE_CHECK(darwin_major_version >= 6);
87    if (darwin_major_version <= 19) {
88      return 10;
89    }
90
91    // Darwin major version 20 corresponds to macOS version 11.0. Assume a
92    // correspondence between Darwin's major version numbers and macOS major
93    // version numbers.
94    return darwin_major_version - 9;
95  }();
96  return macos_major_version;
97}
98
99}  // namespace partition_alloc::internal::base::mac
100