Overview

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

Changed Functions

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

Files Changed

  • content/browser/renderer_host/legacy_render_widget_host_win.cc
  • content/browser/renderer_host/render_widget_host_view_aura.cc
From d4a5fe4028c21e514138d79b7f0b07beb5abc6fa Mon Sep 17 00:00:00 2001
From: Alex Moshchuk <[email protected]>
Date: Fri, 15 May 2026 10:38:34 -0700
Subject: [PATCH] Guard against UAF in LegacyRenderWidgetHostHWND::SetBounds()

LegacyRenderWidgetHostHWND::SetBounds() calls the ::SetWindowPos() Windows
API which can trigger a nested message loop and theoretically lead to
the destruction of RenderWidgetHostViewAura/LegacyRenderWidgetHostHWND.
Add some bandaid WeakPtr checks to guard against this.

Fixed: 513235131
Change-Id: Ia95e4bdbe1faf8ffe5e2a0956670005dff1d4841
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7848426
Reviewed-by: Andrew Paseltiner <[email protected]>
Commit-Queue: Alex Moshchuk <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1631398}
---

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 27667c8e..5128325 100644
--- a/content/browser/renderer_host/legacy_render_widget_host_win.cc
+++ b/content/browser/renderer_host/legacy_render_widget_host_win.cc
@@ -196,9 +196,16 @@
 void LegacyRenderWidgetHostHWND::SetBounds(const gfx::Rect& bounds) {
   gfx::Rect bounds_in_pixel =
       display::win::GetScreenWin()->DIPToClientRect(hwnd(), bounds);
+  // ::SetWindowPos can spin a nested message loop, which can potentially
+  // destroy `this`.
+  base::WeakPtr<LegacyRenderWidgetHostHWND> ref(
+      msg_handler_weak_factory_.GetWeakPtr());
   ::SetWindowPos(hwnd(), nullptr, bounds_in_pixel.x(), bounds_in_pixel.y(),
                  bounds_in_pixel.width(), bounds_in_pixel.height(),
                  SWP_NOREDRAW);
+  if (!ref) {
+    return;
+  }
   if (direct_manipulation_helper_) {
     direct_manipulation_helper_->SetSizeInPixels(bounds_in_pixel.size());
   }
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 3f497e5..6abce33 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -580,8 +580,15 @@
 void RenderWidgetHostViewAura::HandleBoundsInRootChanged() {
 #if BUILDFLAG(IS_WIN)
   if (legacy_render_widget_host_HWND_) {
+    // `SetBounds()` calls ::SetWindowPos which can spin a nested message loop
+    // on Windows, potentially destroying `this`.
+    base::WeakPtr<RenderWidgetHostViewAura> weak_this(
+        weak_ptr_factory_.GetWeakPtr());
     legacy_render_widget_host_HWND_->SetBounds(
         window_->GetBoundsInRootWindow());
+    if (!weak_this) {
+      return;
+    }
   }
 #endif
   if (!in_shutdown_) {
@@ -705,6 +712,8 @@
 
 #if BUILDFLAG(IS_WIN)
   UpdateLegacyWin();
+  // WARNING: Do not add any code after this line, since the last call can
+  // potentially destroy `this`.
 #endif
 }
 
@@ -3124,7 +3133,14 @@
                               window_->GetLocalSurfaceId());
 
 #if BUILDFLAG(IS_WIN)
+  // `UpdateLegacyWin()` can spin a nested message loop on Windows, potentially
+  // destroying `this`.
+  base::WeakPtr<RenderWidgetHostViewAura> weak_this(
+      weak_ptr_factory_.GetWeakPtr());
   UpdateLegacyWin();
+  if (!weak_this) {
+    return;
+  }
 
   if (IsPointerLocked()) {
     UpdateMouseLockRegion();
@@ -3154,9 +3170,19 @@
   }
 
   if (legacy_render_widget_host_HWND_) {
+    // Both UpdateParent and SetBounds can spin a nested message loop on
+    // Windows, potentially destroying `this`.
+    base::WeakPtr<RenderWidgetHostViewAura> weak_this(
+        weak_ptr_factory_.GetWeakPtr());
     legacy_render_widget_host_HWND_->UpdateParent(GetHostWindowHWND());
+    if (!weak_this) {
+      return;
+    }
     legacy_render_widget_host_HWND_->SetBounds(
         window_->GetBoundsInRootWindow());
+    if (!weak_this) {
+      return;
+    }
     // There are cases where the parent window is created, made visible and
     // the associated RenderWidget is also visible before the
     // LegacyRenderWidgetHostHWND instace is created. Ensure that it is shown
@@ -3187,7 +3213,14 @@
   }
 
 #if BUILDFLAG(IS_WIN)
+  // `UpdateLegacyWin()` can spin a nested message loop on Windows, potentially
+  // destroying `this`.
+  base::WeakPtr<RenderWidgetHostViewAura> weak_this(
+      weak_ptr_factory_.GetWeakPtr());
   UpdateLegacyWin();
+  if (!weak_this) {
+    return;
+  }
 #endif
 
   delegated_frame_host_->AttachToCompositor(GetCompositor());
Loading diff…

Original Bug Report

reported by [email protected]

Potential Browser Use-After-Free in RenderWidgetHostViewAura and LegacyRenderWidgetHostHWND

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential Use-After-Free (UAF) exists in the Windows-specific renderer host code due to re-entrancy in synchronous Win32 calls. Synchronous APIs like ::SetWindowPos can trigger nested message loops that process pending window-destruction tasks while the caller is still on the stack. This can lead to the destruction of RenderWidgetHostViewAura and LegacyRenderWidgetHostHWND before their methods finish executing.

Affected files:

  • content/browser/renderer_host/legacy_render_widget_host_win.cc
  • content/browser/renderer_host/render_widget_host_view_aura.cc
  • content/browser/renderer_host/direct_manipulation_helper_win.cc

Estimated timestamp from git blame: 2015-08-11

Description

A potential Use-After-Free (UAF) vulnerability has been identified in content/browser/renderer_host/render_widget_host_view_aura.cc and content/browser/renderer_host/legacy_render_widget_host_win.cc. The issue stems from the use of synchronous Win32 calls, such as ::SetWindowPos, within the resize and bounds-synchronization logic on Windows.

On Windows, synchronous calls to ::SetWindowPos can dispatch window messages and, under certain conditions (such as when UI Automation or system hooks are active), enter a nested message loop. If a renderer-initiated window closure (e.g., via window.close()) is pending, the nested loop may process the destruction task while the bounds-synchronization method is still executing.

In LegacyRenderWidgetHostHWND::SetBounds, the code calls ::SetWindowPos at line 199. If a nested loop processes the window destruction, both the LegacyRenderWidgetHostHWND and its parent RenderWidgetHostViewAura (RWHVA) are deleted. Upon return from ::SetWindowPos, the method continues to access members of the now-deleted object. Furthermore, the caller RenderWidgetHostViewAura::HandleBoundsInRootChanged (or UpdateLegacyWin) resumes execution and accesses its own members (e.g., in_shutdown_, host_) on a potentially freed RWHVA object.

Potential Impact

This is a potential Use-After-Free in the unsandboxed browser process. While LegacyRenderWidgetHostHWND is protected by the ADVANCED_MEMORY_SAFETY_CHECKS quarantine, RenderWidgetHostViewAura is not. An attacker could potentially reclaim the memory of the RenderWidgetHostViewAura object with controlled data before the execution resumes, leading to a control-flow hijack via virtual function calls on members like host_.

Suggested Reproductions Steps (Potential)

  1. From a compromised renderer, open a new popup window: const p = window.open('about:blank', ...).
  2. Request a resize of the popup: p.resizeTo(200, 200).
  3. Immediately request the popup to close: p.close().
  4. If the browser processes the resize first, it enters SetBounds. If ::SetWindowPos triggers a nested loop, it may execute the queued destruction task, leading to the UAF when execution resumes.

Note: These steps are based on static analysis and have not been verified with a functional Proof of Concept.

Methods that perform synchronous Win32 calls or parent updates should use a base::WeakPtr guard to check for object liveness after the call. Similar to the pattern already used in LegacyRenderWidgetHostHWND::UpdateParent, a guard should be added in SetBounds and its callers in RenderWidgetHostViewAura to return early if the object has been destroyed.

// Suggested fix in LegacyRenderWidgetHostHWND::SetBounds
base::WeakPtr<LegacyRenderWidgetHostHWND> ref(msg_handler_weak_factory_.GetWeakPtr());
::SetWindowPos(hwnd(), nullptr, ...);
if (!ref) return;

Evaluated with Chrome root at commit: b3153093eb3c78c3e88ccf562bcbc20437a04b0e


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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