Title: [222058] trunk/Source
Revision
222058
Author
[email protected]
Date
2017-09-14 16:08:54 -0700 (Thu, 14 Sep 2017)

Log Message

AddressSanitizer: stack-buffer-underflow in JSC::Probe::Page::Page
https://bugs.webkit.org/show_bug.cgi?id=176874
<rdar://problem/34436415>

Reviewed by Saam Barati.

Source/_javascript_Core:

1. Make Probe::Stack play nice with ASan by:

   a. using a local memcpy implementation that suppresses ASan on ASan builds.
      We don't want to use std:memcpy() which validates stack memory because
      we are intentionally copying stack memory beyond the current frame.

   b. changing Stack::s_chunkSize to equal sizeof(uintptr_t) on ASan builds.
      This ensures that Page::flushWrites() only writes stack memory that was
      modified by a probe.  The probes should only modify stack memory that
      belongs to JSC stack data structures.  We don't want to inadvertently
      modify adjacent words that may belong to ASan (which may happen if
      s_chunkSize is larger than sizeof(uintptr_t)).

   c. fixing a bug in Page dirtyBits management for when the size of the value to
      write is greater than s_chunkSize.  The fix in generic, but in practice,
      this currently only manifests on 32-bit ASan builds because
      sizeof(uintptr_t) and s_chunkSize are 32-bit, and we may write 64-bit
      values.

   d. making Page::m_dirtyBits 64 bits always.  This maximizes the number of
      s_chunksPerPage we can have even on ASan builds.

2. Fixed the bottom most Probe::Context and Probe::Stack get/set methods to use
   std::memcpy to avoid strict aliasing issues.

3. Optimized the implementation of Page::physicalAddressFor().

4. Optimized the implementation of Stack::set() in the recording of the low
   watermark.  We just record the lowest raw pointer now, and only compute the
   alignment to its chuck boundary later when the low watermark is requested.

5. Changed a value in testmasm to make the test less vulnerable to rounding issues.

No new test needed because this is already covered by testmasm with ASan enabled.

* assembler/ProbeContext.h:
(JSC::Probe::CPUState::gpr const):
(JSC::Probe::CPUState::spr const):
(JSC::Probe::Context::gpr):
(JSC::Probe::Context::spr):
(JSC::Probe::Context::fpr):
(JSC::Probe::Context::gprName):
(JSC::Probe::Context::sprName):
(JSC::Probe::Context::fprName):
(JSC::Probe::Context::gpr const):
(JSC::Probe::Context::spr const):
(JSC::Probe::Context::fpr const):
(JSC::Probe::Context::pc):
(JSC::Probe::Context::fp):
(JSC::Probe::Context::sp):
(JSC::Probe:: const): Deleted.
* assembler/ProbeStack.cpp:
(JSC::Probe::copyStackPage):
(JSC::Probe::Page::Page):
(JSC::Probe::Page::flushWrites):
* assembler/ProbeStack.h:
(JSC::Probe::Page::get):
(JSC::Probe::Page::set):
(JSC::Probe::Page::dirtyBitFor):
(JSC::Probe::Page::physicalAddressFor):
(JSC::Probe::Stack::lowWatermark):
(JSC::Probe::Stack::get):
(JSC::Probe::Stack::set):
* assembler/testmasm.cpp:
(JSC::testProbeModifiesStackValues):

Source/WTF:

Added a convenience version of roundUpToMultipleOf() so that it can be applied to
pointers without the client having to cast explicitly.

* wtf/StdLibExtras.h:
(WTF::roundUpToMultipleOf):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (222057 => 222058)


