← WebKit — 2026-W35

9bff36df8c41a76485e23c663255b827edd84e16  [Site Isolation] Frame process is terminated before pageswap and unload event handlers run

severity low class UAF confidence 0.3 signal score 6.47 Source/WebKit/UIProcess
Primitive: FrameProcess torn down before old document finishes dispatching unload events during cross-process commit
Triage note: commitProvisionalFrame/commitProvisionalPage switch send() to sendWithAsyncReply() and hold a FrameProcessRefWithExpiration (1s timer) to keep the old FrameProcess alive until the LoadDidCommitInAnotherProcess reply. Reads mainly as event-dispatch correctness for site isolation; a lifetime/UAF angle is possible but not clearly demonstrated.
Structural signals: smartptr_member_added (+6.47)

Commit message

[Site Isolation] Frame process is terminated before pageswap and unload event handlers run
https://bugs.webkit.org/show_bug.cgi?id=322285
rdar://185522165

Reviewed by Per Arne Vollan.

When cross-site subframe navigation that swaps process happens, it is noted that event handlers of pageswap, pagehide
or unload event don't run or don't run to completion. This is observable especially when the even handler post message.
This is because WebFrameProxy::commitProvisionalFrame() sends LoadDidCommitInAnotherProcess to the outgoing process and
then immediately calls setProcess(), destroying the frame's old FrameProcess while that message is still in flight. That
disconnects the process's RemotePageProxy, so anything a handler sends arrives in the UI process with no receiver, and
it shuts the process down outright when this was its last frame.

To fix this, give LoadDidCommitInAnotherProcess message an async reply and let UI process release the reference of the
old FrameProcess after receiving reply. On the receiver web process site, dispatching pageswap and, through
FrameTree::replaceChild() -> detachFromParent() ->  closeURL(), pagehide and unload, all happen before the reply is
sent. Since a web process can hang itself from inside one of those handlers (e.g. not sending the reply), the reference
in UI process side also expires after a second.

This fix helps remove a FIXME in API test SiteIsolation.Events, and ensures the test has the same behavior in both full
SI and shared process mode.

* Source/WebKit/UIProcess/WebFrameProxy.cpp:
(WebKit::FrameProcessRefWithExpiration::create):
(WebKit::FrameProcessRefWithExpiration::FrameProcessRefWithExpiration):
(WebKit::WebFrameProxy::commitProvisionalFrame):
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::commitProvisionalPage):
* Source/WebKit/WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::loadDidCommitInAnotherProcess):
* Source/WebKit/WebProcess/WebPage/WebPage.h:
* Source/WebKit/WebProcess/WebPage/WebPage.messages.in:
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/SiteIsolation.mm:
(TestWebKitAPI::(SiteIsolation, Events)):

Canonical link: https://commits.webkit.org/319732@main

Before / after

Loading diff…