Title: [295092] trunk
Revision
295092
Author
[email protected]
Date
2022-06-01 09:58:41 -0700 (Wed, 01 Jun 2022)

Log Message

WeakHashMap::ensure() may crash if the map contains null references https://bugs.webkit.org/show_bug.cgi?id=241162

Reviewed by Geoffrey Garen.

WeakHashMap::ensure() may crash if the map contains null references, because
the WeakHashMap iterator destructor can clear null references and the AddResult
constructor copies and destroys the input iterator.

I find it very error-prone that destroying an iterator would modify the hash
map and thus invalidate other iterators (or even itself if the iterator was
merely copied). As a result, I removed this logic from the
WeakHashMapIteratorBase destructor. Instead, I now increase
WeakHashMap::m_operationCountSinceLastCleanup whenever the iterator gets
incremented so that null references will be removed the next time the hash map
is modified.

I also updated other read-only operations (such as get() / find() / contains())
to just increment m_operationCountSinceLastCleanup without actually clearing
null references for the same reason as above. Having such read-only operations
invalidate existing iterators is just too error-prone.

Finally, I updated the AddResult constructor to avoid copying the
WeakHashMapIterator it is passed, given that the WeakHashMapIterator
constructor and destructor do some work.

* Source/WTF/wtf/WeakHashMap.h:
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::identifier const):
* Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp:
(TestWebKitAPI::TEST):

Canonical link: https://commits.webkit.org/251187@main

Modified Paths

Diff

Modified: trunk/Source/WTF/wtf/WeakHashMap.h (295091 => 295092)


--- trunk/Source/WTF/wtf/WeakHashMap.h	2022-06-01 16:55:02 UTC (rev 295091)
+++ trunk/Source/WTF/wtf/WeakHashMap.h	2022-06-01 16:58:41 UTC (rev 295092)
@@ -89,13 +89,7 @@
             skipEmptyBuckets();
         }
     
-        ~WeakHashMapIteratorBase()
-        {
-            if (m_emptyBucketCount > m_weakHashMap.m_map.size() / 8)
-                const_cast<WeakHashMap&>(m_weakHashMap).removeNullReferences();
-            else
-                m_weakHashMap.amortizedCleanupIfNeeded(m_advanceCount + m_emptyBucketCount);
-        }
+        ~WeakHashMapIteratorBase() = default;
 
         ALWAYS_INLINE IteratorPeekType makePeek()
         {
@@ -117,14 +111,13 @@
             ++m_position;
             ++m_advanceCount;
             skipEmptyBuckets();
+            m_weakHashMap.increaseOperationCountSinceLastCleanup();
         }
 
         void skipEmptyBuckets()
         {
-            while (m_position != m_endPosition && !m_position->key.get()) {
+            while (m_position != m_endPosition && !m_position->key.get())
                 ++m_position;
-                ++m_emptyBucketCount;
-            }
         }
 
         const MapType& m_weakHashMap;
@@ -131,7 +124,6 @@
         IteratorType m_position;
         IteratorType m_endPosition;
         unsigned m_advanceCount { 0 };
