Bug 233945

Summary: ActiveDOMObject::suspendIfNeeded() should not be called within constructors
Product: WebKit Reporter: Antoine Quint <graouts>
Component: New BugsAssignee: Antoine Quint <graouts>
Status: RESOLVED FIXED    
Severity: Normal CC: alecflett, beidson, benjamin, calvaris, cdumez, changseok, darin, eric.carlson, esprehn+autocc, ews-watchlist, glenn, gyuyoung.kim, hta, jer.noble, jsbell, kangil.han, macpherson, menard, philipj, sergio, tommyw, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch
none
Patch for landing
none
Patch
none
Patch
ews-feeder: commit-queue-
Patch
none
Patch
none
Patch for landing
ews-feeder: commit-queue-
Patch for landing
none
Patch
none
Patch
none
Patch for landing
none
Patch
none
Patch none

Antoine Quint
Reported 2021-12-07 11:51:57 PST
ActiveDOMObject::suspendIfNeeded() should not be called within constructors
Attachments
Patch (36.91 KB, patch)
2021-12-07 11:52 PST, Antoine Quint
no flags
Patch for landing (28.59 KB, patch)
2021-12-14 03:12 PST, Antoine Quint
no flags
Patch (18.53 KB, patch)
2021-12-15 08:24 PST, Antoine Quint
no flags
Patch (20.05 KB, patch)
2021-12-15 09:42 PST, Antoine Quint
ews-feeder: commit-queue-
Patch (20.42 KB, patch)
2021-12-15 11:34 PST, Antoine Quint
no flags
Patch (20.29 KB, patch)
2021-12-15 12:22 PST, Antoine Quint
no flags
Patch for landing (20.31 KB, patch)
2021-12-15 12:46 PST, Antoine Quint
ews-feeder: commit-queue-
Patch for landing (19.96 KB, patch)
2021-12-15 22:21 PST, Antoine Quint
no flags
Patch (45.08 KB, patch)
2021-12-16 05:17 PST, Antoine Quint
no flags
Patch (40.51 KB, patch)
2021-12-16 06:20 PST, Antoine Quint
no flags
Patch for landing (38.82 KB, patch)
2021-12-17 00:10 PST, Antoine Quint
no flags
Patch (5.62 KB, patch)
2021-12-17 04:11 PST, Antoine Quint
no flags
Patch (7.05 KB, patch)
2021-12-17 04:23 PST, Antoine Quint
no flags
Antoine Quint
Comment 1 2021-12-07 11:52:35 PST
Chris Dumez
Comment 2 2021-12-07 11:54:29 PST
Comment on attachment 446215 [details] Patch r=me assuming the bots are happy
Darin Adler
Comment 3 2021-12-07 12:08:00 PST
Comment on attachment 446215 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=446215&action=review Really great patch. Too bad we can’t get the compiler or an assertion to catch this mistake, but this is nicely thorough. Maybe we should rename this function suspendIfNeededDoNotCallInConstructor (just kidding, but maybe not kidding). > Source/WebCore/Modules/cache/DOMCache.h:40 > + static Ref<DOMCache> create(ScriptExecutionContext&, String&&, uint64_t, Ref<CacheStorageConnection>&&); Not sure that we should remove *all* of these names. The argument names "name" and "identifier" are probably still helpful. > Source/WebCore/Modules/geolocation/Geolocation.cpp:137 > - geolocation.get().suspendIfNeeded(); > + geolocation->suspendIfNeeded(); Ah, brevity competes with "looks like a risky pointer dereference, but is not". I think the more terse version is better, and it’s what we use almost everywhere, but I am conflicted. There’s something cool about ".get()." not looking like a pointer is involved.
Antoine Quint
Comment 4 2021-12-07 12:53:21 PST
(In reply to Darin Adler from comment #3) > Comment on attachment 446215 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=446215&action=review > > Really great patch. Too bad we can’t get the compiler or an assertion to > catch this mistake, but this is nicely thorough. Not quite! There are 17 cases left to fix, those were just the easy ones. I'll look into fixing the other ones piecemeal. > Maybe we should rename this function suspendIfNeededDoNotCallInConstructor > (just kidding, but maybe not kidding). > > > Source/WebCore/Modules/cache/DOMCache.h:40 > > + static Ref<DOMCache> create(ScriptExecutionContext&, String&&, uint64_t, Ref<CacheStorageConnection>&&); > > Not sure that we should remove *all* of these names. The argument names > "name" and "identifier" are probably still helpful. Will restore those. > > Source/WebCore/Modules/geolocation/Geolocation.cpp:137 > > - geolocation.get().suspendIfNeeded(); > > + geolocation->suspendIfNeeded(); > > Ah, brevity competes with "looks like a risky pointer dereference, but is > not". I think the more terse version is better, and it’s what we use almost > everywhere, but I am conflicted. There’s something cool about ".get()." not > looking like a pointer is involved. I personally don't mind either way, I stumbled onto this code looking at all call sites to ActiveDOMObject::suspendIfNeeded() and it was the only call on a Ref that called .get(). Felt right to put it in line with the rest.
Darin Adler
Comment 5 2021-12-07 15:40:54 PST
(In reply to Antoine Quint from comment #4) > Not quite! There are 17 cases left to fix, those were just the easy ones. > I'll look into fixing the other ones piecemeal. !! > Felt right to put it in line with the rest. Agreed.
Antoine Quint
Comment 6 2021-12-14 03:12:13 PST
Created attachment 447120 [details] Patch for landing
Radar WebKit Bug Importer
Comment 7 2021-12-14 11:52:15 PST
EWS
Comment 8 2021-12-15 01:17:16 PST
Committed r287066 (245261@main): <https://commits.webkit.org/245261@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 447120 [details].
Antoine Quint
Comment 9 2021-12-15 03:02:25 PST
Lots to do on this left, this first commit was only part of the work.
Antoine Quint
Comment 10 2021-12-15 08:24:26 PST
Chris Dumez
Comment 11 2021-12-15 08:38:27 PST
Comment on attachment 447234 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=447234&action=review r=me with suggestions. > Source/WebCore/Modules/indexeddb/IDBIndex.cpp:45 > +std::unique_ptr<IDBIndex> IDBIndex::create(ScriptExecutionContext& context, const IDBIndexInfo& info, IDBObjectStore& objectStore) We may want to return a UniqueRef instead since this cannot return null. > Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp:58 > +std::unique_ptr<IDBObjectStore> IDBObjectStore::create(ScriptExecutionContext& context, const IDBObjectStoreInfo& info, IDBTransaction& transaction) ditto.
Antoine Quint
Comment 12 2021-12-15 09:42:56 PST
Chris Dumez
Comment 13 2021-12-15 11:19:00 PST
Comment on attachment 447245 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=447245&action=review > Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp:532 > + m_referencedIndexes.set(indexName, std::unique_ptr<IDBIndex>(&index)); You want `index.moveToUniquePtr()`. This adopts the pointer so you will double-free.
Antoine Quint
Comment 14 2021-12-15 11:34:17 PST
Antoine Quint
Comment 15 2021-12-15 12:22:10 PST
Darin Adler
Comment 16 2021-12-15 12:35:49 PST
Comment on attachment 447270 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=447270&action=review > Source/WebCore/Modules/indexeddb/IDBObjectStore.cpp:530 > + Ref<IDBIndex> referencedIndex { index }; Since we’re touching this code, it can likely be changed to Ref instead of Ref<IDBIndex> since we have type deduction now. > Source/WebCore/Modules/indexeddb/IDBTransaction.cpp:753 > + auto index = IDBIndex::create(*scriptExecutionContext(), info, objectStore); > + return index.moveToUniquePtr(); Should just be able to write: return IDBIndex::create(*scriptExecutionContext(), info, objectStore); Move construction of unique_ptr from UniqueRef should work.
Antoine Quint
Comment 17 2021-12-15 12:46:44 PST
Created attachment 447279 [details] Patch for landing
Antoine Quint
Comment 18 2021-12-15 22:21:15 PST
Created attachment 447327 [details] Patch for landing
Antoine Quint
Comment 19 2021-12-16 03:31:27 PST
Antoine Quint
Comment 20 2021-12-16 05:17:46 PST
Antoine Quint
Comment 21 2021-12-16 06:20:23 PST
Darin Adler
Comment 22 2021-12-16 11:09:23 PST
Comment on attachment 447349 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=447349&action=review Looks fantastic > Source/WebCore/Modules/entriesapi/FileSystemEntry.cpp:57 > +void FileSystemEntry::initialize() > +{ > + suspendIfNeeded(); > +} Why do we need these initialize functions instead of calling suspendIfNeeded directly? I’m sure that you thought about a reason, but I don’t know what it is. Would be nice to have one less function if there’s no abstraction here and suspendIfNeeded would do the same thing. If what we’re trying to mitigate is callers neglecting to call suspendIfNeeded, I’m not sure that renaming it to initialize will help. If we do want to tackle that problem there may be some compile time techniques or some runtime ones, like adding a debug-only boolean set every time suspendIfNeeded and finding a good place to assert afterward to catch cases where it wasn’t called even once. Could even use a micro-task to do that assertion. > Source/WebCore/Modules/fetch/FetchBodyOwner.cpp:60 > +void FetchBodyOwner::initialize() > +{ > + suspendIfNeeded(); > +} Ditto. > Source/WebCore/Modules/fetch/FetchRequest.h:57 > + static Ref<FetchRequest> create(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&, ResourceRequest&&, FetchOptions&&, String&&); Not sure we should have removed the name "referrer". > Source/WebCore/Modules/filesystemaccess/FileSystemHandle.cpp:51 > +void FileSystemHandle::initialize() > +{ > + suspendIfNeeded(); > +} Ditto. > Source/WebCore/Modules/mediastream/RTCDTMFSender.cpp:47 > + auto result = adoptRef(* new RTCDTMFSender(context, sender, WTFMove(backend))); Maybe get rid of the space between the "*" and "new"? > Source/WebCore/workers/service/ServiceWorkerContainer.h:44 > +#include <wtf/UniqueRef.h> Not something that should block landing this patch as-is, but: This should require only a forward-declaration of UniqueRef, which should be provided by Forward.h. Files where ServiceWorkerContainer::create are called will need UniquerRef.h, but this header should not require it. > Source/WebCore/workers/service/ServiceWorkerContainer.h:61 > + static UniqueRef<ServiceWorkerContainer> create(ScriptExecutionContext*, NavigatorBase&); Another thing not about this patch: Do you know why this takes ScriptExecutionContext* rather than ScriptExecutionContext&? Is nullptr OK?
Antoine Quint
Comment 23 2021-12-16 12:52:15 PST
(In reply to Darin Adler from comment #22) > Comment on attachment 447349 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=447349&action=review > > Looks fantastic > > > Source/WebCore/Modules/entriesapi/FileSystemEntry.cpp:57 > > +void FileSystemEntry::initialize() > > +{ > > + suspendIfNeeded(); > > +} > > Why do we need these initialize functions instead of calling suspendIfNeeded > directly? > > I’m sure that you thought about a reason, but I don’t know what it is. Would > be nice to have one less function if there’s no abstraction here and > suspendIfNeeded would do the same thing. > > If what we’re trying to mitigate is callers neglecting to call > suspendIfNeeded, I’m not sure that renaming it to initialize will help. If > we do want to tackle that problem there may be some compile time techniques > or some runtime ones, like adding a debug-only boolean set every time > suspendIfNeeded and finding a good place to assert afterward to catch cases > where it wasn’t called even once. Could even use a micro-task to do that > assertion. That was exactly my thought. If I were subclassing a class and looking at an existing subclass, I would see `initialize()` and think "I better call this", but if I saw `suspendIfNeeded()` without knowing necessarily much about ActiveDOMObject, it might not be as clear that it should be called. But this is just me, and it might in fact be a missed opportunity for the person subclassing to ask themselves the right questions and engage with a domain expert about what `suspendIfNeeded()` is. So I'm leaning towards removing initialize() and replacing its calls with `suspendIfNeeded()`. > > Source/WebCore/Modules/fetch/FetchRequest.h:57 > > + static Ref<FetchRequest> create(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&, ResourceRequest&&, FetchOptions&&, String&&); > > Not sure we should have removed the name "referrer". I'll put it back. > > Source/WebCore/Modules/mediastream/RTCDTMFSender.cpp:47 > > + auto result = adoptRef(* new RTCDTMFSender(context, sender, WTFMove(backend))); > > Maybe get rid of the space between the "*" and "new"? Yes, copying-and-pasting without thinking. > > Source/WebCore/workers/service/ServiceWorkerContainer.h:44 > > +#include <wtf/UniqueRef.h> > > Not something that should block landing this patch as-is, but: This should > require only a forward-declaration of UniqueRef, which should be provided by > Forward.h. Files where ServiceWorkerContainer::create are called will need > UniquerRef.h, but this header should not require it. OK, I will switch to a forward declaration and hopefully the bots will prove that is just fine. > > Source/WebCore/workers/service/ServiceWorkerContainer.h:61 > > + static UniqueRef<ServiceWorkerContainer> create(ScriptExecutionContext*, NavigatorBase&); > > Another thing not about this patch: Do you know why this takes > ScriptExecutionContext* rather than ScriptExecutionContext&? Is nullptr OK? I don't know, but the ActiveDOMObject constructor takes a ScriptExecutionContext* and this just passes this along to that constructor: ActiveDOMObject::ActiveDOMObject(ScriptExecutionContext* scriptExecutionContext) : ActiveDOMObject(suitableScriptExecutionContext(scriptExecutionContext), CheckedScriptExecutionContext) { } … which calls this method: static inline ScriptExecutionContext* suitableScriptExecutionContext(ScriptExecutionContext* scriptExecutionContext) { // For detached documents, make sure we observe their context document instead. return is<Document>(scriptExecutionContext) ? &downcast<Document>(*scriptExecutionContext).contextDocument() : scriptExecutionContext; } … and calls this other constructor: inline ActiveDOMObject::ActiveDOMObject(ScriptExecutionContext* context, CheckedScriptExecutionContextType) : ContextDestructionObserver(context) { ASSERT(!is<Document>(context) || &downcast<Document>(context)->contextDocument() == downcast<Document>(context)); if (!context) return; ASSERT(context->isContextThread()); context->didCreateActiveDOMObject(*this); }
Antoine Quint
Comment 24 2021-12-17 00:10:12 PST
Created attachment 447437 [details] Patch for landing
Antoine Quint
Comment 25 2021-12-17 03:58:42 PST
Antoine Quint
Comment 26 2021-12-17 04:11:52 PST
Antoine Quint
Comment 27 2021-12-17 04:23:53 PST
Chris Dumez
Comment 28 2021-12-17 07:26:47 PST
Comment on attachment 447448 [details] Patch r=me
EWS
Comment 29 2021-12-17 07:52:57 PST
Committed r287186 (245353@main): <https://commits.webkit.org/245353@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 447448 [details].
Darin Adler
Comment 30 2021-12-17 10:36:32 PST
We should also consider adding a checker that asserts if we make this error, to make sure this mistake doesn’t creep back in. I have some ideas about how to add that, for any objects that are put into our Ref/RefPtr/UniqueRef after creating them. No idea how to do the same for objects that go into unique_ptr, though.
Note You need to log in before you can comment on or make changes to this bug.