Add more tests around JitMemoryRegion.

- Test that dropping the writable mapping and keeping the readable one works.
- Test using MADV_DONTFORK.
- Test behavior of memfd when mapping MAP_PRIVATE.

Also move IsSealFutureWriteSupported to libartbase/mem_fd.

Test: jit_memory_region_test
Change-Id: If6392780595d968588041762a72d7fa46a7b0429
diff --git a/libartbase/base/memfd.cc b/libartbase/base/memfd.cc
index c8ec18d..2aab7fc 100644
--- a/libartbase/base/memfd.cc
+++ b/libartbase/base/memfd.cc
@@ -24,9 +24,16 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 #endif
+#if defined(__BIONIC__)
+#include <linux/memfd.h>  // To access memfd flags.
+#endif
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 
 #include "macros.h"
 
+
 // When building for linux host, glibc in prebuilts does not include memfd_create system call
 // number. As a temporary testing measure, we add the definition here.
 #if defined(__linux__) && !defined(__NR_memfd_create)
@@ -91,4 +98,35 @@
   return res;
 }
 
+#if defined(__BIONIC__)
+
+static bool IsSealFutureWriteSupportedInternal() {
+  android::base::unique_fd fd(art::memfd_create("test_android_memfd", MFD_ALLOW_SEALING));
+  if (fd == -1) {
+    LOG(INFO) << "memfd_create failed: " << strerror(errno) << ", no memfd support.";
+    return false;
+  }
+
+  if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
+    LOG(INFO) << "fcntl(F_ADD_SEALS) failed: " << strerror(errno) << ", no memfd support.";
+    return false;
+  }
+
+  LOG(INFO) << "Using memfd for future sealing";
+  return true;
+}
+
+bool IsSealFutureWriteSupported() {
+  static bool is_seal_future_write_supported = IsSealFutureWriteSupportedInternal();
+  return is_seal_future_write_supported;
+}
+
+#else
+
+bool IsSealFutureWriteSupported() {
+  return false;
+}
+
+#endif
+
 }  // namespace art
diff --git a/libartbase/base/memfd.h b/libartbase/base/memfd.h
index b5945fb..53cfe9c 100644
--- a/libartbase/base/memfd.h
+++ b/libartbase/base/memfd.h
@@ -31,6 +31,9 @@
 // other way if memfd fails or isn't supported.
 int memfd_create_compat(const char* name, unsigned int flags);
 
+// Return whether the kernel supports sealing future writes of a memfd.
+bool IsSealFutureWriteSupported();
+
 }  // namespace art
 
 #endif  // ART_LIBARTBASE_BASE_MEMFD_H_
diff --git a/runtime/jit/jit_memory_region.cc b/runtime/jit/jit_memory_region.cc
index 379aa7c..9092695 100644
--- a/runtime/jit/jit_memory_region.cc
+++ b/runtime/jit/jit_memory_region.cc
@@ -486,31 +486,10 @@
 
 #if defined(__BIONIC__)
 
