Posix: Unify minimum kernel version checks

Introduce SysInfo::KernelVersionNumber to hold kernel version numbers
for POSIX platforms. Also provide a static method
SysInfo::KernelVersionNumber::Current() to query the kernel version
using uname. This deduplicates code across the codebase and makes
minimum kernel version checks more readable.

Bug: 384902323
Change-Id: Ib92ad9aeafa13045a52d109c46a8e9a5436cff2b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6304829
Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Anand Ravi <anandrv@google.com>
Cr-Commit-Position: refs/heads/main@{#1430493}
diff --git a/base/rand_util_posix.cc b/base/rand_util_posix.cc
index dc030af9..bced708 100644
--- a/base/rand_util_posix.cc
+++ b/base/rand_util_posix.cc
@@ -25,6 +25,7 @@
 #include "base/metrics/histogram_macros.h"
 #include "base/no_destructor.h"
 #include "base/posix/eintr_wrapper.h"
+#include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 
@@ -72,38 +73,10 @@
 #if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \
      BUILDFLAG(IS_ANDROID)) &&                        \
     !BUILDFLAG(IS_NACL)
-// TODO(pasko): Unify reading kernel version numbers in:
-// mojo/core/channel_linux.cc
-// chrome/browser/android/seccomp_support_detector.cc
-void KernelVersionNumbers(int32_t* major_version,
-                          int32_t* minor_version,
-                          int32_t* bugfix_version) {
-  struct utsname info;
-  if (uname(&info) < 0) {
-    NOTREACHED();
-  }
-  int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
-                        bugfix_version);
-  if (num_read < 1) {
-    *major_version = 0;
-  }
-  if (num_read < 2) {
-    *minor_version = 0;
-  }
-  if (num_read < 3) {
-    *bugfix_version = 0;
-  }
-}
 
 bool KernelSupportsGetRandom() {
-  int32_t major = 0;
-  int32_t minor = 0;
-  int32_t bugfix = 0;
-  KernelVersionNumbers(&major, &minor, &bugfix);
-  if (major > 3 || (major == 3 && minor >= 17)) {
-    return true;
-  }
-  return false;
+  return base::SysInfo::KernelVersionNumber::Current() >=
+         base::SysInfo::KernelVersionNumber(3, 17);
 }
 
 bool GetRandomSyscall(void* output, size_t output_length) {
diff --git a/base/system/sys_info.h b/base/system/sys_info.h
index f57ebd3..f7b9aaa4 100644
--- a/base/system/sys_info.h
+++ b/base/system/sys_info.h
@@ -8,6 +8,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <compare>
+#include <iosfwd>
 #include <map>
 #include <optional>
 #include <string>
@@ -194,6 +196,27 @@
                                             int32_t* minor_version,
                                             int32_t* bugfix_version);
 
+#if BUILDFLAG(IS_POSIX)
+  // Struct containing the the kernel version number of the host operating
+  // system.
+  struct BASE_EXPORT KernelVersionNumber {
+    // Queries the current kernel version number using uname and parses the
+    // release string to construct the KernelVersionNumber struct. This does not
+    // cache the result.
+    static KernelVersionNumber Current();
+
+    friend bool operator==(const KernelVersionNumber& v1,
+                           const KernelVersionNumber& v2) = default;
+
+    friend auto operator<=>(const KernelVersionNumber& v1,
+                            const KernelVersionNumber& v2) = default;
+
+    int32_t major = 0;
+    int32_t minor = 0;
+    int32_t bugfix = 0;
+  };
+#endif  // BUILDFLAG(IS_POSIX)
+
   // Returns the architecture of the running operating system.
   // Exact return value may differ across platforms.
   // e.g. a 32-bit x86 kernel on a 64-bit capable CPU will return "x86",
@@ -356,6 +379,13 @@
   static void ClearAmountOfPhysicalMemoryMbForTesting();
 };
 
+#if BUILDFLAG(IS_POSIX)
+// Stream operator so that SysInfo::KernelVersionNumber can be logged with a
+// consistent format.
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+                                     const SysInfo::KernelVersionNumber& v);
+#endif  // BUILDFLAG(IS_POSIX)
+
 }  // namespace base
 
 #endif  // BASE_SYSTEM_SYS_INFO_H_
diff --git a/base/system/sys_info_posix.cc b/base/system/sys_info_posix.cc
index 1f43721..19f50f0b 100644
--- a/base/system/sys_info_posix.cc
+++ b/base/system/sys_info_posix.cc
@@ -20,6 +20,7 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <iostream>
 
 #include "base/check.h"
 #include "base/files/file_util.h"
@@ -120,6 +121,24 @@
   return true;
 }
 
+void GetKernelVersionNumbers(int32_t* major_version,
+                             int32_t* minor_version,
+                             int32_t* bugfix_version) {
+  struct utsname info;
+  CHECK_EQ(uname(&info), 0);
+  int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
+                        bugfix_version);
+  if (num_read < 1) {
+    *major_version = 0;
+  }
+  if (num_read < 2) {
+    *minor_version = 0;
+  }
+  if (num_read < 3) {
+    *bugfix_version = 0;
+  }
+}
+
 }  // namespace
 
 namespace base {
@@ -238,24 +257,22 @@
 void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
                                             int32_t* minor_version,
                                             int32_t* bugfix_version) {
-  struct utsname info;
-  if (uname(&info) < 0) {
-    NOTREACHED();
-  }
-  int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
-                        bugfix_version);
-  if (num_read < 1) {
-    *major_version = 0;
-  }
-  if (num_read < 2) {
-    *minor_version = 0;
-  }
-  if (num_read < 3) {
-    *bugfix_version = 0;
-  }
+  GetKernelVersionNumbers(major_version, minor_version, bugfix_version);
 }
 #endif
 
