Bug 200920

Summary: Unsafe usage of CookieStorageObserver from a background thread
Product: WebKit Reporter: Chris Dumez <cdumez>
Component: WebKit2Assignee: Chris Dumez <cdumez>
Status: RESOLVED FIXED    
Severity: Normal CC: achristensen, beidson, commit-queue, ggaren, rniwa, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch
none
Patch none

Description Chris Dumez 2019-08-20 08:32:37 PDT
Unsafe usage of CookieStorageObserver from a background thread. CookieStorageObserver gets constructed / destructed on the main thread. However, CookieStorageObserver::cookiesDidChange() gets called on a background thread and tries to ref |this|. Even though CookieStorageObserver is ThreadSafeRefCounted, this is still unsafe because the CookieStorageObserver may already be running on the main thread when CookieStorageObserver::cookiesDidChange() gets called on the background thread. This could lead to a double free.
Comment 1 Chris Dumez 2019-08-20 08:36:20 PDT
Created attachment 376770 [details]
Patch
Comment 2 Alex Christensen 2019-08-20 08:40:44 PDT
Comment on attachment 376770 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=376770&action=review

> Source/WebCore/ChangeLog:11
> +        is ThreadSafeRefCounted, this is still unsafe because the CookieStorageObserver may already be

Did you forget "destructor" here?  I don't understand what is different about CookieStorageObserver.  Couldn't this happen with any ThreadSafeRefCounted type?
Comment 3 Chris Dumez 2019-08-20 08:58:18 PDT
Comment on attachment 376770 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=376770&action=review

>> Source/WebCore/ChangeLog:11
>> +        is ThreadSafeRefCounted, this is still unsafe because the CookieStorageObserver may already be
> 
> Did you forget "destructor" here?  I don't understand what is different about CookieStorageObserver.  Couldn't this happen with any ThreadSafeRefCounted type?

Yes, I forgot destructor :/

Yes, this could happen with any ThreadSafeRefCounted type. It is almost always unsafe to create a Ref<> / RefPtr<> from a raw pointer for a ThreadSafeRefCounted type from a thread that is not the thread that owns the object. Brady suggest adding a ThreadSafeRefCounted::tryRef() for this and I think this is a nice idea but I'd rather keep this patch small (cherry-pickable).
Not that usually, we do not have this problem since we pass a RefPtr<> or Ref<> from the owner thread to another thread. It is not common that we create the Ref<> / RefPtr<> from a non-owner thread.
Comment 4 Alex Christensen 2019-08-20 09:00:00 PDT
Comment on attachment 376770 [details]
Patch

Sounds like we should add some assertions to ThreadSafeRefCounted, too.  The name implies that you can't run into problems like this.  I'll bet there are other double frees out there.
Comment 5 Chris Dumez 2019-08-20 09:00:42 PDT
Comment on attachment 376770 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=376770&action=review

>>> Source/WebCore/ChangeLog:11
>>> +        is ThreadSafeRefCounted, this is still unsafe because the CookieStorageObserver may already be
>> 
>> Did you forget "destructor" here?  I don't understand what is different about CookieStorageObserver.  Couldn't this happen with any ThreadSafeRefCounted type?
> 
> Yes, I forgot destructor :/
> 
> Yes, this could happen with any ThreadSafeRefCounted type. It is almost always unsafe to create a Ref<> / RefPtr<> from a raw pointer for a ThreadSafeRefCounted type from a thread that is not the thread that owns the object. Brady suggest adding a ThreadSafeRefCounted::tryRef() for this and I think this is a nice idea but I'd rather keep this patch small (cherry-pickable).
> Not that usually, we do not have this problem since we pass a RefPtr<> or Ref<> from the owner thread to another thread. It is not common that we create the Ref<> / RefPtr<> from a non-owner thread.

Also note that the CookieStorageObserver only stops observing notifications in its destructor, which is why this is an issue. I decided to fix it with a WeakPtr<> but another way to fix this would have been a 2-step destruction, requiring the caller to call a CookieStorageObserver::stopListingForNotifications() method on the main thread *before* destroying the CookieStorageObserver object. The WeakPtr approach is a bit less error-prone IMO though.
Comment 6 Chris Dumez 2019-08-20 09:06:15 PDT
Created attachment 376774 [details]
Patch
Comment 7 Radar WebKit Bug Importer 2019-08-20 09:06:52 PDT
<rdar://problem/54513840>
Comment 8 WebKit Commit Bot 2019-08-20 09:53:27 PDT
Comment on attachment 376774 [details]
Patch

Clearing flags on attachment: 376774

Committed r248902: <https://trac.webkit.org/changeset/248902>
Comment 9 WebKit Commit Bot 2019-08-20 09:53:29 PDT
All reviewed patches have been landed.  Closing bug.
Comment 10 Geoffrey Garen 2019-08-20 10:49:48 PDT
Comment on attachment 376774 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=376774&action=review

> Source/WebCore/platform/network/cocoa/CookieStorageObserver.mm:78
> +    : m_weakThis(makeWeakPtr(*this))

Similar to my comment in bug 200925, I think we should remove this data member, and just use makeWeakPtr(*this).
Comment 11 Chris Dumez 2019-08-20 11:29:05 PDT
Comment on attachment 376774 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=376774&action=review

>> Source/WebCore/platform/network/cocoa/CookieStorageObserver.mm:78
>> +    : m_weakThis(makeWeakPtr(*this))
> 
> Similar to my comment in bug 200925, I think we should remove this data member, and just use makeWeakPtr(*this).

MakeWeakPtr() is not currently safe to call from another thread though...
Comment 12 Chris Dumez 2019-08-20 11:57:51 PDT
(In reply to Chris Dumez from comment #11)
> Comment on attachment 376774 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=376774&action=review
> 
> >> Source/WebCore/platform/network/cocoa/CookieStorageObserver.mm:78
> >> +    : m_weakThis(makeWeakPtr(*this))
> > 
> > Similar to my comment in bug 200925, I think we should remove this data member, and just use makeWeakPtr(*this).
> 
> MakeWeakPtr() is not currently safe to call from another thread though...

Oh, I now see your other comment about initializing the WeakPtrFactory in the constructor on the main thread to work around the thread-safety problem. I will look into this.