ART: Add version check for memfd_create()

Add a kernel version check in the ART shim for memfd_create().

Bug: b/116769556
Test: Tested on go/lem (3.14 kernel)
Change-Id: I1fd5cabc6705f856bea8044ca82274020ff84914
diff --git a/libartbase/base/memfd.cc b/libartbase/base/memfd.cc
index 1afcd7b..7c20401 100644
--- a/libartbase/base/memfd.cc
+++ b/libartbase/base/memfd.cc
@@ -17,7 +17,9 @@
 #include "memfd.h"
 
 #include <errno.h>
+#include <stdio.h>
 #include <sys/syscall.h>
+#include <sys/utsname.h>
 #include <unistd.h>
 
 #include "macros.h"
@@ -37,6 +39,20 @@
 #if defined(__NR_memfd_create)
 
 int memfd_create(const char* name, unsigned int flags) {
+  // Check kernel version supports memfd_create(). Some older kernels segfault executing
+  // memfd_create() rather than returning ENOSYS (b/116769556).
+  static constexpr int kRequiredMajor = 3;
+  static constexpr int kRequiredMinor = 17;
+  struct utsname uts;
+  int major, minor;
+  if (uname(&uts) != 0 ||
+      strcmp(uts.sysname, "Linux") != 0 ||
+      sscanf(uts.release, "%d.%d", &major, &minor) != 2 ||
+      (major < kRequiredMajor || (major == kRequiredMajor && minor < kRequiredMinor))) {
+    errno = ENOSYS;
+    return -1;
+  }
+
   return syscall(__NR_memfd_create, name, flags);
 }
 
diff --git a/libartbase/base/memfd.h b/libartbase/base/memfd.h
index 4367198..91db0b2 100644
--- a/libartbase/base/memfd.h
+++ b/libartbase/base/memfd.h
@@ -19,7 +19,8 @@
 
 namespace art {
 
-  // Call memfd(2) if available on platform and return result.
+// Call memfd(2) if available on platform and return result. This call also makes a kernel version
+// check for safety on older kernels (b/116769556)..
 int memfd_create(const char* name, unsigned int flags);
 
 }  // namespace art