Title: [251034] trunk/Source
Revision
251034
Author
[email protected]
Date
2019-10-11 19:19:34 -0700 (Fri, 11 Oct 2019)

Log Message

Make it safe to store a ThreadSafeRefCounted object in Ref & RefPtr safe inside its destructor
https://bugs.webkit.org/show_bug.cgi?id=201576
Source/_javascript_Core:

<rdar://problem/56001847>

Reviewed by Geoffrey Garen and Mark Lam.

Made DropAllLocks::DropAllLocks check Heap::isShuttingDown instead of VM's refCount being 0 to detect
when VM is getting destroyed.

* runtime/JSLock.cpp:
(JSC::JSLock::DropAllLocks::DropAllLocks):

Source/WTF:


Reviewed by Geoffrey Garen and Mark Lam.

This patch leaves m_refCount 1 inside the last deref call to ThreadSafeRefCounted
so that storing an instance of this object in Ref or RefPtr would not trigger a recursive delete.

Note: this patch does not try to fix a race condition by which another thread tries to ref()
this object after the "last" call to deref had happened since such a code would ref() this object
long after it had been removed (UAF), nor some code calling deref() before calling ref() inside
a destructor since there is no way to defend against unpaired calls to ref() & deref() like that.

Also added m_deletionHasBegun like RefCounted.

* wtf/ThreadSafeRefCounted.h:
(WTF::ThreadSafeRefCountedBase::ref const):
(WTF::ThreadSafeRefCountedBase::hasOneRef const):
(WTF::ThreadSafeRefCountedBase::derefBase const):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (251033 => 251034)


--- trunk/Source/_javascript_Core/ChangeLog	2019-10-12 01:27:57 UTC (rev 251033)
+++ trunk/Source/_javascript_Core/ChangeLog	2019-10-12 02:19:34 UTC (rev 251034)
@@ -1,3 +1,17 @@
+2019-10-10  Ryosuke Niwa  <[email protected]>
+
+        Make it safe to store a ThreadSafeRefCounted object in Ref & RefPtr safe inside its destructor
+        https://bugs.webkit.org/show_bug.cgi?id=201576
+        <rdar://problem/56001847>
+
+        Reviewed by Geoffrey Garen and Mark Lam.
+
+        Made DropAllLocks::DropAllLocks check Heap::isShuttingDown instead of VM's refCount being 0 to detect
+        when VM is getting destroyed.
+
+        * runtime/JSLock.cpp:
+        (JSC::JSLock::DropAllLocks::DropAllLocks):
+
 2019-10-11  Keith Miller  <[email protected]>
 
         Wasm B3IRGenerator should use arguments for control data.

Modified: trunk/Source/_javascript_Core/runtime/JSLock.cpp (251033 => 251034)


--- trunk/Source/_javascript_Core/runtime/JSLock.cpp	2019-10-12 01:27:57 UTC (rev 251033)
+++ trunk/Source/_javascript_Core/runtime/JSLock.cpp	2019-10-12 02:19:34 UTC (rev 251034)
@@ -279,7 +279,7 @@
     // If the VM is in the middle of being destroyed then we don't want to resurrect it
     // by allowing DropAllLocks to ref it. By this point the JSLock has already been 
     // released anyways, so it doesn't matter that DropAllLocks is a no-op.
-    , m_vm(vm->refCount() ? vm : nullptr)
+    , m_vm(vm->heap.isShuttingDown() ? nullptr : vm)
 {
     if (!m_vm)
         return;

Modified: trunk/Source/WTF/ChangeLog (251033 => 251034)


--- trunk/Source/WTF/ChangeLog	2019-10-12 01:27:57 UTC (rev 251033)
+++ trunk/Source/WTF/ChangeLog	2019-10-12 02:19:34 UTC (rev 251034)
@@ -1,3 +1,25 @@
+2019-10-10  Ryosuke Niwa  <[email protected]>
+
+        Make it safe to store a ThreadSafeRefCounted object in Ref & RefPtr safe inside its destructor
+        https://bugs.webkit.org/show_bug.cgi?id=201576
+
+        Reviewed by Geoffrey Garen and Mark Lam.
+
+        This patch leaves m_refCount 1 inside the last deref call to ThreadSafeRefCounted
+        so that storing an instance of this object in Ref or RefPtr would not trigger a recursive delete.
+
+        Note: this patch does not try to fix a race condition by which another thread tries to ref()
+        this object after the "last" call to deref had happened since such a code would ref() this object
+        long after it had been removed (UAF), nor some code calling deref() before calling ref() inside
+        a destructor since there is no way to defend against unpaired calls to ref() & deref() like that.
+
+        Also added m_deletionHasBegun like RefCounted.
+
+        * wtf/ThreadSafeRefCounted.h:
+        (WTF::ThreadSafeRefCountedBase::ref const):
+        (WTF::ThreadSafeRefCountedBase::hasOneRef const):
+        (WTF::ThreadSafeRefCountedBase::derefBase const):
+
 2019-10-11  Alex Christensen  <[email protected]>
 
         Only use CFNetwork SPI for metrics where needed

Modified: trunk/Source/WTF/wtf/ThreadSafeRefCounted.h (251033 => 251034)


--- trunk/Source/WTF/wtf/ThreadSafeRefCounted.h	2019-10-12 01:27:57 UTC (rev 251033)
+++ trunk/Source/WTF/wtf/ThreadSafeRefCounted.h	2019-10-12 02:19:34 UTC (rev 251034)
@@ -32,6 +32,12 @@
 
 namespace WTF {
 
+#if defined(NDEBUG) && !ENABLE(SECURITY_ASSERTIONS)
+#define CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE 0
+#else
+#define CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE 1
+#endif
+
 class ThreadSafeRefCountedBase {
     WTF_MAKE_NONCOPYABLE(ThreadSafeRefCountedBase);
     WTF_MAKE_FAST_ALLOCATED;
@@ -38,13 +44,27 @@
 public:
     ThreadSafeRefCountedBase() = default;
 
+#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
+    ~ThreadSafeRefCountedBase()
+    {
+        // When this ThreadSafeRefCounted object is a part of another object, derefBase() is never called on this object.
+        m_deletionHasBegun = true;
+    }
+#endif
+
     void ref() const
     {
+#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
+        ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
+#endif
         ++m_refCount;
     }
 
     bool hasOneRef() const
     {
+#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
+        ASSERT(!m_deletionHasBegun);
+#endif
         return refCount() == 1;
     }
 
@@ -57,11 +77,31 @@
     // Returns whether the pointer should be freed or not.
     bool derefBase() const
     {
-        return !--m_refCount;
+        ASSERT(m_refCount);
+
+#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
+        ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
+#endif
+
+        if (UNLIKELY(!--m_refCount)) {
+            // Setting m_refCount to 1 here prevents double delete within the destructor but not from another thread
+            // since such a thread could have ref'ed this object long after it had been deleted. See webkit.org/b/201576.
+            m_refCount = 1;
+#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
+            m_deletionHasBegun = true;
+#endif
+            return true;
+        }
+
+        return false;
     }
 
 private:
     mutable std::atomic<unsigned> m_refCount { 1 };
+
+#if CHECK_THREAD_SAFE_REF_COUNTED_LIFECYCLE
+    mutable std::atomic<bool> m_deletionHasBegun { false };
+#endif
 };
 
 enum class DestructionThread { Any, Main, MainRunLoop };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to