Overview

High
Severity
9.6
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Core
DescriptionUse after free in Core
ComponentCore
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker553133215
Fix commit77d3b6745972 (chromium/src) +84/-5
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-08-26 (per vendor advisory)
Fix landed2026-09-09 (public git)
Disclosed2026-09-15
Patch gap6 days — fix public in source before the advisory

Background

`RenderWidgetHostViewAura` (RWHVA)
the Aura-platform browser-process view object that hosts a renderer widget and mediates IME/text-input interactions with the platform input method.
IME callout
a call from the view into GetInputMethod() (e.g. OnTextInputTypeChanged, SetVirtualKeyboardVisibilityIfEnabled, NotifySelectionBoundsChanged) that can synchronously run observers, message pumps, or the ChromeOS keyboard show/hide cascade and thus re-enter browser code.
`TextInputManager` / `state`
the manager that tracks the active view’s text-input state; state is a raw pointer returned by text_input_manager_->GetTextInputState() that is owned by, and freed with, the registered active view’s tracked entry.
`base::WeakPtr`
a pointer that automatically nulls when its referent is destroyed, used here to detect re-entrant deletion of this or of updated_view.

Root Cause Analysis

The edited function (identified from the shipped test comment as RenderWidgetHostViewAura::OnUpdateTextInputStateCalled, (inferred) since the name is not in the hunk context) caches a raw state pointer from text_input_manager_->GetTextInputState() and then performs several IME callouts. Each of these callouts — OnTextInputTypeChanged, SetVirtualKeyboardVisibilityIfEnabled(false/true), virtual_keyboard_controller_win_->UpdateTextInputState, and NotifySelectionBoundsChanged — can re-entrantly run code that destroys the view (this or updated_view) or unregisters/updates the active view, freeing the object state points at.

Pre-patch, the function only re-checked weak_this after two of the callouts and never re-fetched state, so after a re-entrant destruction execution continued to dereference the freed state (e.g. state->type != ui::TEXT_INPUT_TYPE_NONE) and the freed updated_view (updated_view->host()).

The fix introduces a check_alive() lambda that validates weak_this, a new weak_updated_view, a non-null text_input_manager_, and that updated_view is still registered, invoking it after every callout and re-fetching state = text_input_manager_->GetTextInputState() so no stale pointer survives a re-entrant mutation.

The patch strictly TIGHTENS the liveness checks: it replaces the weak_this-only guard with a broader multi-condition guard and adds guards on the previously-unchecked virtual-keyboard callouts.

Key insight
The single mistake is trusting a cached raw state pointer and a this-only liveness check across IME callouts that can re-entrantly free both the view and the text-input state. The fix converts every callout into a checkpoint that revalidates all involved objects and re-reads state, so a stale/freed pointer is never dereferenced.

Attack Path

  1. Reach the text-input update path The renderer sends a text-input state update (type TEXT_INPUT_TYPE_TEXT with show_ime_if_needed set), driving TextInputManager::UpdateTextInputState into the view’s OnUpdateTextInputStateCalled, which caches state and enters the virtual-keyboard branch.
  2. Trigger a re-entrant IME callout Because the view is the focused text input client, the function calls GetInputMethod()->SetVirtualKeyboardVisibilityIfEnabled(true), which on Aura/ChromeOS runs the keyboard visibility cascade (observers / message pump).
  3. Destroy the view during the callout That cascade synchronously destroys updated_view/this (the regression test models this by destroying view_ inside SetVirtualKeyboardVisibilityIfEnabled), freeing the RWHVA and the TextInputState object state referenced.
  4. Use-after-free on unwind Control returns to the (pre-patch) function, which dereferences the freed state (state->type) and later the freed updated_view (updated_view->host()), a use-after-free in the browser process.
  5. Escalation Turning this dangling read/access into a controlled read/write or code execution (inferred) — the patch demonstrates only the dangling dereference, not a specific corruption primitive.

Impact Assessment

An attacker who can influence text-input state and induce a re-entrant view/state destruction during an IME callout gains a use-after-free of a freed RenderWidgetHostViewAura and its TextInputState in the privileged browser process. Preconditions include the view being the focused text-input client and an environment where the virtual-keyboard callout re-enters and destroys the view (the test cites ChromeOS’s KeyboardUIController show/hide cascade). The advisory classifies this as a UAF in Core; any escalation to a controlled memory-corruption primitive or code execution is (inferred) and not established by the patch, and no exploited-in-the-wild status is stated.

