mac: kern.nx is not present in 10.14.4 et seq, but NX is always enabled

Bug: crashpad:295
Change-Id: Id1de68d402d229b43fab5e8d15b0fe23c618ce08
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2119645
Commit-Queue: Justin Cohen <justincohen@chromium.org>
Reviewed-by: Justin Cohen <justincohen@chromium.org>
diff --git a/snapshot/mac/system_snapshot_mac.cc b/snapshot/mac/system_snapshot_mac.cc
index 21f254e..1ece816 100644
--- a/snapshot/mac/system_snapshot_mac.cc
+++ b/snapshot/mac/system_snapshot_mac.cc
@@ -14,6 +14,7 @@
 
 #include "snapshot/mac/system_snapshot_mac.h"
 
+#include <AvailabilityMacros.h>
 #include <stddef.h>
 #include <sys/sysctl.h>
 #include <sys/types.h>
@@ -22,6 +23,7 @@
 #include <algorithm>
 
 #include "base/logging.h"
+#include "base/scoped_clear_errno.h"
 #include "base/strings/stringprintf.h"
 #include "build/build_config.h"
 #include "snapshot/cpu_context.h"
@@ -35,10 +37,15 @@
 namespace {
 
 template <typename T>
+int ReadIntSysctlByName_NoLog(const char* name, T* value) {
+  size_t value_len = sizeof(*value);
+  return sysctlbyname(name, value, &value_len, nullptr, 0);
+}
+
+template <typename T>
 T ReadIntSysctlByName(const char* name, T default_value) {
   T value;
-  size_t value_len = sizeof(value);
-  if (sysctlbyname(name, &value, &value_len, nullptr, 0) != 0) {
+  if (ReadIntSysctlByName_NoLog(name, &value) != 0) {
     PLOG(WARNING) << "sysctlbyname " << name;
     return default_value;
   }
@@ -338,7 +345,35 @@
 
 bool SystemSnapshotMac::NXEnabled() const {
   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
-  return ReadIntSysctlByName<int>("kern.nx", 0);
+
+  int value;
+  if (ReadIntSysctlByName_NoLog("kern.nx", &value) != 0) {
+    {
+      // Support for the kern.nx sysctlbyname is compiled out of production
+      // kernels on macOS 10.14.4 and later, although it’s available in
+      // development and debug kernels. Compare 10.14.3
+      // xnu-4903.241.1/bsd/kern/kern_sysctl.c to 10.15.0
+      // xnu-6153.11.26/bsd/kern/kern_sysctl.c (10.14.4 xnu source is not yet
+      // available). In newer production kernels, NX is always enabled. See
+      // 10.15.0 xnu-6153.11.26/osfmk/x86_64/pmap.c nx_enabled.
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14
+      const bool nx_always_enabled = true;
+#else  // DT >= 10.14
+      base::ScopedClearErrno reset_errno;
+      const bool nx_always_enabled = MacOSXMinorVersion() >= 14;
+#endif  // DT >= 10.14
+      if (nx_always_enabled) {
+        return true;
+      }
+    }
+
+    // Even if sysctlbyname should have worked, NX is enabled by default in all
+    // supported configurations, so return true even while warning.
+    PLOG(WARNING) << "sysctlbyname kern.nx";
+    return true;
+  }
+
+  return value;
 }
 
 void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status,
diff --git a/snapshot/mac/system_snapshot_mac_test.cc b/snapshot/mac/system_snapshot_mac_test.cc
index 69048eb..2b66592 100644
--- a/snapshot/mac/system_snapshot_mac_test.cc
+++ b/snapshot/mac/system_snapshot_mac_test.cc
@@ -126,6 +126,12 @@
   EXPECT_FALSE(system_snapshot().MachineDescription().empty());
 }
 
+TEST_F(SystemSnapshotMacTest, NXEnabled) {
+  // Assume NX will always be enabled, as it was always enabled by default on
+  // all supported versions of macOS.
+  EXPECT_TRUE(system_snapshot().NXEnabled());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace crashpad