← Chrome — 2026-W37

4e5229cb51160fe5d77b020d3d6e4366f50d7f46  [M152] Prevent UAF during re-entrant destruction in IME callouts.

severity high class UAF CVSS 8.1 signal score 10.4 content/browser/ patch-now AI review CONFIRMED
Structural signals: smartptr_member_added (+3.0), null_guard (+1.2), uaf_lifetime (+2.2), msg_strong (+3.0), area:content/browser/ (+1.0)

The bug at a glance

A genuine use-after-free in the browser process: IME callouts can re-entrantly destroy the RenderWidgetHostViewAura / updated_view and free the TextInputState that the function keeps using after the callout returns. It is a cherry-pick to the M152 security branch (Bug 559510415,553133215) with weak-pointer guards added, consistent with a security-serviced UAF.

An attacker who can influence focus/IME state (e.g. via a renderer-driven text input update) can cause an IME callout such as SetVirtualKeyboardVisibilityIfEnabled or NotifySelectionBoundsChanged to re-entrantly destroy the view and free its text input state, leaving the browser process dereferencing a dangling ’this’, ‘updated_view’, or ‘state’ pointer – a browser-process UAF, the most valuable sandbox-escape surface.

Root cause

In RenderWidgetHostViewAura::OnUpdateTextInputStateCalled, several IME callouts (OnTextInputTypeChanged, SetVirtualKeyboardVisibilityIfEnabled true/false, UpdateTextInputState, NotifySelectionBoundsChanged) can pump messages and re-entrantly destroy the view or unregister updated_view, freeing the TextInputState* ‘state’. The original code only checked weak_this after a couple of callouts and never re-fetched ‘state’, so later code dereferenced a freed state object or a destroyed updated_view. The patch adds a check_alive() lambda validating weak_this, a new weak pointer to updated_view, text_input_manager_ liveness, and IsRegistered(updated_view) after each callout, and re-fetches state = text_input_manager_->GetTextInputState() after each callout so stale/freed state is never used.

Key code

Weak-pointer liveness check and state re-fetch after each IME callout

+  auto check_alive = [&]() -> bool {
+    if (!weak_this || !weak_updated_view || !text_input_manager_ ||
+        !text_input_manager_->IsRegistered(updated_view)) {
+      return false;
+    }
+    return true;
+  };
...
+    // ... freeing the object `state` points to.
+    state = text_input_manager_->GetTextInputState();

Patch walkthrough

  • content/browser/renderer_host/render_widget_host_view_aura.cc — Adds a weak pointer to updated_view and a check_alive() lambda (weak_this, weak_updated_view, text_input_manager_ registration) invoked after every IME callout, and re-fetches ‘state’ from the TextInputManager after each callout so a view/state freed re-entrantly during the callout is never dereferenced – directly closing the use-after-free.
  • content/browser/renderer_host/render_widget_host_view_aura_unittest.cc — Adds a MockInputMethod override and a regression test (DestroyDuringSetVirtualKeyboardVisibilityIfEnabled) that synchronously destroys the view inside SetVirtualKeyboardVisibilityIfEnabled to exercise the re-entrant-destruction path the fix guards.

Background

RenderWidgetHostViewAura (RWHVA) — The Aura-platform browser-process view hosting a renderer widget and bridging to the OS InputMethod/IME.

IME callout — A call into the platform input method (OnTextInputTypeChanged, SetVirtualKeyboardVisibilityIfEnabled, NotifySelectionBoundsChanged) that can run nested message loops and re-enter browser code.

TextInputManager / TextInputState — Browser-side owner of per-view text input state; the ‘state’ pointer can be invalidated when a view is unregistered/destroyed.

WeakPtr liveness check — base::WeakPtr becomes null when the pointee is destroyed, letting re-entrancy-prone code detect that ’this’ or a tracked object was freed.

Vulnerability window

  1. Reachability — A text input state update (influenced by renderer-side focus/IME changes) invokes OnUpdateTextInputStateCalled, which issues IME callouts.
  2. Trigger — An IME callout (e.g. SetVirtualKeyboardVisibilityIfEnabled) pumps messages and re-entrantly destroys the RWHVA/updated_view, freeing TextInputState.
  3. Primitive — On unwind the function dereferences the dangling ’this’/‘updated_view’/‘state’, producing a browser-process use-after-free read/write (inferred exploit value).
  4. Escalation — A controlled UAF in the browser process is a classic sandbox-escape primitive if the freed allocation can be reclaimed with attacker-controlled data (inferred).

Proof of concept

Conceptually, drive the view into a focused text-input state so OnUpdateTextInputStateCalled reaches the virtual-keyboard branch, then arrange for the IME callout (SetVirtualKeyboardVisibilityIfEnabled / NotifySelectionBoundsChanged) to run a nested loop that destroys the view (the unit test models this by destroying view_ inside the callout). Pre-patch the subsequent dereference of ‘state’/‘updated_view’ hits freed memory. No exploit code is provided.

Exploitation

  1. Grooming — Attacker arranges heap state so the freed TextInputState/view allocation can be reclaimed with controlled bytes (inferred).
  2. Trigger — Induce re-entrant view destruction inside an IME callout during a text-input-state update (inferred).
  3. Use — Post-callout code reads/writes through the dangling ‘state’ or ‘updated_view’, corrupting browser-process memory (inferred).

Detection & hunting

For defenders and detection engineers:

  • grep for IME/callout sites holding raw TextInputState/view pointers across SetVirtualKeyboardVisibilityIfEnabled/NotifySelectionBoundsChanged without a WeakPtr re-check* — Hunt browser-process code that calls into platform input methods and then dereferences pre-callout pointers; require weak-pointer validation and state re-fetch after each callout.

Audit directions

  • Other RWHVA / RenderWidgetHostViewBase IME and focus paths, and other platform views (Win TSF, ChromeOS keyboard controller) — Look for functions that invoke message-pumping IME callouts and then use cached ’this’/view/state pointers without re-validating liveness.

Before / after

Loading diff…