+// static
+SysInfo::KernelVersionNumber SysInfo::KernelVersionNumber::Current() {
+  KernelVersionNumber v;
+  GetKernelVersionNumbers(&v.major, &v.minor, &v.bugfix);
+  return v;
+}
+
+std::ostream& operator<<(std::ostream& out,
+                         const SysInfo::KernelVersionNumber& v) {
+  return out << v.major << "." << v.minor << "." << v.bugfix;
+}
+
 #if !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_IOS)
 // static
 std::string SysInfo::OperatingSystemArchitecture() {
diff --git a/base/system/sys_info_unittest.cc b/base/system/sys_info_unittest.cc
index 1983447..614472a 100644
--- a/base/system/sys_info_unittest.cc
+++ b/base/system/sys_info_unittest.cc
@@ -462,4 +462,26 @@
 
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(IS_POSIX)
+TEST_F(SysInfoTest, KernelVersionNumber) {
+  auto current_kernel_version = SysInfo::KernelVersionNumber::Current();
+
+  EXPECT_GT(current_kernel_version, SysInfo::KernelVersionNumber());
+  // Chromium will realistically never run on a kernel as old as 2.1.11
+  EXPECT_GT(current_kernel_version, SysInfo::KernelVersionNumber(2, 1, 11));
+
+  SysInfo::KernelVersionNumber next_major_kernel_version(
+      current_kernel_version.major + 1);
+  EXPECT_LT(current_kernel_version, next_major_kernel_version);
+
+  SysInfo::KernelVersionNumber next_minor_kernel_version(
+      current_kernel_version.major, current_kernel_version.minor + 1);
+  EXPECT_LT(current_kernel_version, next_minor_kernel_version);
+
+  SysInfo::KernelVersionNumber next_bugfix_kernel_version(
+      current_kernel_version.major, current_kernel_version.minor,
+      current_kernel_version.bugfix + 1);
+  EXPECT_LT(current_kernel_version, next_bugfix_kernel_version);
+}
+#endif  // BUILDFLAG(IS_POSIX)
 }  // namespace base
diff --git a/mojo/core/channel_linux.cc b/mojo/core/channel_linux.cc
index dd5edfc..84c52a5 100644
--- a/mojo/core/channel_linux.cc
+++ b/mojo/core/channel_linux.cc
@@ -58,29 +58,6 @@
 namespace mojo {
 namespace core {
 
-namespace {
-
-// On Android base::SysInfo::OperatingSystemVersionNumbers actually returns the
-// build numbers and not the kernel version as the other posix OSes would.
-void KernelVersionNumbers(int32_t* major_version,
-                          int32_t* minor_version,
-                          int32_t* bugfix_version) {
-  struct utsname info;
-  if (uname(&info) < 0) {
-    NOTREACHED();
-  }
-  int num_read = sscanf(info.release, "%d.%d.%d", major_version, minor_version,
-                        bugfix_version);
-  if (num_read < 1)
-    *major_version = 0;
-  if (num_read < 2)
-    *minor_version = 0;
-  if (num_read < 3)
-    *bugfix_version = 0;
-}
-
-}  // namespace
-
 // DataAvailableNotifier is a simple interface which allows us to
 // substitute how we notify the reader that we've made data available,
 // implementations might be EventFDNotifier or FutexNotifier.
@@ -918,12 +895,8 @@
     //
     // Additionally, the behavior of eventfd prior to the 4.0 kernel could be
     // racy.
-    int os_major_version = 0;
-    int os_minor_version = 0;
-    int os_bugfix_version = 0;
-    KernelVersionNumbers(&os_major_version, &os_minor_version,
-                         &os_bugfix_version);
-    if (os_major_version < 4) {
+    if (base::SysInfo::KernelVersionNumber::Current() <
+        base::SysInfo::KernelVersionNumber(4, 0)) {
       // Due to the potentially races in 3.17/3.18 kernels with eventfd,
       // explicitly require a 4.x+ kernel.
       return false;
diff --git a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
index c036c515..3cc7d24 100644
--- a/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
+++ b/ui/ozone/platform/wayland/host/wayland_buffer_manager_host.cc
@@ -22,6 +22,7 @@
 #include "base/posix/eintr_wrapper.h"
 #include "base/strings/strcat.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/system/sys_info.h"
 #include "base/task/current_thread.h"
 #include "base/trace_event/trace_event.h"
 #include "ui/gfx/geometry/size.h"
@@ -47,37 +48,10 @@
   return base::UTF16ToUTF8(base::FormatNumber(number));
 }
 
-struct KernelVersion {
-  int32_t major;
-  int32_t minor;
-  int32_t bugfix;
-};
-
-KernelVersion KernelVersionNumbers() {
-  KernelVersion ver;
-  struct utsname info;
-  if (uname(&info) < 0) {
-    NOTREACHED();
-  }
-  int num_read =
-      sscanf(info.release, "%d.%d.%d", &ver.major, &ver.minor, &ver.bugfix);
-  if (num_read < 1) {
-    ver.major = 0;
-  }
-  if (num_read < 2) {
-    ver.minor = 0;
-  }
-  if (num_read < 3) {
-    ver.bugfix = 0;
-  }
-  return ver;
-}
-
 bool CheckImportExportFence() {
-  KernelVersion ver = KernelVersionNumbers();
-
   // DMA_BUF_IOCTL_{IMPORT,EXPORT}_SYNC_FILE was added in 6.0
-  return ver.major >= 6;
+  return base::SysInfo::KernelVersionNumber::Current() >=
+         base::SysInfo::KernelVersionNumber(6, 0);
 }
 
 }  // namespace