--- trunk/Source/_javascript_Core/ChangeLog	2017-09-14 22:43:45 UTC (rev 222057)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-09-14 23:08:54 UTC (rev 222058)
@@ -1,3 +1,77 @@
+2017-09-14  Mark Lam  <[email protected]>
+
+        AddressSanitizer: stack-buffer-underflow in JSC::Probe::Page::Page
+        https://bugs.webkit.org/show_bug.cgi?id=176874
+        <rdar://problem/34436415>
+
+        Reviewed by Saam Barati.
+
+        1. Make Probe::Stack play nice with ASan by:
+
+           a. using a local memcpy implementation that suppresses ASan on ASan builds.
+              We don't want to use std:memcpy() which validates stack memory because
+              we are intentionally copying stack memory beyond the current frame.
+
+           b. changing Stack::s_chunkSize to equal sizeof(uintptr_t) on ASan builds.
+              This ensures that Page::flushWrites() only writes stack memory that was
+              modified by a probe.  The probes should only modify stack memory that
+              belongs to JSC stack data structures.  We don't want to inadvertently
+              modify adjacent words that may belong to ASan (which may happen if
+              s_chunkSize is larger than sizeof(uintptr_t)).
+
+           c. fixing a bug in Page dirtyBits management for when the size of the value to
+              write is greater than s_chunkSize.  The fix in generic, but in practice,
+              this currently only manifests on 32-bit ASan builds because
+              sizeof(uintptr_t) and s_chunkSize are 32-bit, and we may write 64-bit
+              values.
+
+           d. making Page::m_dirtyBits 64 bits always.  This maximizes the number of
+              s_chunksPerPage we can have even on ASan builds.
+
+        2. Fixed the bottom most Probe::Context and Probe::Stack get/set methods to use
+           std::memcpy to avoid strict aliasing issues.
+
+        3. Optimized the implementation of Page::physicalAddressFor().
+
+        4. Optimized the implementation of Stack::set() in the recording of the low
+           watermark.  We just record the lowest raw pointer now, and only compute the
+           alignment to its chuck boundary later when the low watermark is requested.
+
+        5. Changed a value in testmasm to make the test less vulnerable to rounding issues.
+
+        No new test needed because this is already covered by testmasm with ASan enabled.
+
+        * assembler/ProbeContext.h:
+        (JSC::Probe::CPUState::gpr const):
+        (JSC::Probe::CPUState::spr const):
+        (JSC::Probe::Context::gpr):
+        (JSC::Probe::Context::spr):
+        (JSC::Probe::Context::fpr):
+        (JSC::Probe::Context::gprName):
+        (JSC::Probe::Context::sprName):
+        (JSC::Probe::Context::fprName):
+        (JSC::Probe::Context::gpr const):
+        (JSC::Probe::Context::spr const):
+        (JSC::Probe::Context::fpr const):
+        (JSC::Probe::Context::pc):
+        (JSC::Probe::Context::fp):
+        (JSC::Probe::Context::sp):
+        (JSC::Probe:: const): Deleted.
+        * assembler/ProbeStack.cpp:
+        (JSC::Probe::copyStackPage):
+        (JSC::Probe::Page::Page):
+        (JSC::Probe::Page::flushWrites):
+        * assembler/ProbeStack.h:
+        (JSC::Probe::Page::get):
+        (JSC::Probe::Page::set):
+        (JSC::Probe::Page::dirtyBitFor):
+        (JSC::Probe::Page::physicalAddressFor):
+        (JSC::Probe::Stack::lowWatermark):
+        (JSC::Probe::Stack::get):
+        (JSC::Probe::Stack::set):
+        * assembler/testmasm.cpp:
+        (JSC::testProbeModifiesStackValues):
+
 2017-09-14  Yusuke Suzuki  <[email protected]>
 
         [JSC] Disable Arity Fixup Inlining until crash in facebook.com is fixed

Modified: trunk/Source/_javascript_Core/assembler/ProbeContext.h (222057 => 222058)


--- trunk/Source/_javascript_Core/assembler/ProbeContext.h	2017-09-14 22:43:45 UTC (rev 222057)
+++ trunk/Source/_javascript_Core/assembler/ProbeContext.h	2017-09-14 23:08:54 UTC (rev 222058)
@@ -45,14 +45,8 @@
     inline uintptr_t& spr(SPRegisterID);
     inline double& fpr(FPRegisterID);
 
-    template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-    T gpr(RegisterID) const;
-    template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr>
-    T gpr(RegisterID) const;
-    template<typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
-    T spr(SPRegisterID) const;
-    template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr>
-    T spr(SPRegisterID) const;
+    template<typename T> T gpr(RegisterID) const;
+    template<typename T> T spr(SPRegisterID) const;
     template<typename T> T fpr(FPRegisterID) const;
 
     void*& pc();
@@ -85,34 +79,26 @@
     return fprs[id];
 }
 
-template<typename T, typename std::enable_if<std::is_integral<T>::value>::type*>
+template<typename T>
 T CPUState::gpr(RegisterID id) const
 {
     CPUState* cpu = const_cast<CPUState*>(this);
-    return static_cast<T>(cpu->gpr(id));
+    auto& from = cpu->gpr(id);
+    typename std::remove_const<T>::type to { };
+    std::memcpy(&to, &from, sizeof(to)); // Use std::memcpy to avoid strict aliasing issues.
+    return to;
 }
 