Changed Functions

FunctionChangeNotes
if
content/browser/renderer_host/render_widget_host_view_aura.cc
modified
if
content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
modified
BindLambdaForTesting
content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
modified

Files Changed

  • content/browser/renderer_host/render_widget_host_view_aura.cc
  • content/browser/renderer_host/render_widget_host_view_aura_unittest.cc

Audit Directions

  • Cached owner-owned pointers across re-entrant callouts
    Audit any function that caches a raw pointer (like state) obtained from a manager and then calls out to code that can mutate/free the owner’s entry; re-fetch after every such call rather than reusing the cached value.
  • Partial liveness checks
    Look for WeakPtr guards that validate only this while other objects (updated_view, text_input_manager_) used later on the same path can also be freed; every object dereferenced after a callout needs its own liveness/registration check.
  • IME / virtual-keyboard callout chains
    Review other GetInputMethod() and TextInputManager callouts (e.g. NotifySelectionBoundsChanged, OnTextInputTypeChanged, UpdateTextInputState) and platform keyboard controllers that can synchronously re-enter and destroy views, especially on Aura/ChromeOS.
From 77d3b674597221f343a95f95caaf04f1c1a66ea8 Mon Sep 17 00:00:00 2001
From: Dave Tapuska <[email protected]>
Date: Wed, 09 Sep 2026 08:20:50 -0700
Subject: [PATCH] Prevent UAF during re-entrant destruction in IME callouts.

During IME callouts (such as SetVirtualKeyboardVisibilityIfEnabled or
NotifySelectionBoundsChanged), the RenderWidgetHostViewAura or the
updated view can be re-entrantly destroyed or unregistered. This CL adds
weak pointer checks and re-fetches the text input state after these
callouts to prevent accessing freed memory. A unit test is added to
verify the fix when destruction occurs during virtual keyboard
visibility updates.

Bug: 553133215
Change-Id: I4a6fc2c50133c731dad652c07906e6aa597c82dc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8351288
Reviewed-by: Kartar Singh <[email protected]>
Commit-Queue: Dave Tapuska <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1694603}
---
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index 277febb..f839fc1 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -3503,15 +3503,26 @@
     bool did_update_state) {
   CHECK_EQ(text_input_manager_, text_input_manager);
 
-  if (!GetInputMethod())
+  if (!GetInputMethod()) {
     return;
+  }
 
   auto weak_this = weak_ptr_factory_.GetWeakPtr();
+  base::WeakPtr<RenderWidgetHostViewBase> weak_updated_view =
+      updated_view ? updated_view->GetWeakPtr() : nullptr;
+
+  auto check_alive = [&]() -> bool {
+    if (!weak_this || !weak_updated_view || !text_input_manager_ ||
+        !text_input_manager_->IsRegistered(updated_view)) {
+      // `this` or `updated_view` may have been deleted inside the IME callout.
+      return false;
+    }
+    return true;
+  };
 
   if (did_update_state) {
     GetInputMethod()->OnTextInputTypeChanged(this);
-    if (!weak_this) {
-      // `this` may have been deleted inside the IME callout.
+    if (!check_alive()) {
       return;
     }
   }
@@ -3528,6 +3539,12 @@
                ui::mojom::VirtualKeyboardVisibilityRequest::HIDE) {
       GetInputMethod()->SetVirtualKeyboardVisibilityIfEnabled(false);
     }
+    if (!check_alive()) {
+      return;
+    }
+    // The IME callout may have re-entrantly updated or unregistered the
+    // active view's state, freeing the object `state` points to.
+    state = text_input_manager_->GetTextInputState();
   }
 #endif
 
@@ -3538,6 +3555,12 @@
     if (state->show_ime_if_needed &&
         GetInputMethod()->GetTextInputClient() == this) {
       GetInputMethod()->SetVirtualKeyboardVisibilityIfEnabled(true);
+      if (!check_alive()) {
+        return;
+      }
+      // The IME callout may have re-entrantly updated or unregistered the
+      // active view's state, freeing the object `state` points to.
+      state = text_input_manager_->GetTextInputState();
     }
 // TODO(crbug.com/40110609): Remove this once TSF fix for input pane policy
 // is serviced
