Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Core
DescriptionUse after free in Core
ComponentCore
Bug ClassUAF
Tracker497066659
Fix commit73cfb55d92a0 (chromium/src) +21/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-12

Changed Functions

FunctionChangeNotes
if
content/browser/renderer_host/legacy_render_widget_host_win.cc
modified

Files Changed

  • content/browser/renderer_host/legacy_render_widget_host_win.cc
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;
+      }
     }
   }
 
Loading diff…

Original Bug Report

reported by vm...@google.com

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.cc
  • content/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.)

  1. 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.
  2. Reparenting the Window: HideImpl() attempts to reparent the legacy window to the global hidden window by calling legacy_render_widget_host_HWND_->UpdateParent(ui::GetHiddenWindow()).
  3. Synchronous OS Call: Inside LegacyRenderWidgetHostHWND::UpdateParent(), the code executes the synchronous Win32 API ::SetParent(hwnd(), new_parent).
  4. Triggering the Event Hook: ::SetParent synchronously broadcasts the EVENT_OBJECT_PARENTCHANGE event to the OS. If an accessibility tool or UI Automation (UIA) client is running, the OS invokes its registered SetWinEventHook callbacks.
  5. 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 MsgWaitForMultipleObjects to 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.
  6. Window Destruction: A pre-queued attacker IPC (e.g., WidgetHostMsg_Close) is dispatched, tearing down the RenderWidgetHostViewAura’s window. This invokes RenderWidgetHostViewAura::OnWindowDestroying (content/browser/renderer_host/render_widget_host_view_aura.cc:2309).
  7. Extracting the Pointer: At line 2331, the code executes legacy_render_widget_host_HWND_.ExtractAsDangling()->Destroy();.
  8. Bypassing MiraclePtr (BRP): ExtractAsDangling() creates a temporary DanglingType object holding the MiraclePtr reference count and clears the legacy_render_widget_host_HWND_ member. The temporary DanglingType is immediately dereferenced to call LegacyRenderWidgetHostHWND::Destroy(), which sends a WM_NCDESTROY message and calls delete this in OnNCDestroy. Crucially, when Destroy() returns, the statement ends, and the temporary DanglingType drops its BRP refcount to 0. PartitionAlloc immediately unquarantines the freed LegacyRenderWidgetHostHWND chunk and moves it to the freelist.
  9. 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 the raw_ptr<RenderWidgetHostViewAura> host_ field with a forged pointer.
  10. The UAF and Hijack: The nested loop completes, and ::SetParent returns. Execution resumes in UpdateParent using the now-dangling this pointer. At line 122, UpdateParent executes host_->UpdateTooltip(std::u16string());. It reads the attacker’s forged host_ pointer and makes a virtual function call (UpdateTooltip), granting the attacker arbitrary Remote Code Execution (RCE).

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.

View on issue tracker