-template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type*>
-T CPUState::gpr(RegisterID id) const
-{
-    CPUState* cpu = const_cast<CPUState*>(this);
-    return reinterpret_cast<T>(cpu->gpr(id));
-}
-
-template<typename T, typename std::enable_if<std::is_integral<T>::value>::type*>
+template<typename T>
 T CPUState::spr(SPRegisterID id) const
 {
     CPUState* cpu = const_cast<CPUState*>(this);
-    return static_cast<T>(cpu->spr(id));
+    auto& from = cpu->spr(id);
+    typename std::remove_const<T>::type to { };
+    std::memcpy(&to, &from, sizeof(to)); // Use std::memcpy to avoid strict aliasing issues.
+    return to;
 }
 
-template<typename T, typename std::enable_if<std::is_pointer<T>::value>::type*>
-T CPUState::spr(SPRegisterID id) const
-{
-    CPUState* cpu = const_cast<CPUState*>(this);
-    return reinterpret_cast<T>(cpu->spr(id));
-}
-
 template<typename T>
 T CPUState::fpr(FPRegisterID id) const
 {
@@ -210,21 +196,25 @@
         , cpu(state->cpu)
     { }
 
-    uintptr_t& gpr(RegisterID id) { return m_state->cpu.gpr(id); }
-    uintptr_t& spr(SPRegisterID id) { return m_state->cpu.spr(id); }
-    double& fpr(FPRegisterID id) { return m_state->cpu.fpr(id); }
-    const char* gprName(RegisterID id) { return m_state->cpu.gprName(id); }
-    const char* sprName(SPRegisterID id) { return m_state->cpu.sprName(id); }
-    const char* fprName(FPRegisterID id) { return m_state->cpu.fprName(id); }
+    uintptr_t& gpr(RegisterID id) { return cpu.gpr(id); }
+    uintptr_t& spr(SPRegisterID id) { return cpu.spr(id); }
+    double& fpr(FPRegisterID id) { return cpu.fpr(id); }
+    const char* gprName(RegisterID id) { return cpu.gprName(id); }
+    const char* sprName(SPRegisterID id) { return cpu.sprName(id); }
+    const char* fprName(FPRegisterID id) { return cpu.fprName(id); }
 
-    void*& pc() { return m_state->cpu.pc(); }
-    void*& fp() { return m_state->cpu.fp(); }
-    void*& sp() { return m_state->cpu.sp(); }
+    template<typename T> T gpr(RegisterID id) const { return cpu.gpr<T>(id); }
+    template<typename T> T spr(SPRegisterID id) const { return cpu.spr<T>(id); }
+    template<typename T> T fpr(FPRegisterID id) const { return cpu.fpr<T>(id); }
 
-    template<typename T> T pc() { return m_state->cpu.pc<T>(); }
-    template<typename T> T fp() { return m_state->cpu.fp<T>(); }
-    template<typename T> T sp() { return m_state->cpu.sp<T>(); }
+    void*& pc() { return cpu.pc(); }
+    void*& fp() { return cpu.fp(); }
+    void*& sp() { return cpu.sp(); }
 
+    template<typename T> T pc() { return cpu.pc<T>(); }
+    template<typename T> T fp() { return cpu.fp<T>(); }
+    template<typename T> T sp() { return cpu.sp<T>(); }
+
     Stack& stack()
     {
         ASSERT(m_stack.isValid());

Modified: trunk/Source/_javascript_Core/assembler/ProbeStack.cpp (222057 => 222058)


--- trunk/Source/_javascript_Core/assembler/ProbeStack.cpp	2017-09-14 22:43:45 UTC (rev 222057)
+++ trunk/Source/_javascript_Core/assembler/ProbeStack.cpp	2017-09-14 23:08:54 UTC (rev 222058)
@@ -27,6 +27,7 @@
 #include "ProbeStack.h"
 
 #include <memory>
+#include <wtf/StdLibExtras.h>
 
 #if ENABLE(MASM_PROBE)
 
@@ -33,15 +34,34 @@
 namespace JSC {
 namespace Probe {
 
+#if ASAN_ENABLED
+// FIXME: we should consider using the copy function for both ASan and non-ASan builds.
+// https://bugs.webkit.org/show_bug.cgi?id=176961
+SUPPRESS_ASAN
+static void copyStackPage(void* dst, void* src, size_t size)
+{
+    ASSERT(roundUpToMultipleOf<sizeof(uintptr_t)>(dst) == dst);
+    ASSERT(roundUpToMultipleOf<sizeof(uintptr_t)>(src) == src);
+    
+    uintptr_t* dstPointer = reinterpret_cast<uintptr_t*>(dst);
+    uintptr_t* srcPointer = reinterpret_cast<uintptr_t*>(src);
+    for (; size; size -= sizeof(uintptr_t))
+        *dstPointer++ = *srcPointer++;
+}
+#else
+#define copyStackPage(dst, src, size) std::memcpy(dst, src, size);
+#endif
+
 Page::Page(void* baseAddress)
     : m_baseLogicalAddress(baseAddress)
+    , m_physicalAddressOffset(reinterpret_cast<uint8_t*>(&m_buffer) - reinterpret_cast<uint8_t*>(baseAddress))
 {
-    memcpy(&m_buffer, baseAddress, s_pageSize);
+    copyStackPage(&m_buffer, baseAddress, s_pageSize);
 }
 
 void Page::flushWrites()
 {
-    uintptr_t dirtyBits = m_dirtyBits;
+    uint64_t dirtyBits = m_dirtyBits;
     size_t offset = 0;
     while (dirtyBits) {
         // Find start.
@@ -56,7 +76,7 @@
             size_t size = offset - startOffset;
             uint8_t* src = "" + startOffset;
             uint8_t* dst = reinterpret_cast<uint8_t*>(m_baseLogicalAddress) + startOffset;
-            memcpy(dst, src, size);
+            copyStackPage(dst, src, size);
         }
         dirtyBits = dirtyBits >> 1;
         offset += s_chunkSize;

Modified: trunk/Source/_javascript_Core/assembler/ProbeStack.h (222057 => 222058)


--- trunk/Source/_javascript_Core/assembler/ProbeStack.h	2017-09-14 22:43:45 UTC (rev 222057)
+++ trunk/Source/_javascript_Core/assembler/ProbeStack.h	2017-09-14 23:08:54 UTC (rev 222058)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "CPU.h"
 #include <wtf/HashMap.h>
 #include <wtf/StdLibExtras.h>
 #include <wtf/Threading.h>
@@ -56,15 +57,36 @@
     template<typename T>
     T get(void* logicalAddress)
     {
-        return *physicalAddressFor<T*>(logicalAddress);
+        void* from = physicalAddressFor(logicalAddress);
+        typename std::remove_const<T>::type to { };
+        std::memcpy(&to, from, sizeof(to)); // Use std::memcpy to avoid strict aliasing issues.
+        return to;
     }
+    template<typename T>
+    T get(void* logicalBaseAddress, ptrdiff_t offset)
+    {
+        return get<T>(reinterpret_cast<uint8_t*>(logicalBaseAddress) + offset);
+    }
 
     template<typename T>
     void set(void* logicalAddress, T value)
     {
-        m_dirtyBits |= dirtyBitFor(logicalAddress);
-        *physicalAddressFor<T*>(logicalAddress) = value;
+        if (sizeof(T) <= s_chunkSize)
+            m_dirtyBits |= dirtyBitFor(logicalAddress);
+        else {
+            size_t numberOfChunks = roundUpToMultipleOf<sizeof(T)>(s_chunkSize) / s_chunkSize;
+            uint8_t* dirtyAddress = reinterpret_cast<uint8_t*>(logicalAddress);
+            for (size_t i = 0; i < numberOfChunks; ++i, dirtyAddress += s_chunkSize)
+                m_dirtyBits |= dirtyBitFor(dirtyAddress);
+        }
+        void* to = physicalAddressFor(logicalAddress);
+        std::memcpy(to, &value, sizeof(T)); // Use std::memcpy to avoid strict aliasing issues.
     }
+    template<typename T>
+    void set(void* logicalBaseAddress, ptrdiff_t offset, T value)
+    {
+        set<T>(reinterpret_cast<uint8_t*>(logicalBaseAddress) + offset, value);
+    }
 
     bool hasWritesToFlush() const { return !!m_dirtyBits; }
     void flushWritesIfNeeded()
@@ -74,39 +96,48 @@
     }
 
 private:
-    uintptr_t dirtyBitFor(void* logicalAddress)
+    uint64_t dirtyBitFor(void* logicalAddress)
     {
         uintptr_t offset = reinterpret_cast<uintptr_t>(logicalAddress) & s_pageMask;
-        return static_cast<uintptr_t>(1) << (offset >> s_chunkSizeShift);
+        return static_cast<uint64_t>(1) << (offset >> s_chunkSizeShift);
     }
 
-    template<typename T, typename = typename std::enable_if<std::is_pointer<T>::value>::type>
-    T physicalAddressFor(void* logicalAddress)
+    void* physicalAddressFor(void* logicalAddress)
     {
-        uintptr_t offset = reinterpret_cast<uintptr_t>(logicalAddress) & s_pageMask;
-        void* physicalAddress = reinterpret_cast<uint8_t*>(&m_buffer) + offset;
-        return reinterpret_cast<T>(physicalAddress);
+        return reinterpret_cast<uint8_t*>(logicalAddress) + m_physicalAddressOffset;
     }
 
     void flushWrites();
 
     void* m_baseLogicalAddress { nullptr };
-    uintptr_t m_dirtyBits { 0 };
+    ptrdiff_t m_physicalAddressOffset;
+    uint64_t m_dirtyBits { 0 };
 
+#if ASAN_ENABLED
+    // The ASan stack may contain poisoned words that may be manipulated at ASan's discretion.
+    // We would never touch those words anyway, but let's ensure that the page size is set
+    // such that the chunk size is guaranteed to be exactly sizeof(uintptr_t) so that we won't
+    // inadvertently overwrite one of ASan's words on the stack when we copy back the dirty
+    // chunks.
+    // FIXME: we should consider using the same page size for both ASan and non-ASan builds.
+    // https://bugs.webkit.org/show_bug.cgi?id=176961
+    static constexpr size_t s_pageSize = 64 * sizeof(uintptr_t); // because there are 64 bits in m_dirtyBits.
+#else // not ASAN_ENABLED
     static constexpr size_t s_pageSize = 1024;
+#endif // ASAN_ENABLED
     static constexpr uintptr_t s_pageMask = s_pageSize - 1;
-    static constexpr size_t s_chunksPerPage = sizeof(uintptr_t) * 8; // sizeof(m_dirtyBits) in bits.
+    static constexpr size_t s_chunksPerPage = sizeof(uint64_t) * 8; // number of bits in m_dirtyBits.
     static constexpr size_t s_chunkSize = s_pageSize / s_chunksPerPage;
     static constexpr uintptr_t s_chunkMask = s_chunkSize - 1;
-#if USE(JSVALUE64)
+#if ASAN_ENABLED
+    static_assert(s_chunkSize == sizeof(uintptr_t), "bad chunkSizeShift");
+    static constexpr size_t s_chunkSizeShift = is64Bit() ? 3 : 2;
+#else // no ASAN_ENABLED
     static constexpr size_t s_chunkSizeShift = 4;
-#else
-    static constexpr size_t s_chunkSizeShift = 5;
-#endif
+#endif // ASAN_ENABLED
     static_assert(s_pageSize > s_chunkSize, "bad pageSize or chunkSize");
     static_assert(s_chunkSize == (1 << s_chunkSizeShift), "bad chunkSizeShift");
 
-
     typedef typename std::aligned_storage<s_pageSize, std::alignment_of<uintptr_t>::value>::type Buffer;
     Buffer m_buffer;
 };
@@ -120,42 +151,42 @@
     { }
     Stack(Stack&& other);
 
-    void* lowWatermark() { return m_lowWatermark; }
+    void* lowWatermark()
+    {
+        // We use the chunkAddress for the low watermark because we'll be doing write backs
+        // to the stack in increments of chunks. Hence, we'll treat the lowest address of
+        // the chunk as the low watermark of any given set address.
+        return Page::chunkAddressFor(m_lowWatermark);
+    }
 
     template<typename T>
-    typename std::enable_if<!std::is_same<double, typename std::remove_cv<T>::type>::value, T>::type get(void* address)
+    T get(void* address)
     {
         Page* page = pageFor(address);
         return page->get<T>(address);
     }
+    template<typename T>
+    T get(void* logicalBaseAddress, ptrdiff_t offset)
+    {
+        return get<T>(reinterpret_cast<uint8_t*>(logicalBaseAddress) + offset);
+    }
 
-    template<typename T, typename = typename std::enable_if<!std::is_same<double, typename std::remove_cv<T>::type>::value>::type>
+    template<typename T>
     void set(void* address, T value)
     {
         Page* page = pageFor(address);
         page->set<T>(address, value);
 
-        // We use the chunkAddress for the low watermark because we'll be doing write backs
-        // to the stack in increments of chunks. Hence, we'll treat the lowest address of
-        // the chunk as the low watermark of any given set address.
-        void* chunkAddress = Page::chunkAddressFor(address);
-        if (chunkAddress < m_lowWatermark)
-            m_lowWatermark = chunkAddress;
+        if (address < m_lowWatermark)
+            m_lowWatermark = address;
     }
 
     template<typename T>
-    typename std::enable_if<std::is_same<double, typename std::remove_cv<T>::type>::value, T>::type get(void* address)
+    void set(void* logicalBaseAddress, ptrdiff_t offset, T value)
     {
-        Page* page = pageFor(address);
-        return bitwise_cast<double>(page->get<uint64_t>(address));
+        set<T>(reinterpret_cast<uint8_t*>(logicalBaseAddress) + offset, value);
     }
 
-    template<typename T, typename = typename std::enable_if<std::is_same<double, typename std::remove_cv<T>::type>::value>::type>
-    void set(void* address, double value)
-    {
-        set<uint64_t>(address, bitwise_cast<uint64_t>(value));
-    }
-
     JS_EXPORT_PRIVATE Page* ensurePageFor(void* address);
 
     void* newStackPointer() const { return m_newStackPointer; };

Modified: trunk/Source/_javascript_Core/assembler/testmasm.cpp (222057 => 222058)


--- trunk/Source/_javascript_Core/assembler/testmasm.cpp	2017-09-14 22:43:45 UTC (rev 222057)
+++ trunk/Source/_javascript_Core/assembler/testmasm.cpp	2017-09-14 23:08:54 UTC (rev 222058)
@@ -600,7 +600,7 @@
             // Fill the stack with values.
             uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
             int count = 0;
-            stack.set<double>(p++, 1.23456789);
+            stack.set<double>(p++, 1.234567);
             if (is32Bit())
                 p++; // On 32-bit targets, a double takes up 2 uintptr_t.
             while (p < reinterpret_cast<uintptr_t*>(originalSP))
@@ -631,7 +631,7 @@
             // Validate the stack values.
             uintptr_t* p = reinterpret_cast<uintptr_t*>(newSP);
             int count = 0;
-            CHECK_EQ(stack.get<double>(p++), 1.23456789);
+            CHECK_EQ(stack.get<double>(p++), 1.234567);
             if (is32Bit())
                 p++; // On 32-bit targets, a double takes up 2 uintptr_t.
             while (p < reinterpret_cast<uintptr_t*>(originalSP))

Modified: trunk/Source/WTF/ChangeLog (222057 => 222058)


--- trunk/Source/WTF/ChangeLog	2017-09-14 22:43:45 UTC (rev 222057)
+++ trunk/Source/WTF/ChangeLog	2017-09-14 23:08:54 UTC (rev 222058)
@@ -1,3 +1,17 @@
+2017-09-14  Mark Lam  <[email protected]>
+
+        AddressSanitizer: stack-buffer-underflow in JSC::Probe::Page::Page
+        https://bugs.webkit.org/show_bug.cgi?id=176874
+        <rdar://problem/34436415>
+
+        Reviewed by Saam Barati.
+
+        Added a convenience version of roundUpToMultipleOf() so that it can be applied to
+        pointers without the client having to cast explicitly.
+
+        * wtf/StdLibExtras.h:
+        (WTF::roundUpToMultipleOf):
+
 2017-09-14  Youenn Fablet  <[email protected]>
 
         Allow WTF::map to take function as parameter

Modified: trunk/Source/WTF/wtf/StdLibExtras.h (222057 => 222058)


--- trunk/Source/WTF/wtf/StdLibExtras.h	2017-09-14 22:43:45 UTC (rev 222057)
+++ trunk/Source/WTF/wtf/StdLibExtras.h	2017-09-14 23:08:54 UTC (rev 222058)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2016 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008-2017 Apple Inc. All Rights Reserved.
  * Copyright (C) 2013 Patrick Gansterer <[email protected]>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -204,6 +204,12 @@
     return roundUpToMultipleOfImpl(divisor, x);
 }
 
+template<size_t divisor, typename T> inline T* roundUpToMultipleOf(T* x)
+{
+    static_assert(sizeof(T*) == sizeof(size_t), "");
+    return reinterpret_cast<T*>(roundUpToMultipleOf<divisor>(reinterpret_cast<size_t>(x)));
+}
+
 enum BinarySearchMode {
     KeyMustBePresentInArray,
     KeyMightNotBePresentInArray,
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to