d402226fba320b5bc7f7de9157234a66bd6e84d5 [M154] [FedCM] Fix UAF in content::webid::Request
The bug at a glance
Use-after-free in the FedCM content::webid::Request object in the browser process. ShowModalDialog can synchronously re-enter page script (via fullscreen exit / blur), which can detach the initiating iframe and destroy the Request; the code then kept using member fields of the freed object. A browser-process UAF reachable from an attacker-controlled renderer is a high-severity, potentially RCE-grade sandbox-escape primitive, mitigated somewhat by needing a FedCM flow and precise timing.
Browser-initiated navigation to show a FedCM modal/popup can exit fullscreen or steal focus, firing synchronous fullscreenchange/blur handlers in the page. Those handlers can detach the initiating iframe, which destroys the Request mid-call, leaving the subsequent member-field accesses operating on freed memory.
Root cause
In Request::ShowModalDialog and Request::ClickErrorDialogMoreDetailsForDevtools, the code calls into the dialog controller (ShowModalDialog) which opens a popup or foreground tab. Opening that window can synchronously cause the initiating tab to leave fullscreen or lose focus, dispatching JS events in the page. An attacker’s handler detaches the initiating iframe, tearing down the FedCM request and freeing the Request object. Upon return, the original frame continues to touch member state (did_show_ui_ = true, GetDialogController(), config_url_, token_error_, token_request_status_), i.e. a classic re-entrancy UAF. The fix captures a base::WeakPtr before the call and returns early if the Request was destroyed.
Key code
WeakPtr liveness guard inserted around ShowModalDialog so member access is skipped when re-entrant script destroys the Request.
base::WeakPtr<Request> weak_this = weak_ptr_factory_.GetWeakPtr();
WebContents* web_contents = GetDialogController()->ShowModalDialog(...);
if (!weak_this) {
return;
}
did_show_ui_ = true; // previously ran even if |this| was freed
Patch walkthrough
content/browser/webid/request.cc— Two call sites are hardened. In the primary ShowModalDialog path, a WeakPtr to this is taken before GetDialogController()->ShowModalDialog(…); after the call it checks if (!weak_this) return; before setting did_show_ui_ and touching further state. In ClickErrorDialogMoreDetailsForDevtools, the same WeakPtr guard wraps the ShowModalDialog(kErrorUrlPopup, …) call before OnDismissErrorDialog is invoked. The guard prevents dereferencing a Request destroyed by synchronous script during the dialog-opening navigation.content/browser/webid/request_unittest.cc— Adds RequestTest.FrameDetachDuringShowModalDialog: a TestDialogController’s ShowModalDialog mock synchronously navigates the WebContents to another origin, destroying the Request during the call, then asserts EXPECT_FALSE(request_) and that no UAF occurs. This models the frame-detach re-entrancy that triggered the bug.
Background
FedCM / content::webid::Request — Federated Credential Management browser-process state machine driving the identity-provider sign-in UI. Request owns dialog interactions and callbacks for a single FedCM invocation initiated by a renderer frame.
base::WeakPtr liveness guard — Idiomatic Chromium pattern: capture GetWeakPtr() before a call that may re-enter and destroy this, then check the WeakPtr afterward to detect self-destruction and bail out safely.
Re-entrant script via fullscreen/focus change — Opening a popup or foreground tab can cause the initiating tab to exit fullscreen or lose focus, which synchronously dispatches fullscreenchange/blur handlers in the page during a browser-side call.
Fixed/Bug references — Fixed: 552751639; cherry-pick to M154 (Bug: 556055329,552751639). Indicates a tracked security-relevant fix merged to a release branch.
Vulnerability window
- Introduction — ShowModalDialog paths accessed Request members after invoking the dialog controller without accounting for synchronous re-entrant destruction of the frame/Request. (inferred)
- Discovery — Issue 552751639 filed; reproduced by frame detachment during ShowModalDialog.
- Fix — WeakPtr guards added at both call sites (commit 5d16106435), with a regression unit test.
- Merge — Cherry-picked to M154 release branch 8037 as d402226fba.
Proof of concept
Conceptual only. Trigger a FedCM request (navigator.credentials.get with an identity provider) from within an iframe while the page is in fullscreen or focused; register a fullscreenchange/blur handler that removes/detaches the initiating iframe. When the browser opens the FedCM modal/popup, the exit-fullscreen or blur event fires synchronously, the handler detaches the frame, and the Request is destroyed; pre-patch, the browser then reads freed Request member state. Reliable exploitation requires heap grooming and precise event timing, which are not reconstructable from the diff.
Exploitation
- Setup — Attacker page embeds an iframe and arranges fullscreen or focus so that opening a browser popup will trigger a synchronous fullscreenchange/blur event. (inferred)
- Trigger — Initiate a FedCM flow that reaches Request::ShowModalDialog so the browser opens the modal/popup, dispatching the re-entrant event. (inferred)
- Free — The event handler detaches the initiating iframe, destroying the content::webid::Request object. (inferred)
- Use — Control returns and the browser touches freed Request members (did_show_ui_, GetDialogController(), config_url_, token_error_), yielding a browser-process UAF with potential for controlled corruption after heap reclamation. (inferred)
Detection & hunting
For defenders and detection engineers:
- ASan heap-use-after-free in content::webid::Request —
- FedCM flow + synchronous frame detach —
Audit directions
- content/browser/webid/ — Audit all Request methods that call into the dialog controller or trigger browser-initiated navigation for post-call member access without a WeakPtr/liveness check; the same re-entrancy class may affect OnDialogDismissed, OnNativeAppResult, and continuation-popup paths.
- Browser-process UI that opens popups/tabs — Review any browser-side state object whose method opens a window or exits fullscreen and then dereferences this; such calls can synchronously re-enter renderer script and destroy the object.