Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Aura
DescriptionUse after free in Aura
ComponentAura
Bug ClassUAF
Tracker542565481
Fix commit00f687785bd7 (chromium/src) +56/-0
CISA KEVNot listed
CreditedMicrosoft
Disclosed2026-09-08

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
RenderWidgetHostViewAuraReentrantDestructionIME
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
MockVirtualKeyboardController
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
From 00f687785bd706d9d19a238045059445d811bbff Mon Sep 17 00:00:00 2001
From: Gaston Rodriguez <[email protected]>
Date: Fri, 04 Sep 2026 09:52:04 -0700
Subject: [PATCH] Prevent UAF if DetachFromInputMethod frees RWHVA

RenderWidgetHostViewAura::DetachFromInputMethod() calls
input_method->DetachTextInputClient(), which can result in RWHVA being
freed. DetachFromInputMethod() guards against this by getting a weak
pointer to `this` and checking it after the call.

RWHVA::OnWindowFocused() calls DetachFromInputMethod(), but doesn't
account for the possibility that RWHVA will be freed, and
unconditionally continues execution. If DetachFromInputMethod() has
freed RWHVA, this results in a UAF.

This CL addresses this issue by adding the same weak pointer mechanism
to the focus change function, and also adds a unit test that would crash
if this check weren't introduced.

Bug: 542565481
Change-Id: If73645258ebf19765f2eb625928e56f821229561
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8306510
Reviewed-by: Olga Gerchikov <[email protected]>
Commit-Queue: Gaston Rodriguez <[email protected]>
Reviewed-by: Dave Tapuska <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1692536}
---

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 77c4593..277febb 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -2850,7 +2850,11 @@
   UpdateActiveState(false);
   host()->LostFocus();
 
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
   DetachFromInputMethod(false);
+  if (!weak_this) {
+    return;
+  }
 
   // TODO(wjmaclean): Do we need to let TouchSelectionControllerClientAura
   // handle this, just in case it stomps on a new highlight in another view
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 10341a0..33ed433 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 DetachTextInputClient(ui::TextInputClient* client) override {
+    ui::MockInputMethod::DetachTextInputClient(client);
+    if (on_detach_text_input_client_) {
+      std::move(on_detach_text_input_client_).Run();
+    }
+  }
+
   void set_on_caret_bounds_changed(base::OnceClosure closure) {
     on_caret_bounds_changed_ = std::move(closure);
   }
@@ -7053,9 +7060,14 @@
     on_text_input_type_changed_ = std::move(closure);
   }
 
+  void set_on_detach_text_input_client(base::OnceClosure closure) {
+    on_detach_text_input_client_ = std::move(closure);
+  }
+
  private:
   base::OnceClosure on_caret_bounds_changed_;
   base::OnceClosure on_text_input_type_changed_;
+  base::OnceClosure on_detach_text_input_client_;
 };
 
 class RenderWidgetHostViewAuraReentrantDestructionIME
@@ -7143,6 +7155,46 @@
   GetTextInputManager(view_)->UpdateTextInputState(view_, state);
 }
 
+// RWHVA::OnWindowFocused()'s lost-focus branch calls
+// DetachFromInputMethod(false). On Windows this reaches
+// TSFBridge::RemoveFocusedClient() -> ITfThreadMgr::AssociateFocus(), a
+// synchronous COM call into the platform text-services framework that can run
+// arbitrary code registered by a third-party Text Input Processor (e.g. an
+// IME). DetachFromInputMethod() already guards its own remaining statements
+// with a weak_ptr check in case that callout destroys the view re-entrantly,
+// but OnWindowFocused()'s lost-focus branch does not: it unconditionally
+// continues on to
+// `selection_controller_->HideAndDisallowShowingAutomatically()` afterward,
+// touching the now-freed `this`.
+TEST_F(RenderWidgetHostViewAuraReentrantDestructionIME,
+       DestroyDuringDetachTextInputClientOnLostFocus) {
+  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());
+
+  // Focus `view_` so that DetachTextInputClient(view_) below is not a no-op.
+  input_method_->SetFocusedTextInputClient(view_.get());
+
+  // Arrange for the view to be synchronously destroyed inside
+  // DetachTextInputClient, simulating re-entrant destruction triggered by a
+  // TSF/IME callout that pumps a queued window.close() task
+  // while the view is losing focus.
+  FakeRenderWidgetHostViewAura* raw_view = view_.get();
+  input_method_->set_on_detach_text_input_client(
+      base::BindLambdaForTesting([&]() {
+        widget_host_ = nullptr;
+        view_.ExtractAsDangling()->Destroy();
+      }));
+
+  // If the weak_ptr check wasn't in place, OnWindowFocused()'s lost-focus
+  // branch resumes after DetachFromInputMethod() returns and dereferences
+  // `this->selection_controller_` on the freed view.
+  raw_view->OnWindowFocused(nullptr, raw_view->GetNativeView());
+}
+
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
 class MockVirtualKeyboardController final
     : public ui::VirtualKeyboardController {
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 10341a0..33ed433 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 DetachTextInputClient(ui::TextInputClient* client) override {
+    ui::MockInputMethod::DetachTextInputClient(client);
+    if (on_detach_text_input_client_) {
+      std::move(on_detach_text_input_client_).Run();
+    }
+  }
+
   void set_on_caret_bounds_changed(base::OnceClosure closure) {
     on_caret_bounds_changed_ = std::move(closure);
   }
@@ -7053,9 +7060,14 @@
     on_text_input_type_changed_ = std::move(closure);
   }
 
