jitzygote: madvise DONTFORK on writable shared region.

So only the zygote can write to the shared mapping.

Test: boots
Bug: 119800099
Change-Id: I9efde2c9c89dbc7445da890ee975effe13ba3790
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index 7a7158d..03e8218 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -619,6 +619,13 @@
   Invalidate();
 }
 
+void MemMap::ResetInForkedProcess() {
+  // This should be called on a map that has MADV_DONTFORK.
+  // The kernel has already unmapped this.
+  already_unmapped_ = true;
+  Reset();
+}
+
 void MemMap::Invalidate() {
   DCHECK(IsValid());
 
@@ -824,6 +831,15 @@
   }
 }
 
+int MemMap::MadviseDontFork() {
+#if defined(__linux__)
+  if (base_begin_ != nullptr || base_size_ != 0) {
+    return madvise(base_begin_, base_size_, MADV_DONTFORK);
+  }
+#endif
+  return -1;
+}
+
 bool MemMap::Sync() {
 #ifdef _WIN32
   // TODO: add FlushViewOfFile support.
diff --git a/libartbase/base/mem_map.h b/libartbase/base/mem_map.h
index 525e622..cdcfc3e 100644
--- a/libartbase/base/mem_map.h
+++ b/libartbase/base/mem_map.h
@@ -232,6 +232,7 @@
   bool Protect(int prot);
 
   void MadviseDontNeedAndZero();
+  int MadviseDontFork();
 
   int GetProtect() const {
     return prot_;
@@ -317,6 +318,10 @@
     return nullptr;
   }
 
+  // Reset in a forked process the MemMap whose memory has been madvised MADV_DONTFORK
+  // in the parent process.
+  void ResetInForkedProcess();
+
  private:
   MemMap(const std::string& name,
          uint8_t* begin,
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 97b5b8d..8f4d0d4 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1730,6 +1730,12 @@
 }
 
 void JitCodeCache::PostForkChildAction(bool is_system_server, bool is_zygote) {
+  MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+
+  // Reset potential writable MemMaps inherited from the zygote. We never want
+  // to write to them.
+  shared_region_.ResetWritableMappings();
+
   if (is_zygote || Runtime::Current()->IsSafeMode()) {
     // Don't create a private region for a child zygote. Regions are usually map shared
     // (to satisfy dual-view), and we don't want children of a child zygote to inherit it.
@@ -1742,7 +1748,6 @@
     CHECK(!shared_region_.IsValid());
     std::swap(shared_region_, private_region_);
   }
-  MutexLock mu(Thread::Current(), *Locks::jit_lock_);
 
   // Reset all statistics to be specific to this process.
   number_of_compilations_ = 0;
diff --git a/runtime/jit/jit_memory_region.cc b/runtime/jit/jit_memory_region.cc
index 865a7d4..379aa7c 100644
--- a/runtime/jit/jit_memory_region.cc
+++ b/runtime/jit/jit_memory_region.cc
@@ -231,6 +231,14 @@
           *error_msg = oss.str();
           return false;
         }
+        if (writable_data_pages.MadviseDontFork() != 0) {
+          *error_msg = "Failed to madvise dont fork the writable data view";
+          return false;
+        }
+        if (non_exec_pages.MadviseDontFork() != 0) {
+          *error_msg = "Failed to madvise dont fork the writable code view";
+          return false;
+        }
         // Now that we have created the writable and executable mappings, prevent creating any new
         // ones.
         if (!ProtectZygoteMemory(mem_fd.get(), error_msg)) {
diff --git a/runtime/jit/jit_memory_region.h b/runtime/jit/jit_memory_region.h
index fa74378..0833380 100644
--- a/runtime/jit/jit_memory_region.h
+++ b/runtime/jit/jit_memory_region.h
@@ -98,6 +98,15 @@
       REQUIRES(Locks::jit_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void ResetWritableMappings() REQUIRES(Locks::jit_lock_) {
+    non_exec_pages_.ResetInForkedProcess();
+    writable_data_pages_.ResetInForkedProcess();
+    // Also clear the mspaces, which, in their implementation,
+    // point to the discarded mappings.
+    exec_mspace_ = nullptr;
+    data_mspace_ = nullptr;
+  }
+
   bool IsValid() const NO_THREAD_SAFETY_ANALYSIS {
     return exec_mspace_ != nullptr || data_mspace_ != nullptr;
   }