Bug 1329499 - Part 2: Keep outside actors from messing with our old buffer during realloc. r=jandem
authorEmanuel Hoogeveen <emanuel.hoogeveen@gmail.com>
Mon, 09 Jan 2017 08:32:00 -0500
changeset 328652 c89eb360f419e9e688aa08604e7a2c088aae62b3
parent 328651 e9e9e07a5afbae19a8470f2a8dacacfc4a2184fd
child 328653 aae1f858f6deca2d9c1cfb78229c24a886aefe45
push id31182
push usercbook@mozilla.com
push dateTue, 10 Jan 2017 11:14:56 +0000
treeherdermozilla-central@7011ed1427de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1329499
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1329499 - Part 2: Keep outside actors from messing with our old buffer during realloc. r=jandem
js/src/ds/PageProtectingVector.h
js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
--- a/js/src/ds/PageProtectingVector.h
+++ b/js/src/ds/PageProtectingVector.h
@@ -7,16 +7,17 @@
 #ifndef ds_PageProtectingVector_h
 #define ds_PageProtectingVector_h
 
 #include "mozilla/Atomics.h"
 #include "mozilla/Vector.h"
 
 #include "ds/MemoryProtectionExceptionHandler.h"
 #include "gc/Memory.h"
+#include "js/Utility.h"
 
 namespace js {
 
 /*
  * PageProtectingVector is a vector that can only grow or be cleared, restricts
  * access to memory pages that haven't been used yet, and marks all of its fully
  * used memory pages as read-only. It can be used to detect heap corruption in
  * important buffers, since anything that tries to write into its protected
@@ -531,11 +532,71 @@ PageProtectingVector<T, N, A, P, Q, G, D
 
 template<typename T, size_t N, class A, bool P, bool Q, bool G, bool D, size_t I, uint8_t X>
 MOZ_NEVER_INLINE void
 PageProtectingVector<T, N, A, P, Q, G, D, I, X>::lockSlow() const
 {
     MOZ_CRASH("Cannot access PageProtectingVector from more than one thread at a time!");
 }
 
+class ProtectedReallocPolicy
+{
+    /* We hardcode the page size here to minimize administrative overhead. */
+    static const size_t pageShift = 12;
+    static const size_t pageSize = 1 << pageShift;
+    static const size_t pageMask = pageSize - 1;
+
+  public:
+    template <typename T> T* maybe_pod_malloc(size_t numElems) {
+        return js_pod_malloc<T>(numElems);
+    }
+    template <typename T> T* maybe_pod_calloc(size_t numElems) {
+        return js_pod_calloc<T>(numElems);
+    }
+    template <typename T> T* maybe_pod_realloc(T* oldAddr, size_t oldSize, size_t newSize) {
+        MOZ_ASSERT_IF(oldAddr, oldSize);
+        MOZ_ASSERT(gc::SystemPageSize() == pageSize);
+        if (MOZ_UNLIKELY(!newSize))
+            return nullptr;
+        if (MOZ_UNLIKELY(!oldAddr))
+            return js_pod_malloc<T>(newSize);
+
+        T* newAddr = nullptr;
+        size_t initPage = (uintptr_t(oldAddr - 1) >> pageShift) + 1;
+        size_t lastPage = (uintptr_t(oldAddr + oldSize) >> pageShift) - 1;
+        size_t toCopy = (newSize >= oldSize ? oldSize : newSize) * sizeof(T);
+        if (MOZ_UNLIKELY(oldSize >= 32 * 1024 && lastPage >= initPage)) {
+            T* protectAddr = reinterpret_cast<T*>(initPage << pageShift);
+            size_t protectSize = (lastPage - initPage + 1) << pageShift;
+            MemoryProtectionExceptionHandler::addRegion(protectAddr, protectSize);
+            gc::MakePagesReadOnly(protectAddr, protectSize);
+            newAddr = js_pod_malloc<T>(newSize);
+            if (MOZ_LIKELY(newAddr))
+                memcpy(newAddr, oldAddr, toCopy);
+            gc::UnprotectPages(protectAddr, protectSize);
+            MemoryProtectionExceptionHandler::removeRegion(protectAddr);
+            if (MOZ_LIKELY(newAddr))
+                js_free(oldAddr);
+        } else {
+            newAddr = js_pod_malloc<T>(newSize);
+            if (MOZ_LIKELY(newAddr)) {
+                memcpy(newAddr, oldAddr, toCopy);
+                js_free(oldAddr);
+            }
+        }
+        return newAddr;
+    }
+
+    template <typename T> T* pod_malloc(size_t numElems) { return maybe_pod_malloc<T>(numElems); }
+    template <typename T> T* pod_calloc(size_t numElems) { return maybe_pod_calloc<T>(numElems); }
+    template <typename T> T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
+        return maybe_pod_realloc<T>(p, oldSize, newSize);
+    }
+    void free_(void* p) { js_free(p); }
+    void reportAllocOverflow() const {}
+    bool checkSimulatedOOM() const {
+        return !js::oom::ShouldFailWithOOM();
+    }
+};
+
 } /* namespace js */
 
 #endif /* ds_PageProtectingVector_h */
--- a/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
+++ b/js/src/jit/x86-shared/AssemblerBuffer-x86-shared.h
@@ -181,17 +181,17 @@ namespace jit {
          * See also the |buffer| method.
          */
         void oomDetected() {
             m_oom = true;
             m_buffer.clear();
         }
 
 #ifndef RELEASE_OR_BETA
-        PageProtectingVector<unsigned char, 256, SystemAllocPolicy,
+        PageProtectingVector<unsigned char, 256, ProtectedReallocPolicy,
                              /* ProtectUsed = */ false, /* ProtectUnused = */ false,
                              /* GuardAgainstReentrancy = */ true, /* DetectPoison = */ true,
                              /* InitialLowerBound = */ 32 * 1024> m_buffer;
 #else
         mozilla::Vector<unsigned char, 256, SystemAllocPolicy> m_buffer;
 #endif
         bool m_oom;
     };