Bug 179745 - Dispatching an event on a ServiceWorker may fail or crash due to GC
Summary: Dispatching an event on a ServiceWorker may fail or crash due to GC
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: WebCore Misc. (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Chris Dumez
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2017-11-15 14:38 PST by Chris Dumez
Modified: 2017-11-16 10:22 PST (History)
9 users (show)

See Also:


Attachments
Patch (11.16 KB, patch)
2017-11-15 14:50 PST, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (12.33 KB, patch)
2017-11-15 16:02 PST, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (10.88 KB, patch)
2017-11-16 10:13 PST, Chris Dumez
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Dumez 2017-11-15 14:38:01 PST
Dispatching an event on a ServiceWorker may fail or crash due to GC. We need to make sure that a ServiceWorker's wrapper stays alive as long as we may dispatch events on it.
Comment 1 Chris Dumez 2017-11-15 14:50:16 PST
Created attachment 327028 [details]
Patch
Comment 2 Chris Dumez 2017-11-15 16:02:29 PST
Created attachment 327031 [details]
Patch
Comment 3 Chris Dumez 2017-11-15 16:13:29 PST
Crash trace for reference:
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.JavaScriptCore      	0x000000040bcfc0b4 WTFCrash + 36 (Assertions.cpp:270)
1   com.apple.WebCore             	0x00000003fe22b3be WebCore::JSEventListener::jsFunction(WebCore::ScriptExecutionContext&) const + 398 (JSEventListener.h:128)
2   com.apple.WebCore             	0x00000003fe22a84d WebCore::JSEventListener::handleEvent(WebCore::ScriptExecutionContext&, WebCore::Event&) + 205 (JSEventListener.cpp:97)
3   com.apple.WebCore             	0x00000003fe773df2 WebCore::EventTarget::fireEventListeners(WebCore::Event&, WTF::Vector<WTF::RefPtr<WebCore::RegisteredEventListener>, 1ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>) + 962 (EventTarget.cpp:287)
4   com.apple.WebCore             	0x00000003fe76b8ca WebCore::EventTarget::fireEventListeners(WebCore::Event&) + 314 (EventTarget.cpp:228)
5   com.apple.WebCore             	0x00000003fe7739a9 WebCore::EventTarget::dispatchEvent(WebCore::Event&) + 233 (EventTarget.cpp:188)
6   com.apple.WebCore             	0x00000003ffb77c84 WebCore::ServiceWorker::scheduleTaskToUpdateState(WebCore::ServiceWorkerState)::$_3::operator()(WebCore::ScriptExecutionContext&) const + 84 (ServiceWorker.cpp:100)
7   com.apple.WebCore             	0x00000003ffb77ae4 WTF::Function<void (WebCore::ScriptExecutionContext&)>::CallableWrapper<WebCore::ServiceWorker::scheduleTaskToUpdateState(WebCore::ServiceWorkerState)::$_3>::call(WebCore::ScriptExecutionContext&) + 52 (Function.h:101)
8   com.apple.WebCore             	0x00000003fe1113be WTF::Function<void (WebCore::ScriptExecutionContext&)>::operator()(WebCore::ScriptExecutionContext&) const + 158 (Function.h:56)
9   com.apple.WebCore             	0x00000003fe0fed8d WebCore::ScriptExecutionContext::Task::performTask(WebCore::ScriptExecutionContext&) + 29 (ScriptExecutionContext.h:183)
10  com.apple.WebCore             	0x00000003fe7269c6 WebCore::Document::postTask(WebCore::ScriptExecutionContext::Task&&)::$_2::operator()() + 278 (Document.cpp:5749)
11  com.apple.WebCore             	0x00000003fe7267a9 WTF::Function<void ()>::CallableWrapper<WebCore::Document::postTask(WebCore::ScriptExecutionContext::Task&&)::$_2>::call() + 25 (Function.h:101)
12  com.apple.JavaScriptCore      	0x000000040bd316fb WTF::Function<void ()>::operator()() const + 139 (Function.h:56)
13  com.apple.JavaScriptCore      	0x000000040bd3130b WTF::dispatchFunctionsFromMainThread() + 331 (MainThread.cpp:129)
14  com.apple.JavaScriptCore      	0x000000040bd33fc1 WTF::timerFired(__CFRunLoopTimer*, void*) + 49 (MainThreadMac.mm:111)
15  com.apple.CoreFoundation      	0x00007fff4abe4ce4 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
Comment 4 Geoffrey Garen 2017-11-16 09:51:08 PST
Comment on attachment 327031 [details]
Patch

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

r=me

> Source/WebCore/ChangeLog:12
> +        implementation objects keeps a PendingActivity alive while it may dispatch JS events.

object

> Source/WebCore/ChangeLog:13
> +        The only even dispatched on ServiceWorker objects is the "statechange" one. We may

event

> Source/WebCore/workers/service/ServiceWorker.cpp:78
> +    serviceWorker->updatePendingActivityForEventDispatch();

Since this is required for correctness upon allocation, I think it would be clearer to do this in the ServiceWorker constructor.

> Source/WebCore/workers/service/ServiceWorker.cpp:175
> +    // FIXME: We should do better.

Would be nice to explain here that this prevents us from entering the page cache when we have a service worker.

> Source/WebCore/workers/service/ServiceWorker.cpp:181
> +    removeFromAllWorkers(*this);

Are we required to remove ourselves from the map upon stop, or can we wait for our destructor? Since we added ourselves in the constructor, I think it's a little clearer to remove ourselves in the destructor.

> Source/WebCore/workers/service/ServiceWorker.cpp:188
> +    // ServiceWorker can dispatch events until they become redundant or they are stopped.

ServiceWorkers
Comment 5 Chris Dumez 2017-11-16 09:57:23 PST
Comment on attachment 327031 [details]
Patch

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

>> Source/WebCore/workers/service/ServiceWorker.cpp:78
>> +    serviceWorker->updatePendingActivityForEventDispatch();
> 
> Since this is required for correctness upon allocation, I think it would be clearer to do this in the ServiceWorker constructor.

The reason I moved it here is because I hit an assertion when doing this in the constructor. The reason is that this refs |this| when taking a PendingActivity and doing so in the constructor would mean we ref *before* the object has been adopted. (Should I use relax adoptionRequirements() instead?)

>> Source/WebCore/workers/service/ServiceWorker.cpp:181
>> +    removeFromAllWorkers(*this);
> 
> Are we required to remove ourselves from the map upon stop, or can we wait for our destructor? Since we added ourselves in the constructor, I think it's a little clearer to remove ourselves in the destructor.

I'll drop this and replace with an assertion to make sure we do not recycle/reuse workers that have been stopped.
Comment 6 Geoffrey Garen 2017-11-16 10:06:09 PST
> > Since this is required for correctness upon allocation, I think it would be clearer to do this in the ServiceWorker constructor.
> 
> The reason I moved it here is because I hit an assertion when doing this in
> the constructor. The reason is that this refs |this| when taking a
> PendingActivity and doing so in the constructor would mean we ref *before*
> the object has been adopted. (Should I use relax adoptionRequirements()
> instead?)

I think I would either relaxAdoptionRequirement() or add a create method to encapsulate the updatePendingActivityForEventDispatch() call and make the constructor private.

It's kind of annoying that the m_adoptionIsRequired mechanism prevents you from calling a function that uses RefPtr from your constructor. It is definitely not a programming error to do that.

> >> Source/WebCore/workers/service/ServiceWorker.cpp:181
> >> +    removeFromAllWorkers(*this);
> > 
> > Are we required to remove ourselves from the map upon stop, or can we wait for our destructor? Since we added ourselves in the constructor, I think it's a little clearer to remove ourselves in the destructor.
> 
> I'll drop this and replace with an assertion to make sure we do not
> recycle/reuse workers that have been stopped.

Sounds good.
Comment 7 Chris Dumez 2017-11-16 10:08:25 PST
(In reply to Geoffrey Garen from comment #6)
> > > Since this is required for correctness upon allocation, I think it would be clearer to do this in the ServiceWorker constructor.
> > 
> > The reason I moved it here is because I hit an assertion when doing this in
> > the constructor. The reason is that this refs |this| when taking a
> > PendingActivity and doing so in the constructor would mean we ref *before*
> > the object has been adopted. (Should I use relax adoptionRequirements()
> > instead?)
> 
> I think I would either relaxAdoptionRequirement() or add a create method to
> encapsulate the updatePendingActivityForEventDispatch() call and make the
> constructor private.

I do not understand this comment. The constructor already is private and the call to updatePendingActivityForEventDispatch() is already in the create function of the class.
Comment 8 Chris Dumez 2017-11-16 10:13:27 PST
Created attachment 327076 [details]
Patch
Comment 9 Chris Dumez 2017-11-16 10:13:55 PST
(In reply to Chris Dumez from comment #7)
> (In reply to Geoffrey Garen from comment #6)
> > > > Since this is required for correctness upon allocation, I think it would be clearer to do this in the ServiceWorker constructor.
> > > 
> > > The reason I moved it here is because I hit an assertion when doing this in
> > > the constructor. The reason is that this refs |this| when taking a
> > > PendingActivity and doing so in the constructor would mean we ref *before*
> > > the object has been adopted. (Should I use relax adoptionRequirements()
> > > instead?)
> > 
> > I think I would either relaxAdoptionRequirement() or add a create method to
> > encapsulate the updatePendingActivityForEventDispatch() call and make the
> > constructor private.
> 
> I do not understand this comment. The constructor already is private and the
> call to updatePendingActivityForEventDispatch() is already in the create
> function of the class.

Anyway, latest iteration now uses relaxAdoptionRequirements().
Comment 10 Geoffrey Garen 2017-11-16 10:16:05 PST
> > I think I would either relaxAdoptionRequirement() or add a create method to
> > encapsulate the updatePendingActivityForEventDispatch() call and make the
> > constructor private.
> 
> I do not understand this comment. The constructor already is private and the
> call to updatePendingActivityForEventDispatch() is already in the create
> function of the class.

Sorry, I didn't notice that this was the getOrCreate function :(.

I think relaxAdoptionRequirement() is fine too, so I'll cq+ this new patch.
Comment 11 Chris Dumez 2017-11-16 10:21:49 PST
Comment on attachment 327076 [details]
Patch

Clearing flags on attachment: 327076

Committed r224924: <https://trac.webkit.org/changeset/224924>
Comment 12 Chris Dumez 2017-11-16 10:21:51 PST
All reviewed patches have been landed.  Closing bug.
Comment 13 Radar WebKit Bug Importer 2017-11-16 10:22:23 PST
<rdar://problem/35591546>