-static bool IsSealFutureWriteSupportedInternal() {
-  unique_fd fd(art::memfd_create("test_android_memfd", MFD_ALLOW_SEALING));
-  if (fd == -1) {
-    LOG(INFO) << "memfd_create failed: " << strerror(errno) << ", no memfd support.";
-    return false;
-  }
-
-  if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {
-    LOG(INFO) << "fcntl(F_ADD_SEALS) failed: " << strerror(errno) << ", no memfd support.";
-    return false;
-  }
-
-  LOG(INFO) << "Using memfd for future sealing";
-  return true;
-}
-
-static bool IsSealFutureWriteSupported() {
-  static bool is_seal_future_write_supported = IsSealFutureWriteSupportedInternal();
-  return is_seal_future_write_supported;
-}
-
 int JitMemoryRegion::CreateZygoteMemory(size_t capacity, std::string* error_msg) {
   /* Check if kernel support exists, otherwise fall back to ashmem */
   static const char* kRegionName = "/jit-zygote-cache";
-  if (IsSealFutureWriteSupported()) {
+  if (art::IsSealFutureWriteSupported()) {
     int fd = art::memfd_create(kRegionName, MFD_ALLOW_SEALING);
     if (fd == -1) {
       std::ostringstream oss;
@@ -544,7 +523,7 @@
 }
 
 bool JitMemoryRegion::ProtectZygoteMemory(int fd, std::string* error_msg) {
-  if (IsSealFutureWriteSupported()) {
+  if (art::IsSealFutureWriteSupported()) {
     if (fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL | F_SEAL_FUTURE_WRITE)
             == -1) {
       std::ostringstream oss;
diff --git a/runtime/jit/jit_memory_region_test.cc b/runtime/jit/jit_memory_region_test.cc
index 25255bb..e99ff70 100644
--- a/runtime/jit/jit_memory_region_test.cc
+++ b/runtime/jit/jit_memory_region_test.cc
@@ -16,19 +16,43 @@
 
 #include "jit/jit_memory_region.h"
 
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
-#include <sys/mman.h>
 
 #include "base/globals.h"
+#include "base/memfd.h"
 
 namespace art {
 namespace jit {
 
+// These tests only run on bionic.
+#if defined(__BIONIC__)
+static constexpr int kReturnFromFault = 42;
+
+// These globals are only set in child processes.
+void* gAddrToFaultOn = nullptr;
+
+void handler(int ATTRIBUTE_UNUSED, siginfo_t* info, void* ATTRIBUTE_UNUSED) {
+  CHECK_EQ(info->si_addr, gAddrToFaultOn);
+  exit(kReturnFromFault);
+}
+
+static void registerSignalHandler() {
+  struct sigaction sa;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = SA_SIGINFO;
+  sa.sa_sigaction = handler;
+  sigaction(SIGSEGV, &sa, nullptr);
+}
+
 class TestZygoteMemory : public testing::Test {
  public:
   void BasicTest() {
-#if defined(__BIONIC__)
     std::string error_msg;
     size_t size = kPageSize;
     android::base::unique_fd fd(JitMemoryRegion::CreateZygoteMemory(size, &error_msg));
@@ -77,7 +101,386 @@
     // Test that we can  write into the remapped mapping.
     addr2[0] = 4;
     CHECK_EQ(addr2[0], 4);
-#endif
+  }
+
+  void TestUnmapWritableAfterFork() {
+    std::string error_msg;
+    size_t size = kPageSize;
+    int32_t* addr = nullptr;
+    int32_t* addr2 = nullptr;
+    {
+      android::base::unique_fd fd(JitMemoryRegion::CreateZygoteMemory(size, &error_msg));
+      CHECK_NE(fd.get(), -1);
+
+      // Create a writable mapping.
+      addr = reinterpret_cast<int32_t*>(
+          mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0));
+      CHECK(addr != nullptr);
+      CHECK_NE(addr, MAP_FAILED);
+
+      // Test that we can write into the mapping.
+      addr[0] = 42;
+      CHECK_EQ(addr[0], 42);
+
+      // Create a read-only mapping.
+      addr2 = reinterpret_cast<int32_t*>(
+          mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, fd.get(), 0));
+      CHECK(addr2 != nullptr);
+
+      // Protect the memory.
+      bool res = JitMemoryRegion::ProtectZygoteMemory(fd.get(), &error_msg);
+      CHECK(res);
+    }
+    // At this point, the fd has been dropped, but the memory mappings are still
+    // there.
+
+    // Create a mapping of atomic ints to communicate between processes.
+    android::base::unique_fd fd2(JitMemoryRegion::CreateZygoteMemory(size, &error_msg));
+    CHECK_NE(fd2.get(), -1);
+    std::atomic<int32_t>* shared = reinterpret_cast<std::atomic<int32_t>*>(
+        mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd2.get(), 0));
+
+    // Values used for the tests below.
+    const int32_t parent_value = 66;
+    const int32_t child_value = 33;
+    const int32_t starting_value = 22;
+
+    shared[0] = 0;
+    addr[0] = starting_value;
+    CHECK_EQ(addr[0], starting_value);
+    CHECK_EQ(addr2[0], starting_value);
+    pid_t pid = fork();
+    if (pid == 0) {
+      // Test that we can write into the mapping.
+      addr[0] = child_value;
+      CHECK_EQ(addr[0], child_value);
+      CHECK_EQ(addr2[0], child_value);
+
+      // Unmap the writable mappping.
+      munmap(addr, kPageSize);
+
+      CHECK_EQ(addr2[0], child_value);
+
+      // Notify parent process.
+      shared[0] = 1;
+
+      // Wait for parent process for a new value.
+      while (shared[0] != 2) {
+        sched_yield();
+      }
+      CHECK_EQ(addr2[0], parent_value);
+
+      // Test that we cannot write into the mapping. The signal handler will
+      // exit the process.
+      gAddrToFaultOn = addr;
+      registerSignalHandler();
+      // This write will trigger a fault, as `addr` is unmapped.
+      addr[0] = child_value + 1;
+      exit(0);
+    } else {
+      while (shared[0] != 1) {
+        sched_yield();
+      }
+      CHECK_EQ(addr[0], child_value);
+      CHECK_EQ(addr2[0], child_value);
+      addr[0] = parent_value;
+      // Notify the child if the new value.
+      shared[0] = 2;
+      int status;
+      CHECK_EQ(waitpid(pid, &status, 0), pid);
+      CHECK(WIFEXITED(status)) << strerror(errno);
+      CHECK_EQ(WEXITSTATUS(status), kReturnFromFault);
+      CHECK_EQ(addr[0], parent_value);
+      CHECK_EQ(addr2[0], parent_value);
+      munmap(addr, kPageSize);
+      munmap(addr2, kPageSize);
+      munmap(shared, kPageSize);
+    }
+  }
+
+  void TestMadviseDontFork() {
+    std::string error_msg;
+    size_t size = kPageSize;
+    int32_t* addr = nullptr;
+    int32_t* addr2 = nullptr;
+    {
+      android::base::unique_fd fd(JitMemoryRegion::CreateZygoteMemory(size, &error_msg));
+      CHECK_NE(fd.get(), -1);
+
+      // Create a writable mapping.
+      addr = reinterpret_cast<int32_t*>(
+          mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0));
+      CHECK(addr != nullptr);
+      CHECK_NE(addr, MAP_FAILED);
+      CHECK_EQ(madvise(addr, kPageSize, MADV_DONTFORK), 0);
+
+      // Test that we can write into the mapping.
+      addr[0] = 42;
+      CHECK_EQ(addr[0], 42);
+
+      // Create a read-only mapping.
+      addr2 = reinterpret_cast<int32_t*>(
+          mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, fd.get(), 0));
+      CHECK(addr2 != nullptr);
+
+      // Protect the memory.
+      bool res = JitMemoryRegion::ProtectZygoteMemory(fd.get(), &error_msg);
+      CHECK(res);
+    }
+    // At this point, the fd has been dropped, but the memory mappings are still
+    // there.
+
+    // Create a mapping of atomic ints to communicate between processes.
+    android::base::unique_fd fd2(JitMemoryRegion::CreateZygoteMemory(size, &error_msg));
+    CHECK_NE(fd2.get(), -1);
+    std::atomic<int32_t>* shared = reinterpret_cast<std::atomic<int32_t>*>(
+        mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd2.get(), 0));
+
+    // Values used for the tests below.
+    const int32_t parent_value = 66;
+    const int32_t child_value = 33;
+    const int32_t starting_value = 22;
+
+    shared[0] = 0;
+    addr[0] = starting_value;
+    CHECK_EQ(addr[0], starting_value);
+    CHECK_EQ(addr2[0], starting_value);
+    pid_t pid = fork();
+    if (pid == 0) {
+      CHECK_EQ(addr2[0], starting_value);
+
+      // Notify parent process.
+      shared[0] = 1;
+
+      // Wait for parent process for new value.
+      while (shared[0] != 2) {
+        sched_yield();
+      }
+
+      CHECK_EQ(addr2[0], parent_value);
+      // Test that we cannot write into the mapping. The signal handler will
+      // exit the process.
+      gAddrToFaultOn = addr;
+      registerSignalHandler();
+      addr[0] = child_value + 1;
+      exit(0);
+    } else {
+      while (shared[0] != 1) {
+        sched_yield();
+      }
+      CHECK_EQ(addr[0], starting_value);
+      CHECK_EQ(addr2[0], starting_value);
+      addr[0] = parent_value;
+      // Notify the child of the new value.
+      shared[0] = 2;
+      int status;
+      CHECK_EQ(waitpid(pid, &status, 0), pid);
+      CHECK(WIFEXITED(status)) << strerror(errno);
+      CHECK_EQ(WEXITSTATUS(status), kReturnFromFault);
+      CHECK_EQ(addr[0], parent_value);
+      CHECK_EQ(addr2[0], parent_value);
+
+      munmap(addr, kPageSize);
+      munmap(addr2, kPageSize);
+      munmap(shared, kPageSize);
+    }
+  }
+
+  // This code is testing some behavior that ART could potentially use: get a
+  // copy-on-write mapping that can incorporate changes from a shared mapping
+  // owned by another process.
+  void TestFromSharedToPrivate() {
+    // This test is only for memfd with future write sealing support:
+    // 1) ashmem with PROT_READ doesn't permit mapping MAP_PRIVATE | PROT_WRITE
+    // 2) ashmem mapped MAP_PRIVATE discards the contents already written.
+    if (!art::IsSealFutureWriteSupported()) {
+      return;
+    }
+    std::string error_msg;
+    size_t size = kPageSize;
+    int32_t* addr = nullptr;
+    android::base::unique_fd fd(JitMemoryRegion::CreateZygoteMemory(size, &error_msg));
+    CHECK_NE(fd.get(), -1);
+
+    // Create a writable mapping.
+    addr = reinterpret_cast<int32_t*>(
+        mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0));
+    CHECK(addr != nullptr);
+    CHECK_NE(addr, MAP_FAILED);
+
+    // Test that we can write into the mapping.
+    addr[0] = 42;
+    CHECK_EQ(addr[0], 42);
+
+    // Create another mapping of atomic ints to communicate between processes.
+    android::base::unique_fd fd2(JitMemoryRegion::CreateZygoteMemory(size, &error_msg));
+    CHECK_NE(fd2.get(), -1);
+    std::atomic<int32_t>* shared = reinterpret_cast<std::atomic<int32_t>*>(
+        mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd2.get(), 0));
+
+    // Protect the memory.
+    CHECK(JitMemoryRegion::ProtectZygoteMemory(fd.get(), &error_msg));
+
+    // Values used for the tests below.
+    const int32_t parent_value = 66;
+    const int32_t child_value = 33;
+    const int32_t starting_value = 22;
+
+    // Check that updates done by a child mapping write-private are not visible
+    // to the parent.
+    addr[0] = starting_value;
+    shared[0] = 0;
+    pid_t pid = fork();
+    if (pid == 0) {
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd.get(), 0),
+               addr);
+      addr[0] = child_value;
+      exit(0);
+    } else {
+      int status;
+      CHECK_EQ(waitpid(pid, &status, 0), pid);
+      CHECK(WIFEXITED(status)) << strerror(errno);
+      CHECK_EQ(addr[0], starting_value);
+    }
+
+    addr[0] = starting_value;
+    shared[0] = 0;
+
+    // Check getting back and forth on shared mapping.
+    pid = fork();
+    if (pid == 0) {
+      // Map it private with write access. MAP_FIXED will replace the existing
+      // mapping.
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd.get(), 0),
+               addr);
+      addr[0] = child_value;
+      CHECK_EQ(addr[0], child_value);
+
+      // Check that mapping shared with write access fails.
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd.get(), 0),
+               MAP_FAILED);
+      CHECK_EQ(errno, EPERM);
+
+      // Map shared with read access.
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ, MAP_SHARED | MAP_FIXED, fd.get(), 0), addr);
+      CHECK_NE(addr[0], child_value);
+
+      // Wait for the parent to notify.
+      while (shared[0] != 1) {
+        sched_yield();
+      }
+      CHECK_EQ(addr[0], parent_value);
+
+      // Notify the parent for getting a new update of the buffer.
+      shared[0] = 2;
+
+      // Map it private again.
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd.get(), 0),
+               addr);
+      addr[0] = child_value + 1;
+      CHECK_EQ(addr[0], child_value + 1);
+
+      // And map it back shared.
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ, MAP_SHARED | MAP_FIXED, fd.get(), 0), addr);
+      while (shared[0] != 3) {
+        sched_yield();
+      }
+      CHECK_EQ(addr[0], parent_value + 1);
+      exit(0);
+    } else {
+      addr[0] = parent_value;
+      CHECK_EQ(addr[0], parent_value);
+
+      // Notify the child of the new value.
+      shared[0] = 1;
+
+      // Wait for the child to ask for a new value;
+      while (shared[0] != 2) {
+        sched_yield();
+      }
+      addr[0] = parent_value + 1;
+      CHECK_EQ(addr[0], parent_value + 1);
+
+      // Notify the child of a new value.
+      shared[0] = 3;
+      int status;
+      CHECK_EQ(waitpid(pid, &status, 0), pid);
+      CHECK(WIFEXITED(status)) << strerror(errno);
+      CHECK_EQ(addr[0], parent_value + 1);
+    }
+
+    // Check that updates done by the parent are visible after a new mmap
+    // write-private.
+    shared[0] = 0;
+    addr[0] = starting_value;
+    pid = fork();
+    if (pid == 0) {
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd.get(), 0),
+               addr);
+      CHECK_EQ(addr[0], starting_value);
+      addr[0] = child_value;
+      CHECK_EQ(addr[0], child_value);
+
+      // Notify the parent to update the buffer.
+      shared[0] = 1;
+
+      // Wait for the parent update.
+      while (shared[0] != 2) {
+        sched_yield();
+      }
+      // Test the buffer still contains our own data, and not the parent's.
+      CHECK_EQ(addr[0], child_value);
+
+      // Test the buffer contains the parent data after a new mmap.
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd.get(), 0),
+               addr);
+      CHECK_EQ(addr[0], parent_value);
+      exit(0);
+    } else {
+      // Wait for the child to start
+      while (shared[0] != 1) {
+        sched_yield();
+      }
+      CHECK_EQ(addr[0], starting_value);
+      addr[0] = parent_value;
+      // Notify the child that the buffer has been written.
+      shared[0] = 2;
+      int status;
+      CHECK_EQ(waitpid(pid, &status, 0), pid);
+      CHECK(WIFEXITED(status)) << strerror(errno);
+      CHECK_EQ(addr[0], parent_value);
+    }
+
+    // Check that updates done by the parent are visible for a new mmap
+    // write-private that hasn't written to the buffer yet.
+    shared[0] = 0;
+    addr[0] = starting_value;
+    pid = fork();
+    if (pid == 0) {
+      CHECK_EQ(mmap(addr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED, fd.get(), 0),
+               addr);
+      CHECK_EQ(addr[0], starting_value);
+      // Notify the parent for a new update of the buffer.
+      shared[0] = 1;
+      while (addr[0] != parent_value) {
+        sched_yield();
+      }
+      addr[0] = child_value;
+      CHECK_EQ(addr[0], child_value);
+      exit(0);
+    } else {
+      while (shared[0] != 1) {
+        sched_yield();
+      }
+      CHECK_EQ(addr[0], starting_value);
+      addr[0] = parent_value;
+      int status;
+      CHECK_EQ(waitpid(pid, &status, 0), pid);
+      CHECK(WIFEXITED(status)) << strerror(errno);
+      CHECK_EQ(addr[0], parent_value);
+    }
+    munmap(addr, kPageSize);
+    munmap(shared, kPageSize);
   }
 };
 
@@ -85,5 +488,19 @@
   BasicTest();
 }
 
+TEST_F(TestZygoteMemory, TestUnmapWritableAfterFork) {
+  TestUnmapWritableAfterFork();
+}
+
+TEST_F(TestZygoteMemory, TestMadviseDontFork) {
+  TestMadviseDontFork();
+}
+
+TEST_F(TestZygoteMemory, TestFromSharedToPrivate) {
+  TestFromSharedToPrivate();
+}
+
+#endif  // defined (__BIONIC__)
+
 }  // namespace jit
 }  // namespace art