-        unsigned m_emptyBucketCount { 0 };
     };
 
     class WeakHashMapIterator : public WeakHashMapIteratorBase<WeakHashMap, typename WeakHashImplMap::iterator, PeekPtrType, PeekType> {
@@ -186,7 +178,10 @@
 
     struct AddResult {
         AddResult() : isNewEntry(false) { }
-        AddResult(WeakHashMapIterator it, bool isNewEntry) : iterator(it), isNewEntry(isNewEntry) { }
+        AddResult(WeakHashMapIterator&& it, bool isNewEntry)
+            : iterator(WTFMove(it))
+            , isNewEntry(isNewEntry)
+        { }
         WeakHashMapIterator iterator;
         bool isNewEntry;
 
@@ -226,7 +221,7 @@
 
     iterator find(const KeyType& key)
     {
-        amortizedCleanupIfNeeded();
+        increaseOperationCountSinceLastCleanup();
         auto* keyImpl = keyImplIfExists(key);
         if (!keyImpl)
             return end();
@@ -235,7 +230,7 @@
 
     const_iterator find(const KeyType& key) const
     {
-        amortizedCleanupIfNeeded();
+        increaseOperationCountSinceLastCleanup();
         auto* keyImpl = keyImplIfExists(key);
         if (!keyImpl)
             return end();
@@ -244,7 +239,7 @@
 
     bool contains(const KeyType& key) const
     {
-        amortizedCleanupIfNeeded();
+        increaseOperationCountSinceLastCleanup();
         auto* keyImpl = keyImplIfExists(key);
         if (!keyImpl)
             return false;
@@ -262,7 +257,7 @@
 
     typename ValueTraits::PeekType get(const KeyType& key)
     {
-        amortizedCleanupIfNeeded();
+        increaseOperationCountSinceLastCleanup();
         auto* keyImpl = keyImplIfExists(key);
         if (!keyImpl)
             return ValueTraits::peek(ValueTraits::emptyValue());
@@ -347,10 +342,16 @@
 #endif
 
 private:
+    ALWAYS_INLINE unsigned increaseOperationCountSinceLastCleanup(unsigned operationsPerformed = 1) const
+    {
+        unsigned currentCount = m_operationCountSinceLastCleanup;
+        m_operationCountSinceLastCleanup += operationsPerformed;
+        return currentCount;
+    }
+
     ALWAYS_INLINE void amortizedCleanupIfNeeded(unsigned operationsPerformed = 1) const
     {
-        unsigned currentCount = m_operationCountSinceLastCleanup;
-        m_operationCountSinceLastCleanup = currentCount + operationsPerformed;
+        unsigned currentCount = increaseOperationCountSinceLastCleanup(operationsPerformed);
         if (currentCount / 2 > m_map.size())
             const_cast<WeakHashMap&>(*this).removeNullReferences();
     }

Modified: trunk/Source/WebCore/dom/Element.cpp (295091 => 295092)


--- trunk/Source/WebCore/dom/Element.cpp	2022-06-01 16:55:02 UTC (rev 295091)
+++ trunk/Source/WebCore/dom/Element.cpp	2022-06-01 16:58:41 UTC (rev 295092)
@@ -4869,7 +4869,6 @@
 
 ElementIdentifier Element::identifier() const
 {
-    elementIdentifiersMap().removeNullReferences();
     return elementIdentifiersMap().ensure(*this, [] { return ElementIdentifier::generate(); }).iterator->value;
 }
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp (295091 => 295092)


--- trunk/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp	2022-06-01 16:55:02 UTC (rev 295091)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/WeakPtr.cpp	2022-06-01 16:58:41 UTC (rev 295092)
@@ -1602,9 +1602,9 @@
         auto pairs = collectKeyValuePairsUsingIterators<Base*, int>(weakHashMap);
         EXPECT_EQ(pairs.size(), 0u);
 
-        EXPECT_EQ(s_baseWeakReferences, 0u); // Iterating over WeakHashMap should have triggerd amortized deletion.
+        EXPECT_EQ(s_baseWeakReferences, 36u);
         weakHashMap.checkConsistency();
-        EXPECT_FALSE(weakHashMap.hasNullReferences());
+        EXPECT_TRUE(weakHashMap.hasNullReferences());
     }
 
     {
@@ -1744,8 +1744,8 @@
                 collectKeyValuePairsUsingIterators<Base*, int>(weakHashMap);
 
             weakHashMap.checkConsistency();
-            EXPECT_EQ(s_baseWeakReferences, 40u);
-            EXPECT_FALSE(weakHashMap.hasNullReferences());
+            EXPECT_EQ(s_baseWeakReferences, 50u);
+            EXPECT_TRUE(weakHashMap.hasNullReferences());
 
             for (unsigned i = 0; i < 50; ++i) {
                 if (!(i % 9))
@@ -1760,8 +1760,8 @@
                 collectKeyValuePairsUsingIterators<Base*, int>(weakHashMap);
 
             weakHashMap.checkConsistency();
-            EXPECT_EQ(s_baseWeakReferences, 36u);
-            EXPECT_FALSE(weakHashMap.hasNullReferences());
+            EXPECT_EQ(s_baseWeakReferences, 40u);
+            EXPECT_TRUE(weakHashMap.hasNullReferences());
         }
     }
 
@@ -1797,9 +1797,9 @@
                 }
             }
             weakHashMap.checkConsistency();
-            EXPECT_EQ(s_baseWeakReferences, 42u);
-            EXPECT_EQ(ValueObject::s_count, 42u);
-            EXPECT_FALSE(weakHashMap.hasNullReferences());
+            EXPECT_EQ(s_baseWeakReferences, 50u);
+            EXPECT_EQ(ValueObject::s_count, 50u);
+            EXPECT_TRUE(weakHashMap.hasNullReferences());
 
             for (unsigned i = 0; i < 50; ++i) {
                 if (!(i % 3))
@@ -1839,6 +1839,30 @@
     }
 }
 
+TEST(WTF_WeakPtr, WeakHashMap_iterator_destruction)
+{
+    constexpr unsigned objectCount = 10;
+    WeakHashMap<Base, unsigned> weakHashMap;
+    Vector<std::unique_ptr<Base>> objects;
+    objects.reserveInitialCapacity(objectCount);
+    for (unsigned i = 0; i < objectCount; ++i) {
+        auto object = makeUnique<Base>();
+        weakHashMap.add(*object, i);
+        objects.uncheckedAppend(WTFMove(object));
+    }
+
+    auto a = objects.takeLast();
+    auto b = objects.takeLast();
+
+    auto aIterator = weakHashMap.find(*a);
+    objects.clear();
+    for (unsigned i = 0; i < 20; ++i) {
+        auto bIterator = weakHashMap.find(*b);
+        EXPECT_EQ(bIterator->value, objectCount - 2);
+        EXPECT_EQ(aIterator->value, objectCount - 1);
+    }
+}
+
 class MultipleInheritanceBase1 : public CanMakeWeakPtr<MultipleInheritanceBase1> {
 public:
     virtual void meow() = 0;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to