Title: [184316] trunk/Source
Revision
184316
Author
oli...@apple.com
Date
2015-05-13 16:18:56 -0700 (Wed, 13 May 2015)

Log Message

Source/_javascript_Core:
Ensure that all the smart pointer types in WTF clear their pointer before deref
https://bugs.webkit.org/show_bug.cgi?id=143789

Reviewed by Ryosuke Niwa.

One of the simpler cases of this in _javascript_Core. There
are other cases where we need to guard the derefs but they
are more complex cases.

* inspector/JSInjectedScriptHost.cpp:
(Inspector::JSInjectedScriptHost::releaseImpl):
* inspector/JSJavaScriptCallFrame.cpp:
(Inspector::JSJavaScriptCallFrame::releaseImpl):

Source/WTF:
       Ensure that all the smart pointer types in WTF clear their pointer before deref
       https://bugs.webkit.org/show_bug.cgi?id=143789

       Reviewed by Ryosuke Niwa.

       In order to prevent use after free bugs caused by destructors
       that end up trying to access the smart pointer itself, we should
       make sure we always clear the m_ptr field before calling deref.

       Essentially the UaF path is:
       struct Foo : RefCounted<Foo> {
 Wibble* m_wibble;
 void doSomething();
 ~Foo() { m_wibble->doSomethingLikeCleanup(); }
       };

       struct Wibble {
 void doSomethingLikeCleanup()
 {
   if (m_foo) {
       /* if this branch is not here we get a null deref */
       m_foo->doSomething();
   }
 }
 void replaceFoo(Foo* foo) { m_foo = foo; }
 RefPtr<Foo> m_foo;
       };

       Wibble* someWibble = /* a Wibble with m_foo->m_refCount == 1 */;
                    /* and m_foo points to someWibble       */;

       someWibble->replaceFoo(someOtherFoo);
       + someWibble->m_foo->m_ptr->deref();
 + someWibble->m_foo->m_ptr->~Foo()
   + someWibble->m_foo->m_ptr->m_wibble->doSomethingLikeCleanup()
     + someWibble->m_foo->m_ptr->m_wibble /* someWibble */ ->m_foo->m_ptr /*logically dead*/ ->doSomething()

       By clearing m_ptr first we either force a null pointer deref or
       we force our code down a path that does not use the dead smart
       pointer.

       * wtf/PassRefPtr.h:
       (WTF::PassRefPtr::~PassRefPtr):
       * wtf/Ref.h:
       (WTF::Ref::~Ref):
       (WTF::Ref::operator=):
       * wtf/RefPtr.h:
       (WTF::RefPtr::~RefPtr):
       * wtf/RetainPtr.h:
       (WTF::RetainPtr::~RetainPtr):
       (WTF::RetainPtr<T>::clear):

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (184315 => 184316)


--- trunk/Source/_javascript_Core/ChangeLog	2015-05-13 23:09:13 UTC (rev 184315)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-05-13 23:18:56 UTC (rev 184316)
@@ -1,3 +1,18 @@
+2015-05-13  Oliver Hunt  <oli...@apple.com>
+        Ensure that all the smart pointer types in WTF clear their pointer before deref
+        https://bugs.webkit.org/show_bug.cgi?id=143789
+
+        Reviewed by Ryosuke Niwa.
+
+        One of the simpler cases of this in _javascript_Core. There
+        are other cases where we need to guard the derefs but they
+        are more complex cases.
+
+        * inspector/JSInjectedScriptHost.cpp:
+        (Inspector::JSInjectedScriptHost::releaseImpl):
+        * inspector/JSJavaScriptCallFrame.cpp:
+        (Inspector::JSJavaScriptCallFrame::releaseImpl):
+
 2015-05-13  Alexandr Skachkov  <gskach...@gmail.com>
 
         Small refactoring before ES6 Arrow function implementation.

Modified: trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp (184315 => 184316)


--- trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp	2015-05-13 23:09:13 UTC (rev 184315)
+++ trunk/Source/_javascript_Core/inspector/JSInjectedScriptHost.cpp	2015-05-13 23:18:56 UTC (rev 184316)
@@ -85,10 +85,8 @@
 
 void JSInjectedScriptHost::releaseImpl()
 {
-    if (m_impl) {
-        m_impl->deref();
-        m_impl = nullptr;
-    }
+    if (auto impl = std::exchange(m_impl, nullptr))
+        impl->deref();
 }
 
 JSInjectedScriptHost::~JSInjectedScriptHost()

Modified: trunk/Source/_javascript_Core/inspector/JSJavaScriptCallFrame.cpp (184315 => 184316)


--- trunk/Source/_javascript_Core/inspector/JSJavaScriptCallFrame.cpp	2015-05-13 23:09:13 UTC (rev 184315)
+++ trunk/Source/_javascript_Core/inspector/JSJavaScriptCallFrame.cpp	2015-05-13 23:18:56 UTC (rev 184316)
@@ -64,10 +64,8 @@
 
 void JSJavaScriptCallFrame::releaseImpl()
 {
-    if (m_impl) {
-        m_impl->deref();
-        m_impl = nullptr;
-    }
+    if (auto impl = std::exchange(m_impl, nullptr))
+        impl->deref();
 }
 
 JSJavaScriptCallFrame::~JSJavaScriptCallFrame()

Modified: trunk/Source/WTF/ChangeLog (184315 => 184316)


