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;
}