← Chrome — 2026-W37

553b8e73bfef563085d030ccb82acae0a7372bc9  [M154] Fix UAF in WebContentsImpl when dropping fullscreen for dialogs

severity high class UAF CVSS 8.1 signal score 15.81 content/browser/ patch-now AI review CONFIRMED
Structural signals: smartptr_member_added (+6.47), null_guard (+2.59), uaf_lifetime (+4.75), msg_strong (+3.0), area:content/browser/ (+1.0), large_churn (-2.0)

The bug at a glance

A heap use-after-free of a RenderFrameHostImpl is reachable from ordinary web content (JS dialog, beforeunload, file chooser, view-source) because exiting fullscreen can synchronously destroy the initiating frame while WebContentsImpl still holds a raw pointer to it. UAF of a browser-process RenderFrameHostImpl is a classic high-severity sandbox-escape primitive.

A page in fullscreen opens a popup and, from the popup, triggers a JS dialog/beforeunload/file chooser; the forced fullscreen exit runs an observer that removes the initiating iframe from the DOM, freeing its RenderFrameHostImpl, which WebContentsImpl then dereferences.

Root cause

RunJavaScriptDialog, RunBeforeUnloadConfirm, RunFileChooser, and ViewSource call ForSecurityDropFullscreen() to exit HTML fullscreen for security. That call can synchronously dispatch events/observers (e.g. ExitFullscreenModeForTab) that destroy the initiating frame, for example by removing its iframe from the DOM. On return the methods used the raw RenderFrameHostImpl pointer directly, dereferencing freed memory. The fix captures a base::WeakPtr before the drop-fullscreen call and bails out (or guards IsActive()) if the frame was destroyed or deactivated.

Key code

WeakPtr guard across ForSecurityDropFullscreen reentrancy

base::WeakPtr<RenderFrameHostImpl> weak_rfh = render_frame_host->GetWeakPtr();
auto blocker =
    ForSecurityDropFullscreen(/*display_id=*/display::kInvalidDisplayId);
if (!blocker || !weak_rfh || !weak_rfh->IsActive()) {
  return;
}

Patch walkthrough

  • content/browser/web_contents/web_contents_impl.cc — Captures a base::WeakPtr before each ForSecurityDropFullscreen() call in ViewSource, RunJavaScriptDialog, RunBeforeUnloadConfirm, and RunFileChooser, and returns early if the frame was freed or is no longer active; also switches later uses (ComputeIsolationInfoForNavigation, delegate_->RunFileChooser) to the weak pointer instead of the raw pointer.
  • content/browser/web_contents/web_contents_impl.h — Changes RunFileChooser’s parameter from RenderFrameHost* to RenderFrameHostImpl* so a WeakPtr can be taken for the reentrancy guard.
  • content/browser/web_contents/web_contents_impl_browsertest.cc — Adds regression tests using a delegate that removes the iframe during ExitFullscreenModeForTab, asserting the child RFH’s WeakPtr becomes null without a crash for both the dialog and beforeunload paths.

Background

ForSecurityDropFullscreen — Browser-process routine that exits HTML fullscreen across related WebContents before showing a dialog, which can run observers/events reentrantly.

RenderFrameHostImpl — Browser-process object representing a renderer frame; freeing it while a raw pointer is held yields a UAF.

base::WeakPtr guard — Chromium idiom to detect object destruction across a reentrant call before dereferencing again.

Vulnerability window

  1. Reachability — Fullscreen WebContents with a related popup frame that initiates a dialog/beforeunload/file chooser.
  2. Trigger — ForSecurityDropFullscreen() exits fullscreen and synchronously dispatches an observer that removes the initiating iframe.
  3. Primitive — The initiating RenderFrameHostImpl is freed, then dereferenced via the stale raw pointer (ComputeIsolationInfoForNavigation / delegate call).
  4. Escalation — Controlling reuse of the freed RFH allocation could turn the UAF into a browser-process read/write (inferred, not shown).

Proof of concept

Conceptually: open a popup from a fullscreen opener; in the popup add an about:blank iframe; from that child frame call alert()/confirm() (or trigger beforeunload or a file input). Install a delegate/observer on the opener that, on fullscreen exit, removes the iframe (child.remove()). The forced fullscreen exit frees the child RenderFrameHostImpl before WebContentsImpl re-uses the raw pointer. No exploit code provided.

Exploitation

  1. Setup — Enter fullscreen, spawn a related popup, and create a subframe that will initiate the dialog.
  2. Trigger — Initiate a JS dialog/beforeunload/file chooser so ForSecurityDropFullscreen() runs.
  3. Free — During fullscreen-exit observers, detach the initiating frame to free its RenderFrameHostImpl (inferred control over timing).
  4. Use — Stale raw pointer is dereferenced in the browser process, the UAF (inferred escalation via heap grooming).

Detection & hunting

For defenders and detection engineers:

  • weggli/grep: raw RenderFrameHost used after a call to ForSecurityDropFullscreen() without a WeakPtr/IsActive() recheck* — Flag any method that keeps a raw frame pointer across a call known to run observers/events.
  • ASAN heap-use-after-free in WebContentsImpl::Run{JavaScriptDialog,BeforeUnloadConfirm,FileChooser}/ViewSource — Fuzz dialog paths with frames detached during fullscreen exit.

Audit directions

  • All WebContentsImpl callers of ForSecurityDropFullscreen() and similar reentrant ‘drop fullscreen’/event-dispatching helpers — Verify every post-call use of a frame/host pointer is WeakPtr-guarded.
  • Other browser-process code that holds RenderFrameHostImpl across potentially frame-destroying synchronous callbacks* — Audit for raw-pointer reuse after observer or delegate dispatch.

Before / after

Loading diff…