⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 180701 in webkit


Ignore:
Timestamp:
Feb 26, 2015, 2:24:37 PM (12 years ago)
Author:
ggaren@apple.com
Message:

bmalloc: Large object free list can grow infinitely
https://bugs.webkit.org/show_bug.cgi?id=142055

Reviewed by Andreas Kling.

By design, we don't eagerly remove large objects from the free list.
This creates two simple pathologies:

(1) If you free and then allocate the same object repeatedly, it will
duplicate itself in the free list repeatedly. Since it is never
invalid at the time of allocation, it will never be removed.

(2) If you split and then merge the same object repeatedly, it will
duplicate its split sibling in the free list repeatedly. If its
sibling is in a separate free list size class, it will never be
consulted at the time of allocation, so it will never be removed.

So, a simple "while (1) { free(malloc(x)); }" causes infinite memory
use in the free list.

The solution in this patch is a simple helper to remove garbage from the
free list if it grows too large. This pathology is not common, so the
cost is OK.

Long-term, perhaps we should rethink the laziness of these free lists.

  • bmalloc/BoundaryTag.h:

(bmalloc::BoundaryTag::isMarked):
(bmalloc::BoundaryTag::setMarked): New bit, used by free list GC.

  • bmalloc/FreeList.cpp:

(bmalloc::FreeList::removeInvalidAndDuplicateEntries): The GC algorithm.

  • bmalloc/FreeList.h:

(bmalloc::FreeList::FreeList):
(bmalloc::FreeList::push): Invoke the GC if we're getting huge.

  • bmalloc/LargeObject.h:

(bmalloc::LargeObject::isMarked):
(bmalloc::LargeObject::setMarked):
(bmalloc::LargeObject::validateSelf): Expose the new bit.

  • bmalloc/Sizes.h: New constant to control GC frequency.
