REGRESSION(313207@main): Web Inspector: ProxyingNetworkAgent leaks IPC::MessageReceiver after cross-origin iframe process swap#64973
Conversation
|
EWS run on current version of this PR (hash ea5988f) Details |
…C::MessageReceiver after cross-origin iframe process swap https://bugs.webkit.org/show_bug.cgi?id=314808 rdar://177056433 Reviewed by Abrar Rahman Protyasha. Two distinct teardown bugs surface from the same cross-origin iframe process swap path under Site Isolation. Both manifest as flaky crashes attributed to whatever non-SI test runs after a Web Inspector layout test in WKTR's run order, which is why http/tests/ssl/applepay/ApplePayButton.html was the visible victim. The first issue is that WebPageInspectorController::didCommitProvisionalFrame re-instruments the new WebContent process for network events but never removes the old process's instrumentation. The (oldProcessID, oldPageID) entry stays in ProxyingNetworkAgent::m_instrumentedProcessPageCounts and its IPC::MessageReceiver registration on the old WebProcessProxy outlives the agent. ~ProxyingNetworkAgent then asserts in ~IPC::MessageReceiver during the next WKWebView teardown. Thread the iframe's old pageID from WebFrameProxy::commitProvisionalFrame through to didCommitProvisionalFrame, and disable instrumentation for the old process before re-enabling on the new one. The second issue is that FrameInspectorTarget::connect() chooses provisionalFrame() ?: coreLocalFrame() and registers the frontend channel with that frame's inspector controller. disconnect() makes the same choice independently. Under Site Isolation those two choices can resolve to different LocalFrames -- a provisional load can start after connect, or the previously-provisional frame commits and replaces coreLocalFrame(). Disconnecting from a controller that never registered the channel asserts in FrontendRouter::disconnectFrontend. Pin a WeakPtr<LocalFrame> at connect time and disconnect from that same frame's controller. Test changes: - evaluate-in-cross-origin-iframe.html (was [ Failure Crash ]) now passes. - execution-context-from-frame-target.html (was [ Failure ]) now passes. * Source/WebKit/UIProcess/WebFrameProxy.cpp: (WebKit::WebFrameProxy::commitProvisionalFrame): * Source/WebKit/UIProcess/Inspector/WebPageInspectorController.h: * Source/WebKit/UIProcess/Inspector/WebPageInspectorController.cpp: (WebKit::WebPageInspectorController::didCommitProvisionalFrame): * Source/WebKit/WebProcess/Inspector/FrameInspectorTarget.h: * Source/WebKit/WebProcess/Inspector/FrameInspectorTarget.cpp: (WebKit::FrameInspectorTarget::connect): (WebKit::FrameInspectorTarget::disconnect): * LayoutTests/platform/mac-wk2/TestExpectations: Canonical link: https://commits.webkit.org/313318@main
ea5988f to
04f8f8e
Compare
|
Committed 313318@main (04f8f8e): https://commits.webkit.org/313318@main Reviewed commits have been landed. Closing PR #64973 and removing active labels. |
the-chenergy
left a comment
There was a problem hiding this comment.
I question the validity of the second issue you brought up in the commit message, specifically whether these two things are actually what happened to cause the bug:
The second issue is that
FrameInspectorTarget::connect()chooses
provisionalFrame() ?: coreLocalFrame()and registers the frontend channel
with that frame's inspector controller.disconnect()makes the same
choice independently. Under Site Isolation those two choices can resolve
to differentLocalFrames — a provisional load can start afterconnect,
or the previously-provisional frame commits and replacescoreLocalFrame().
Disconnecting from a controller that never registered the channel asserts
inFrontendRouter::disconnectFrontend. Pin aWeakPtr<LocalFrame>at
connect time and disconnect from that same frame's controller.
Regarding "the previously-provisional frame commits and replaces coreLocalFrame()": This clearly does not change what ?: returns; the same LocalFrame simply migrates from m_provisionalFrame into m_coreFrame in WebFrame::commitProvisionalFrame
Regarding "a provisional load can start after connect":
- First, I doubt this can even happen in practice because
ProvisionalFrameProxyconstructor sendsCreateProvisionalFrameto that web process with theWebFramein question beforeWebPageInspectorController::didCreateProvisionalFrameruns and the target agent auto-connects with theConnectInspectormessage. These two messages will arrive in this order, andWebFrame::createProvisionalFrameensures that am_provisionalFrameexists beforeFrameInspectorTarget::connectruns - Second, if we're worried about a new provisional load happening after
connecthas been run, and the internal states of theWebFramechanges unexpectedly, I don't think using a pin (recording theWeakPtr<LocalFrame>) would be the right solution either. An unexpected state change inWebFramewould be a bug we should properly address in our provisional target system (e.g. by surfacing a new provisional target and destroying the old one in time) rather than bypass it with the pin
Can you share more about how the stacktrace or logs confirms the issue in FrameInspectorTarget? If neither of the two scenarios is what actually happened, I'd rather us landing the network-leak fix first and splitting any changes in provisional frame targets' handling to a later patch
| protect(process())->send(Messages::WebPage::LoadDidCommitInAnotherProcess(frameID, m_layerHostingContextIdentifier, nullptr), *webPageIDInCurrentProcess()); | ||
|
|
||
| WebCore::ProcessIdentifier oldProcessID = process().coreProcessIdentifier(); | ||
| std::optional<WebCore::PageIdentifier> oldPageID = webPageIDInCurrentProcess(); |
There was a problem hiding this comment.
Since two lines above we're already unconditionally doing *webPageIDInCurrentProcess(), we shouldn't need the std::optional here (and therefore the parameter of didCommitProvisionalFrame) because we're asserting the id always exists at this point already
04f8f8e
ea5988f
🧪 win-tests