Bug 1344539 - Make committing executable memory fallible. r=luke a=RyanVM
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 21 Feb 2018 10:05:46 +0100
changeset 454988 00c3d85230c8a0cb809d14b3b84b4561f77b11be
parent 454987 6da57d64acd83c1de02c34c2a50b96a22df0723a
child 454989 0c6c5ed366a631216fed9f158fc6154368c57783
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke, RyanVM
bugs1344539
milestone59.0
Bug 1344539 - Make committing executable memory fallible. r=luke a=RyanVM
js/src/jit/ProcessExecutableMemory.cpp
--- a/js/src/jit/ProcessExecutableMemory.cpp
+++ b/js/src/jit/ProcessExecutableMemory.cpp
@@ -240,21 +240,24 @@ ProtectionSettingToFlags(ProtectionSetti
     switch (protection) {
       case ProtectionSetting::Protected:  return PAGE_NOACCESS;
       case ProtectionSetting::Writable:   return PAGE_READWRITE;
       case ProtectionSetting::Executable: return PAGE_EXECUTE_READ;
     }
     MOZ_CRASH();
 }
 
-static void
+static MOZ_MUST_USE bool
 CommitPages(void* addr, size_t bytes, ProtectionSetting protection)
 {
-    if (!VirtualAlloc(addr, bytes, MEM_COMMIT, ProtectionSettingToFlags(protection)))
-        MOZ_CRASH_UNSAFE_PRINTF("CommitPages failed! Error code: %lu", GetLastError());
+    void* p = VirtualAlloc(addr, bytes, MEM_COMMIT, ProtectionSettingToFlags(protection));
+    if (!p)
+        return false;
+    MOZ_RELEASE_ASSERT(p == addr);
+    return true;
 }
 
 static void
 DecommitPages(void* addr, size_t bytes)
 {
     if (!VirtualFree(addr, bytes, MEM_DECOMMIT))
         MOZ_CRASH("DecommitPages failed");
 }
@@ -325,23 +328,26 @@ ProtectionSettingToFlags(ProtectionSetti
     switch (protection) {
       case ProtectionSetting::Protected:  return PROT_NONE;
       case ProtectionSetting::Writable:   return PROT_READ | PROT_WRITE;
       case ProtectionSetting::Executable: return PROT_READ | PROT_EXEC;
     }
     MOZ_CRASH();
 }
 
-static void
+static MOZ_MUST_USE bool
 CommitPages(void* addr, size_t bytes, ProtectionSetting protection)
 {
     void* p = MozTaggedAnonymousMmap(addr, bytes, ProtectionSettingToFlags(protection),
                                      MAP_FIXED | MAP_PRIVATE | MAP_ANON,
                                      -1, 0, "js-executable-memory");
-    MOZ_RELEASE_ASSERT(addr == p);
+    if (p == MAP_FAILED)
+        return false;
+    MOZ_RELEASE_ASSERT(p == addr);
+    return true;
 }
 
 static void
 DecommitPages(void* addr, size_t bytes)
 {
     // Use mmap with MAP_FIXED and PROT_NONE. Inspired by jemalloc's
     // pages_decommit.
     void* p = MozTaggedAnonymousMmap(addr, bytes, PROT_NONE,
@@ -489,17 +495,17 @@ class ProcessExecutableMemory
     }
 
     void assertValidAddress(void* p, size_t bytes) const {
         MOZ_RELEASE_ASSERT(p >= base_ &&
                            uintptr_t(p) + bytes <= uintptr_t(base_) + MaxCodeBytesPerProcess);
     }
 
     void* allocate(size_t bytes, ProtectionSetting protection);
-    void deallocate(void* addr, size_t bytes);
+    void deallocate(void* addr, size_t bytes, bool decommit);
 };
 
 void*
 ProcessExecutableMemory::allocate(size_t bytes, ProtectionSetting protection)
 {
     MOZ_ASSERT(initialized());
     MOZ_ASSERT(bytes > 0);
     MOZ_ASSERT((bytes % ExecutableCodePageSize) == 0);
@@ -554,36 +560,41 @@ ProcessExecutableMemory::allocate(size_t
             p = base_ + page * ExecutableCodePageSize;
             break;
         }
         if (!p)
             return nullptr;
     }
 
     // Commit the pages after releasing the lock.
-    CommitPages(p, bytes, protection);
+    if (!CommitPages(p, bytes, protection)) {
+        deallocate(p, bytes, /* decommit = */ false);
+        return nullptr;
+    }
+
     return p;
 }
 
 void
-ProcessExecutableMemory::deallocate(void* addr, size_t bytes)
+ProcessExecutableMemory::deallocate(void* addr, size_t bytes, bool decommit)
 {
     MOZ_ASSERT(initialized());
     MOZ_ASSERT(addr);
     MOZ_ASSERT((uintptr_t(addr) % gc::SystemPageSize()) == 0);
     MOZ_ASSERT(bytes > 0);
     MOZ_ASSERT((bytes % ExecutableCodePageSize) == 0);
 
     assertValidAddress(addr, bytes);
 
     size_t firstPage = (static_cast<uint8_t*>(addr) - base_) / ExecutableCodePageSize;
     size_t numPages = bytes / ExecutableCodePageSize;
 
     // Decommit before taking the lock.
-    DecommitPages(addr, bytes);
+    if (decommit)
+        DecommitPages(addr, bytes);
 
     LockGuard<Mutex> guard(lock_);
     MOZ_ASSERT(numPages <= pagesAllocated_);
     pagesAllocated_ -= numPages;
 
     for (size_t i = 0; i < numPages; i++)
         pages_.remove(firstPage + i);
 
@@ -599,17 +610,17 @@ void*
 js::jit::AllocateExecutableMemory(size_t bytes, ProtectionSetting protection)
 {
     return execMemory.allocate(bytes, protection);
 }
 
 void
 js::jit::DeallocateExecutableMemory(void* addr, size_t bytes)
 {
-    execMemory.deallocate(addr, bytes);
+    execMemory.deallocate(addr, bytes, /* decommit = */ true);
 }
 
 bool
 js::jit::InitProcessExecutableMemory()
 {
     return execMemory.init();
 }