@@ -3549,6 +3572,12 @@
                                                            GetInputMethod());
       }
       virtual_keyboard_controller_win_->UpdateTextInputState(state);
+      if (!check_alive()) {
+        return;
+      }
+      // The IME callout may have re-entrantly updated or unregistered the
+      // active view's state, freeing the object `state` points to.
+      state = text_input_manager_->GetTextInputState();
     }
 #endif
   }
@@ -3556,10 +3585,12 @@
   // Ensure that selection bounds changes are sent to the IME.
   if (state && state->type != ui::TEXT_INPUT_TYPE_NONE) {
     text_input_manager->NotifySelectionBoundsChanged(updated_view);
-    if (!weak_this) {
-      // `this` may have been deleted inside the IME callout.
+    if (!check_alive()) {
       return;
     }
+    // The IME callout may have re-entrantly updated or unregistered the
+    // active view's state, freeing the object `state` points to.
+    state = text_input_manager_->GetTextInputState();
   }
 
   if (auto* render_widget_host = updated_view->host()) {
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 33ed433..b74f54f 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -7045,6 +7045,13 @@
     }
   }
 
+  void SetVirtualKeyboardVisibilityIfEnabled(bool should_show) override {
+    ui::MockInputMethod::SetVirtualKeyboardVisibilityIfEnabled(should_show);
+    if (on_set_virtual_keyboard_visibility_if_enabled_) {
+      std::move(on_set_virtual_keyboard_visibility_if_enabled_).Run();
+    }
+  }
+
   void DetachTextInputClient(ui::TextInputClient* client) override {
     ui::MockInputMethod::DetachTextInputClient(client);
     if (on_detach_text_input_client_) {
@@ -7060,6 +7067,11 @@
     on_text_input_type_changed_ = std::move(closure);
   }
 
+  void set_on_set_virtual_keyboard_visibility_if_enabled(
+      base::OnceClosure closure) {
+    on_set_virtual_keyboard_visibility_if_enabled_ = std::move(closure);
+  }
+
   void set_on_detach_text_input_client(base::OnceClosure closure) {
     on_detach_text_input_client_ = std::move(closure);
   }
@@ -7067,6 +7079,7 @@
  private:
   base::OnceClosure on_caret_bounds_changed_;
   base::OnceClosure on_text_input_type_changed_;
+  base::OnceClosure on_set_virtual_keyboard_visibility_if_enabled_;
   base::OnceClosure on_detach_text_input_client_;
 };
 
@@ -7155,6 +7168,41 @@
   GetTextInputManager(view_)->UpdateTextInputState(view_, state);
 }
 
+// RWHVA::OnUpdateTextInputStateCalled() calls
+// GetInputMethod()->SetVirtualKeyboardVisibilityIfEnabled(true) when the state
+// indicates that the virtual keyboard should be shown. If an observer or
+// virtual keyboard controller pumps messages and the view is destroyed
+// re-entrantly (e.g. on ChromeOS where KeyboardUIController runs the keyboard
+// show/hide cascade), on unwind the function must not dereference freed view
+// pointers or stale text input state.
+TEST_F(RenderWidgetHostViewAuraReentrantDestructionIME,
+       DestroyDuringSetVirtualKeyboardVisibilityIfEnabled) {
+  InitViewForFrame(nullptr);
+  ParentHostView(view_, parent_view_);
+  // `view_` shares the root window (and thus the InputMethod) with
+  // `parent_view_`.
+  ASSERT_EQ(static_cast<ui::InputMethod*>(input_method_.get()),
+            GetInputMethod());
+
+  // The virtual-keyboard branch requires the view to be the InputMethod's
+  // focused text input client.
+  input_method_->SetFocusedTextInputClient(view_.get());
+
+  // Arrange for the view to be synchronously destroyed inside
+  // SetVirtualKeyboardVisibilityIfEnabled.
+  input_method_->set_on_set_virtual_keyboard_visibility_if_enabled(
+      base::BindLambdaForTesting([&]() {
+        widget_host_ = nullptr;
+        view_.ExtractAsDangling()->Destroy();
+      }));
+
+  ui::mojom::TextInputState state;
+  state.type = ui::TEXT_INPUT_TYPE_TEXT;
+  state.mode = ui::TEXT_INPUT_MODE_TEXT;
+  state.show_ime_if_needed = true;
+  GetTextInputManager(view_.get())->UpdateTextInputState(view_.get(), state);
+}
+
 // RWHVA::OnWindowFocused()'s lost-focus branch calls
 // DetachFromInputMethod(false). On Windows this reaches
 // TSFBridge::RemoveFocusedClient() -> ITfThreadMgr::AssociateFocus(), a
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 33ed433..b74f54f 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -7045,6 +7045,13 @@
     }
   }
 