Location:
trunk/Source/bmalloc
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/bmalloc/ChangeLog

    r180695 r180701  
     12015-02-26  Geoffrey Garen  <ggaren@apple.com>
     2
     3        bmalloc: Large object free list can grow infinitely
     4        https://bugs.webkit.org/show_bug.cgi?id=142055
     5
     6        Reviewed by Andreas Kling.
     7
     8        By design, we don't eagerly remove large objects from the free list.
     9        This creates two simple pathologies:
     10
     11            (1) If you free and then allocate the same object repeatedly, it will
     12            duplicate itself in the free list repeatedly. Since it is never
     13            invalid at the time of allocation, it will never be removed.
     14
     15            (2) If you split and then merge the same object repeatedly, it will
     16            duplicate its split sibling in the free list repeatedly. If its
     17            sibling is in a separate free list size class, it will never be
     18            consulted at the time of allocation, so it will never be removed.
     19
     20        So, a simple "while (1) { free(malloc(x)); }" causes infinite memory
     21        use in the free list.
     22
     23        The solution in this patch is a simple helper to remove garbage from the
     24        free list if it grows too large. This pathology is not common, so the
     25        cost is OK.
     26
     27        Long-term, perhaps we should rethink the laziness of these free lists.
     28
     29        * bmalloc/BoundaryTag.h:
     30        (bmalloc::BoundaryTag::isMarked):
     31        (bmalloc::BoundaryTag::setMarked): New bit, used by free list GC.
     32
     33        * bmalloc/FreeList.cpp:
     34        (bmalloc::FreeList::removeInvalidAndDuplicateEntries): The GC algorithm.
     35
     36        * bmalloc/FreeList.h:
     37        (bmalloc::FreeList::FreeList):
     38        (bmalloc::FreeList::push): Invoke the GC if we're getting huge.
     39
     40        * bmalloc/LargeObject.h:
     41        (bmalloc::LargeObject::isMarked):
     42        (bmalloc::LargeObject::setMarked):
     43        (bmalloc::LargeObject::validateSelf): Expose the new bit.
     44
     45        * bmalloc/Sizes.h: New constant to control GC frequency.
     46
    1472015-02-26  Csaba Osztrogonác  <ossy@webkit.org>
    248
  • trunk/Source/bmalloc/bmalloc/BoundaryTag.h

    r180688 r180701  
    5252    bool hasPhysicalPages() { return m_hasPhysicalPages; }
    5353    void setHasPhysicalPages(bool hasPhysicalPages) { m_hasPhysicalPages = hasPhysicalPages; }
     54   
     55    bool isMarked() { return m_isMarked; }
     56    void setMarked(bool isMarked) { m_isMarked = isMarked; }
    5457
    5558    bool isNull() { return !m_size; }
     
    6871
    6972private:
    70     static const size_t flagBits = 3;
     73    static const size_t flagBits = 4;
    7174    static const size_t compactBeginBits = 4;
    7275    static const size_t sizeBits = bitCount<unsigned>() - flagBits - compactBeginBits;
     
    8386    bool m_isEnd: 1;
    8487    bool m_hasPhysicalPages: 1;
     88    bool m_isMarked: 1;
    8589    unsigned m_compactBegin: compactBeginBits;
    8690    unsigned m_size: sizeBits;
  • trunk/Source/bmalloc/bmalloc/FreeList.cpp

    r180693 r180701  
    108108}
    109109
     110void FreeList::removeInvalidAndDuplicateEntries()
     111{
     112    for (size_t i = m_vector.size(); i-- > 0; ) {
     113        LargeObject largeObject(LargeObject::DoNotValidate, m_vector[i].begin());
     114        if (!largeObject.isValidAndFree(m_vector[i].size())) {
     115            m_vector.pop(i);
     116            continue;
     117        }
     118       
     119        largeObject.setMarked(false);
     120    }
     121
     122    for (size_t i = m_vector.size(); i-- > 0; ) {
     123        LargeObject largeObject(LargeObject::DoNotValidate, m_vector[i].begin());
     124        if (largeObject.isMarked()) {
     125            m_vector.pop(i);
     126            continue;
     127        }
     128
     129        largeObject.setMarked(true);
     130    }
     131}
     132
     133
    110134} // namespace bmalloc
  • trunk/Source/bmalloc/bmalloc/FreeList.h

    r180693 r180701  
    3636class FreeList {
    3737public:
     38    FreeList();
     39
    3840    void push(const LargeObject&);
    3941
    4042    LargeObject take(size_t);
    4143    LargeObject take(size_t alignment, size_t, size_t unalignedSize);
     44   
    4245    LargeObject takeGreedy(size_t);
     46
     47    void removeInvalidAndDuplicateEntries();
    4348   
    4449private:
    4550    Vector<Range> m_vector;
     51    size_t m_limit;
    4652};
     53
     54inline FreeList::FreeList()
     55    : m_vector()
     56    , m_limit(freeListSearchDepth)
     57{
     58}
    4759
    4860inline void FreeList::push(const LargeObject& largeObject)
    4961{
    5062    BASSERT(largeObject.isFree());
     63    if (m_vector.size() == m_limit) {
     64        removeInvalidAndDuplicateEntries();
     65        m_limit = std::max(m_vector.size() * freeListGrowFactor, freeListSearchDepth);
     66    }
    5167    m_vector.push(largeObject.range());
    5268}
  • trunk/Source/bmalloc/bmalloc/LargeObject.h

    r180693 r180701  
    5656    void setHasPhysicalPages(bool) const;
    5757   
     58    bool isMarked() const;
     59    void setMarked(bool) const;
     60   
    5861    bool isValidAndFree(size_t) const;
    5962
     
    125128    m_beginTag->setHasPhysicalPages(hasPhysicalPages);
    126129    m_endTag->setHasPhysicalPages(hasPhysicalPages);
     130}
     131
     132inline bool LargeObject::isMarked() const
     133{
     134    validate();
     135    return m_beginTag->isMarked();
     136}
     137
     138inline void LargeObject::setMarked(bool isMarked) const
     139{
     140    validate();
     141    m_beginTag->setMarked(isMarked);
     142    m_endTag->setMarked(isMarked);
    127143}
    128144
     
    224240    BASSERT(m_beginTag->isFree() == m_endTag->isFree());
    225241    BASSERT(m_beginTag->hasPhysicalPages() == m_endTag->hasPhysicalPages());
     242    BASSERT(m_beginTag->isMarked() == m_endTag->isMarked());
    226243}
    227244
  • trunk/Source/bmalloc/bmalloc/Sizes.h

    r180693 r180701  
    8383
    8484    static const size_t freeListSearchDepth = 16;
     85    static const size_t freeListGrowFactor = 2;
    8586
    8687    static const uintptr_t typeMask = (superChunkSize - 1) & ~((superChunkSize / 4) - 1); // 4 taggable chunks
Note: See TracChangeset for help on using the changeset viewer.