--- trunk/Source/WTF/ChangeLog	2015-05-13 23:09:13 UTC (rev 184315)
+++ trunk/Source/WTF/ChangeLog	2015-05-13 23:18:56 UTC (rev 184316)
@@ -1,3 +1,56 @@
+2015-05-13  Oliver Hunt  <oli...@apple.com>
+       Ensure that all the smart pointer types in WTF clear their pointer before deref
+       https://bugs.webkit.org/show_bug.cgi?id=143789
+
+       Reviewed by Ryosuke Niwa.
+
+       In order to prevent use after free bugs caused by destructors
+       that end up trying to access the smart pointer itself, we should
+       make sure we always clear the m_ptr field before calling deref.
+
+       Essentially the UaF path is:
+       struct Foo : RefCounted<Foo> {
+         Wibble* m_wibble;
+         void doSomething();
+         ~Foo() { m_wibble->doSomethingLikeCleanup(); }
+       };
+
+       struct Wibble {
+         void doSomethingLikeCleanup()
+         {
+           if (m_foo) {
+               /* if this branch is not here we get a null deref */
+               m_foo->doSomething();
+           }
+         }
+         void replaceFoo(Foo* foo) { m_foo = foo; }
+         RefPtr<Foo> m_foo;
+       };
+
+       Wibble* someWibble = /* a Wibble with m_foo->m_refCount == 1 */;
+                            /* and m_foo points to someWibble       */;
+
+       someWibble->replaceFoo(someOtherFoo);
+       + someWibble->m_foo->m_ptr->deref();
+         + someWibble->m_foo->m_ptr->~Foo()
+           + someWibble->m_foo->m_ptr->m_wibble->doSomethingLikeCleanup()
+             + someWibble->m_foo->m_ptr->m_wibble /* someWibble */ ->m_foo->m_ptr /*logically dead*/ ->doSomething()
+
+       By clearing m_ptr first we either force a null pointer deref or
+       we force our code down a path that does not use the dead smart
+       pointer.
+
+       * wtf/PassRefPtr.h:
+       (WTF::PassRefPtr::~PassRefPtr):
+       * wtf/Ref.h:
+       (WTF::Ref::~Ref):
+       (WTF::Ref::operator=):
+       * wtf/RefPtr.h:
+       (WTF::RefPtr::~RefPtr):
+       * wtf/RetainPtr.h:
+       (WTF::RetainPtr::~RetainPtr):
+       (WTF::RetainPtr<T>::clear):
+
 2015-05-12  Michael Saboff  <msab...@apple.com>
 
         If JSC cannot get executable memory, it shouldn't call madvise

Modified: trunk/Source/WTF/wtf/PassRefPtr.h (184315 => 184316)


--- trunk/Source/WTF/wtf/PassRefPtr.h	2015-05-13 23:09:13 UTC (rev 184315)
+++ trunk/Source/WTF/wtf/PassRefPtr.h	2015-05-13 23:18:56 UTC (rev 184316)
@@ -55,7 +55,7 @@
         PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { }
         template<typename U> PassRefPtr(const PassRefPtr<U>& o) : m_ptr(o.leakRef()) { }
 
-        ALWAYS_INLINE ~PassRefPtr() { derefIfNotNull(m_ptr); }
+        ALWAYS_INLINE ~PassRefPtr() { derefIfNotNull(std::exchange(m_ptr, nullptr)); }
 
         template<typename U> PassRefPtr(const RefPtr<U>&);
         template<typename U> PassRefPtr(Ref<U>&& reference) : m_ptr(&reference.leakRef()) { }

Modified: trunk/Source/WTF/wtf/RefPtr.h (184315 => 184316)


--- trunk/Source/WTF/wtf/RefPtr.h	2015-05-13 23:09:13 UTC (rev 184315)
+++ trunk/Source/WTF/wtf/RefPtr.h	2015-05-13 23:18:56 UTC (rev 184316)
@@ -56,7 +56,7 @@
     RefPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { }
     bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); }
 
-    ALWAYS_INLINE ~RefPtr() { derefIfNotNull(m_ptr); }
+    ALWAYS_INLINE ~RefPtr() { derefIfNotNull(std::exchange(m_ptr, nullptr)); }
 
     T* get() const { return m_ptr; }
     

Modified: trunk/Source/WTF/wtf/RetainPtr.h (184315 => 184316)


--- trunk/Source/WTF/wtf/RetainPtr.h	2015-05-13 23:09:13 UTC (rev 184315)
+++ trunk/Source/WTF/wtf/RetainPtr.h	2015-05-13 23:18:56 UTC (rev 184316)
@@ -71,7 +71,7 @@
     RetainPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { }
     bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); }
     
-    ~RetainPtr() { if (StorageType ptr = m_ptr) CFRelease(ptr); }
+    ~RetainPtr();
     
     template<typename U> RetainPtr(const RetainPtr<U>&);
 
@@ -135,6 +135,12 @@
     StorageType m_ptr;
 };
 
+template<typename T> inline RetainPtr<T>::~RetainPtr()
+{
+    if (StorageType ptr = std::exchange(m_ptr, nullptr))
+        CFRelease(ptr);
+}
+
 // Helper function for creating a RetainPtr using template argument deduction.
 template<typename T> inline RetainPtr<T> retainPtr(T) WARN_UNUSED_RETURN;
 
@@ -147,10 +153,8 @@
 
 template<typename T> inline void RetainPtr<T>::clear()
 {
-    if (StorageType ptr = m_ptr) {
-        m_ptr = nullptr;
+    if (StorageType ptr = std::exchange(m_ptr, nullptr))
         CFRelease(ptr);
-    }
 }
 
 template<typename T> inline typename RetainPtr<T>::PtrType RetainPtr<T>::leakRef()
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to