+  void set_on_detach_text_input_client(base::OnceClosure closure) {
+    on_detach_text_input_client_ = std::move(closure);
+  }
+
  private:
   base::OnceClosure on_caret_bounds_changed_;
   base::OnceClosure on_text_input_type_changed_;
+  base::OnceClosure on_detach_text_input_client_;
 };
 
 class RenderWidgetHostViewAuraReentrantDestructionIME
@@ -7143,6 +7155,46 @@
   GetTextInputManager(view_)->UpdateTextInputState(view_, state);
 }
 
+// RWHVA::OnWindowFocused()'s lost-focus branch calls
+// DetachFromInputMethod(false). On Windows this reaches
+// TSFBridge::RemoveFocusedClient() -> ITfThreadMgr::AssociateFocus(), a
+// synchronous COM call into the platform text-services framework that can run
+// arbitrary code registered by a third-party Text Input Processor (e.g. an
+// IME). DetachFromInputMethod() already guards its own remaining statements
+// with a weak_ptr check in case that callout destroys the view re-entrantly,
+// but OnWindowFocused()'s lost-focus branch does not: it unconditionally
+// continues on to
+// `selection_controller_->HideAndDisallowShowingAutomatically()` afterward,
+// touching the now-freed `this`.
+TEST_F(RenderWidgetHostViewAuraReentrantDestructionIME,
+       DestroyDuringDetachTextInputClientOnLostFocus) {
+  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());
+
+  // Focus `view_` so that DetachTextInputClient(view_) below is not a no-op.
+  input_method_->SetFocusedTextInputClient(view_.get());
+
+  // Arrange for the view to be synchronously destroyed inside
+  // DetachTextInputClient, simulating re-entrant destruction triggered by a
+  // TSF/IME callout that pumps a queued window.close() task
+  // while the view is losing focus.
+  FakeRenderWidgetHostViewAura* raw_view = view_.get();
+  input_method_->set_on_detach_text_input_client(
+      base::BindLambdaForTesting([&]() {
+        widget_host_ = nullptr;
+        view_.ExtractAsDangling()->Destroy();
+      }));
+
+  // If the weak_ptr check wasn't in place, OnWindowFocused()'s lost-focus
+  // branch resumes after DetachFromInputMethod() returns and dereferences
+  // `this->selection_controller_` on the freed view.
+  raw_view->OnWindowFocused(nullptr, raw_view->GetNativeView());
+}
+
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
 class MockVirtualKeyboardController final
     : public ui::VirtualKeyboardController {
Loading diff…

Original Bug Report

reported by [email protected]

RenderWidgetHostViewAura UAF during shutdown with focus change

VULNERABILITY DETAILS

Please provide a brief explanation of the security issue.

VERSION

Chrome Version: [150.0.0.0] + [stable] Operating System: [Windows11, Platforms that use aura]

REPRODUCTION CASE

I don’t have a repro scenario on the browser but can reliably repro with a unit test. I’ve put it on CL:8156380.

ROOT CAUSE

RenderWidgetHostViewAura registers itself as the FocusChangeObserver for its window_ via a raw pointer stored in window_’s property store (aura::client::SetFocusChangeObserver). This property is never explicitly cleared in RenderWidgetHostViewAura’s destructor. [1]

In the normal teardown path (Destroy() -> delete window_), this is safe. The window is destroyed first and the raw pointer never outlives the view. [2]

However, when a renderer process exits unexpectedly (RendererExited -> RenderWidgetHostImpl::RendererExited -> aura::Window::~Window -> OnWindowDestroyed -> delete this), ~RenderWidgetHostViewAura() runs while window_ may still be alive (e.g. owned by its parent). The destructor does not clear the FocusChangeObserver property. A subsequent, unrelated event that triggers a focus change during browser window close eventually reaches the FocusController. The FocusController checks for observers, and finds the dangling pointer to the already deleted RenderWidgetHostViewAura. [3]

[1] https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_widget_host_view_aura.cc;l=2947

[2] https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_widget_host_view_aura.cc;l=1104

[3] https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_widget_host_view_aura.cc;l=2889

FOR CRASHES, PLEASE INCLUDE THE FOLLOWING ADDITIONAL INFORMATION

Type of crash: Browser process UAF

Crash State: Crash stack:

ui::TouchSelectionController::HideAndDisallowShowingAutomatically+0xd 
content::RenderWidgetHostViewAura::OnWindowFocused+0x69 
wm::FocusController::SetFocusedWindow+0x267 
aura::Window::NotifyWindowVisibilityChangedAtReceiver+0xcc 
aura::Window::NotifyWindowVisibilityChangedDown+0x2e 
aura::Window::NotifyWindowVisibilityChangedDown+0x12a 
aura::Window::NotifyWindowVisibilityChanged+0x15 
aura::Window::SetVisibleInternal+0x260 
views::NativeViewHostAuraWithClipWindow::HideWidget+0x18 
views::View::VisibilityChangedImpl+0x29 
views::View::PropagateVisibilityNotifications+0xb4 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::View::PropagateVisibilityNotifications+0x105 
views::Widget::OnNativeWidgetVisibilityChanged+0x38 
views::DesktopWindowTreeHostWin::HandleVisibilityChanged+0x4f 
views::HWNDMessageHandler::OnWindowPosChanged+0x11d 
views::HWNDMessageHandler::_ProcessWindowMessage+0x3e1 
views::HWNDMessageHandler::OnWndProc+0x188 
gfx::WindowImpl::WndProc+0x7e 
base::win::WrappedWindowProc<&gfx::WindowImpl::WndProc>+0xf 
user32!UserCallWinProcCheckWow+0x356 
user32!DispatchClientMessage+0x9c 
user32!__fnINLPWINDOWPOS+0x33 
ntdll!KiUserCallbackDispatcherContinue 
win32u!ZwUserSetWindowPos+0x14 
views::HWNDMessageHandler::Hide+0x3a 
aura::WindowTreeHost::Hide+0x18 
views::DesktopNativeWidgetAura::Hide+0x29 
views::Widget::Hide+0x63 
BrowserView::OnWindowCloseRequested+0x13c 
views::Widget::CloseWithReason+0x4f 
// ...

We also have an ASAN GWP repro, this is the deallocation stack:

Deallocation stack trace size:                    25
Allocation address:                               <>
Allocation size:                                  57c
Error type:                                       Use After Free
Allocator Type:                                   PartitionAlloc
GWP-ASan Mode:                                    Lightweight UAF Detector (BRP sampling)
Deallocation Thread ID:                           13152

Deallocation Stack Trace:

[0-4] ...
4	content::RenderWidgetHostViewAura::~RenderWidgetHostViewAura
5	aura::Window::~Window
6	aura::Window::~Window
7	content::RenderWidgetHostImpl::RendererExited
8	content::RenderViewHostImpl::RenderProcessExited
9	content::RenderProcessHostImpl::ProcessDied
10	content::RenderProcessHostImpl::FastShutdown
11	content::RenderProcessHostImpl::FastShutdownIfPossible
12	TabStripModel::CloseWebContentses
13	TabStripModel::InternalCloseTabsImpl
14	TabStripModel::CloseTabs
15	TabStripModel::CloseAllTabs
16	Browser::OnWindowClosing
17	BrowserView::OnWindowCloseRequested
18	views::Widget::CloseWithReason
19	views::HWNDMessageHandler::_ProcessWindowMessage
20	views::HWNDMessageHandler::OnWndProc
21	gfx::WindowImpl::WndProc
22	base::win::WrappedWindowProc<&gfx::WindowImpl::WndProc>

Client ID (if relevant): N/A

CREDIT INFORMATION

Externally reported security bugs may appear in Chrome release notes. If this bug is included, how would you like to be credited?

Reporter credit: Microsoft

View on issue tracker