+  void SetVirtualKeyboardVisibilityIfEnabled(bool should_show) override {
+    ui::MockInputMethod::SetVirtualKeyboardVisibilityIfEnabled(should_show);
+    if (on_set_virtual_keyboard_visibility_if_enabled_) {
+      std::move(on_set_virtual_keyboard_visibility_if_enabled_).Run();
+    }
+  }
+
   void DetachTextInputClient(ui::TextInputClient* client) override {
     ui::MockInputMethod::DetachTextInputClient(client);
     if (on_detach_text_input_client_) {
@@ -7060,6 +7067,11 @@
     on_text_input_type_changed_ = std::move(closure);
   }
 
+  void set_on_set_virtual_keyboard_visibility_if_enabled(
+      base::OnceClosure closure) {
+    on_set_virtual_keyboard_visibility_if_enabled_ = std::move(closure);
+  }
+
   void set_on_detach_text_input_client(base::OnceClosure closure) {
     on_detach_text_input_client_ = std::move(closure);
   }
@@ -7067,6 +7079,7 @@
  private:
   base::OnceClosure on_caret_bounds_changed_;
   base::OnceClosure on_text_input_type_changed_;
+  base::OnceClosure on_set_virtual_keyboard_visibility_if_enabled_;
   base::OnceClosure on_detach_text_input_client_;
 };
 
@@ -7155,6 +7168,41 @@
   GetTextInputManager(view_)->UpdateTextInputState(view_, state);
 }
 
+// RWHVA::OnUpdateTextInputStateCalled() calls
+// GetInputMethod()->SetVirtualKeyboardVisibilityIfEnabled(true) when the state
+// indicates that the virtual keyboard should be shown. If an observer or
+// virtual keyboard controller pumps messages and the view is destroyed
+// re-entrantly (e.g. on ChromeOS where KeyboardUIController runs the keyboard
+// show/hide cascade), on unwind the function must not dereference freed view
+// pointers or stale text input state.
+TEST_F(RenderWidgetHostViewAuraReentrantDestructionIME,
+       DestroyDuringSetVirtualKeyboardVisibilityIfEnabled) {
+  InitViewForFrame(nullptr);
+  ParentHostView(view_, parent_view_);
+  // `view_` shares the root window (and thus the InputMethod) with
+  // `parent_view_`.
+  ASSERT_EQ(static_cast<ui::InputMethod*>(input_method_.get()),
+            GetInputMethod());
+
+  // The virtual-keyboard branch requires the view to be the InputMethod's
+  // focused text input client.
+  input_method_->SetFocusedTextInputClient(view_.get());
+
+  // Arrange for the view to be synchronously destroyed inside
+  // SetVirtualKeyboardVisibilityIfEnabled.
+  input_method_->set_on_set_virtual_keyboard_visibility_if_enabled(
+      base::BindLambdaForTesting([&]() {
+        widget_host_ = nullptr;
+        view_.ExtractAsDangling()->Destroy();
+      }));
+
+  ui::mojom::TextInputState state;
+  state.type = ui::TEXT_INPUT_TYPE_TEXT;
+  state.mode = ui::TEXT_INPUT_MODE_TEXT;
+  state.show_ime_if_needed = true;
+  GetTextInputManager(view_.get())->UpdateTextInputState(view_.get(), state);
+}
+
 // RWHVA::OnWindowFocused()'s lost-focus branch calls
 // DetachFromInputMethod(false). On Windows this reaches
 // TSFBridge::RemoveFocusedClient() -> ITfThreadMgr::AssociateFocus(), a
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.