CVE-2026-8542
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/browser/renderer_host/legacy_render_widget_host_win.cc |
modified |
Files Changed
content/browser/renderer_host/legacy_render_widget_host_win.cc
Patch
From 73cfb55d92a0eede38f859e26efecfc458253524 Mon Sep 17 00:00:00 2001
From: Andrew Paseltiner <apaseltiner@chromium.org>
Date: Wed, 06 May 2026 10:40:45 -0700
Subject: [PATCH] Prevent UAF in LegacyRenderWidgetHostHWND::UpdateParent
Synchronous Win32 and COM calls (::SetParent,
CreateDirectManipulationHelper) can pump the message loop, leading to
the destruction of the object. This patch adds base::WeakPtr guards to
detect when |this| has been freed mid-method.
Fixed: 497066659
Change-Id: Ieaaf17bb587912a5091d45b65d84463e0ddcbbcb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7822519
Commit-Queue: Andrew Paseltiner <apaseltiner@chromium.org>
Reviewed-by: Alex Moshchuk <alexmos@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1626308}
---
diff --git a/content/browser/renderer_host/legacy_render_widget_host_win.cc b/content/browser/renderer_host/legacy_render_widget_host_win.cc
index 1df9615..3b28ceeb 100644
--- a/content/browser/renderer_host/legacy_render_widget_host_win.cc
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc
@@ -114,10 +114,26 @@
// call altogether.
const HWND current_parent = GetParent();
if (current_parent != new_parent) {
+ // ::SetParent and CreateDirectManipulationHelper (CoCreateInstance /
+ // IDirectManipulationManager::Activate / Enable) can synchronously dispatch
+ // window messages. Re-entrant dispatch may reach
+ // RenderWidgetHostViewAura::OnWindowDestroying ->
+ // LegacyRenderWidgetHostHWND::Destroy() -> ::DestroyWindow() ->
+ // WM_NCDESTROY -> OnNCDestroy -> delete this. Guard against |this| being
+ // freed mid-method, matching the pattern used in OnKeyboardRange et al.
+ base::WeakPtr<LegacyRenderWidgetHostHWND> ref(
+ msg_handler_weak_factory_.GetWeakPtr());
+
::SetParent(hwnd(), new_parent);
+ if (!ref) {
+ return;
+ }
if (!only_update_direct_manipulation_helper) {
CreateDirectManipulationHelper();
+ if (!ref) {
+ return;
+ }
}
// Reset tooltips when parent changed; otherwise tooltips could stay open as
@@ -141,7 +157,12 @@
// subsequently changes.
if (!only_update_direct_manipulation_helper &&
!direct_manipulation_helper_) {
+ base::WeakPtr<LegacyRenderWidgetHostHWND> ref(
+ msg_handler_weak_factory_.GetWeakPtr());
CreateDirectManipulationHelper();
+ if (!ref) {
+ return;
+ }
}
}
Original Bug Report
Potential Use-After-Free in LegacyRenderWidgetHostHWND::UpdateParent via nested message loop
Project Fortify, an experimental security project, has identified the following potential security issue.
Overview: A Use-After-Free (UAF) vulnerability exists in LegacyRenderWidgetHostHWND::UpdateParent because it lacks re-entrancy guards during synchronous Win32 API calls. If the object is destroyed during a nested message loop triggered by these calls, subsequent member accesses lead to a UAF, potentially allowing for a browser-process sandbox escape.
Affected files:
content/browser/renderer_host/legacy_render_widget_host_win.cccontent/browser/renderer_host/render_widget_host_view_aura.cc
Estimated timestamp from git blame: 2025-07-09
Summary
A potential Use-After-Free (UAF) vulnerability has been identified in LegacyRenderWidgetHostHWND::UpdateParent (content/browser/renderer_host/legacy_render_widget_host_win.cc). The method invokes the synchronous Win32 API ::SetParent and potentially CreateDirectManipulationHelper(), both of which can broadcast events or make COM calls that pump the UI thread’s message queue. If a pending IPC task or event handler destroys the window during this nested message loop, the LegacyRenderWidgetHostHWND object is freed. When the synchronous call returns, the method proceeds to access members of the freed object, potentially leading to a sandbox escape via a vtable hijack in the highly privileged Browser Process.
Potential Exploit Steps
(Note: These are suggested steps; our tooling agent does not have the ability to run code or provide a working proof of concept.)
- Triggering the State Change: An attacker initiates an action in a compromised renderer (e.g., hiding a WebContents by switching tabs) that causes
RenderWidgetHostViewAura::HideImpl()to be called in the Browser Process. - Reparenting the Window:
HideImpl()attempts to reparent the legacy window to the global hidden window by callinglegacy_render_widget_host_HWND_->UpdateParent(ui::GetHiddenWindow()). - Synchronous OS Call: Inside
LegacyRenderWidgetHostHWND::UpdateParent(), the code executes the synchronous Win32 API::SetParent(hwnd(), new_parent). - Triggering the Event Hook:
::SetParentsynchronously broadcasts theEVENT_OBJECT_PARENTCHANGEevent to the OS. If an accessibility tool or UI Automation (UIA) client is running, the OS invokes its registeredSetWinEventHookcallbacks. - The Nested Message Loop: UIA hooks frequently make cross-apartment COM calls. Because the UI thread is a COM Single-Threaded Apartment (STA), COM blocks waiting for the RPC reply but uses
MsgWaitForMultipleObjectsto pump the UI thread’s message queue to prevent deadlocks. This creates a nested message loop that allows pending IPC tasks to execute mid-function. - Window Destruction: A pre-queued attacker IPC (e.g.,
WidgetHostMsg_Close) is dispatched, tearing down theRenderWidgetHostViewAura’s window. This invokesRenderWidgetHostViewAura::OnWindowDestroying(content/browser/renderer_host/render_widget_host_view_aura.cc:2309). - Extracting the Pointer: At line 2331, the code executes
legacy_render_widget_host_HWND_.ExtractAsDangling()->Destroy();. - Bypassing MiraclePtr (BRP):
ExtractAsDangling()creates a temporaryDanglingTypeobject holding the MiraclePtr reference count and clears thelegacy_render_widget_host_HWND_member. The temporaryDanglingTypeis immediately dereferenced to callLegacyRenderWidgetHostHWND::Destroy(), which sends aWM_NCDESTROYmessage and callsdelete thisinOnNCDestroy. Crucially, whenDestroy()returns, the statement ends, and the temporaryDanglingTypedrops its BRP refcount to 0. PartitionAlloc immediately unquarantines the freedLegacyRenderWidgetHostHWNDchunk and moves it to the freelist. - Reclaiming the Memory: The nested message loop continues pumping. Subsequent pre-queued attacker IPCs (e.g., Blob allocations) allocate memory matching
sizeof(LegacyRenderWidgetHostHWND). PartitionAlloc assigns the newly freed chunk to the attacker’s object, allowing them to overwrite the memory backing theraw_ptr<RenderWidgetHostViewAura> host_field with a forged pointer. - The UAF and Hijack: The nested loop completes, and
::SetParentreturns. Execution resumes inUpdateParentusing the now-danglingthispointer. At line 122,UpdateParentexecuteshost_->UpdateTooltip(std::u16string());. It reads the attacker’s forgedhost_pointer and makes a virtual function call (UpdateTooltip), granting the attacker arbitrary Remote Code Execution (RCE).
Recommended Fix
Other methods in LegacyRenderWidgetHostHWND (such as OnKeyboardRange, OnMouseMessage, and OnTouchMessage) correctly use a base::WeakPtr guard to detect if the object was destroyed during synchronous processing. The same pattern should be applied to UpdateParent.
base::WeakPtr<LegacyRenderWidgetHostHWND> ref(msg_handler_weak_factory_.GetWeakPtr());
::SetParent(hwnd(), new_parent);
if (!ref) {
return;
}
Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0
Results from so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.