CVE-2026-7343
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/views/win/hwnd_message_handler.cc |
modified |
Files Changed
ui/views/win/hwnd_message_handler.cc
Patch
From 1063c5579ec89566c997e9cba01c32f5d0a4bd38 Mon Sep 17 00:00:00 2001 From: David Bienvenu <[email protected]> Date: Mon, 20 Apr 2026 09:19:56 -0700 Subject: [PATCH] win: Add WeakPtr checks in HWNDMessageHandler. Bug: 503645680 Change-Id: I65089bc28d23a89f75d6982d1055bbba1aa10c1a Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7774439 Reviewed-by: Elly <[email protected]> Commit-Queue: David Bienvenu <[email protected]> Cr-Commit-Position: refs/heads/main@{#1617544} --- diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc index e00c2c13..d6546c2 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -1570,7 +1570,11 @@ MONITORINFO monitor_info = {sizeof(monitor_info)}; ::GetMonitorInfo(::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY), &monitor_info); + auto ref = msg_handler_weak_factory_.GetWeakPtr(); SetBoundsInternal(gfx::Rect(monitor_info.rcMonitor), false); + if (!ref) { + return; + } // Inform the taskbar that this window is now a fullscreen window so it go // behind the window in the Z-Order. The taskbar heuristics to detect // fullscreen windows are not reliable. Marking it explicitly seems to work @@ -2012,8 +2016,15 @@ // in which the display a window is on has a different scale factor than the // window, when the window handles the scale factor change. // See https://crbug.com/1368455 for more info. + auto ref = msg_handler_weak_factory_.GetWeakPtr(); display::win::GetScreenWin()->UpdateDisplayInfos(); + if (!ref) { + return 0; + } SetBoundsInternal(gfx::Rect(*reinterpret_cast<RECT*>(l_param)), false); + if (!ref) { + return 0; + } delegate_->HandleWindowScaleFactorChanged(scaling_factor); return 0; } @@ -3805,16 +3816,19 @@ bool force_size_changed) { gfx::Size old_size = GetClientAreaBounds().size(); + auto ref = msg_handler_weak_factory_.GetWeakPtr(); ::SetWindowPos(hwnd(), nullptr, bounds_in_pixels.x(), bounds_in_pixels.y(), bounds_in_pixels.width(), bounds_in_pixels.height(), SWP_NOACTIVATE | SWP_NOZORDER); + if (!ref) { + return; + } // If HWND size is not changed, we will not receive standard size change // notifications. If |force_size_changed| is |true|, we should pretend size is // changed. if (old_size == bounds_in_pixels.size() && force_size_changed && !background_fullscreen_hack_) { - auto ref = msg_handler_weak_factory_.GetWeakPtr(); delegate_->HandleClientSizeChanged(GetClientAreaBounds().size()); if (!ref) { return; @@ -3847,7 +3861,11 @@ gfx::Rect shrunk_rect(monitor_info.rcMonitor); shrunk_rect.set_height(shrunk_rect.height() - 1); background_fullscreen_hack_ = true; + auto ref = msg_handler_weak_factory_.GetWeakPtr(); SetBoundsInternal(shrunk_rect, false); + if (!ref) { + return; + } // Inform the taskbar that this window is no longer a fullscreen window so it // can bring itself to the top of the Z-Order. The taskbar heuristics to // detect fullscreen windows are not reliable. Marking it explicitly seems to
Original Bug Report
Potential Use-after-free in HWNDMessageHandler::OnDpiChanged via nested message loops
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 go/chrome-ai-generated-security-bugs-faq for more information.
Overview: A potential use-after-free vulnerability exists in HWNDMessageHandler::OnDpiChanged on Windows. Synchronous window operations (like SetWindowPos) can enter a nested message loop, allowing an attacker to queue and execute a window close task. This destroys the HWNDMessageHandler while it is still on the call stack, leading to a UAF upon return.
Affected files:
ui/views/win/hwnd_message_handler.ccui/views/widget/desktop_aura/desktop_native_widget_aura.ccui/views/widget/desktop_aura/desktop_window_tree_host_win.ccui/views/win/hwnd_message_handler.h
Estimated timestamp from git blame: 2026-02-28
Summary
A potential Use-After-Free (UAF) vulnerability has been identified in HWNDMessageHandler::OnDpiChanged in the Chrome browser process on Windows. The issue occurs when the handler performs synchronous operations that trigger a native nested message loop. If application tasks are permitted in this loop, an attacker can destroy the handler while OnDpiChanged is still executing, leading to memory corruption.
Vulnerability Details
In ui/views/win/hwnd_message_handler.cc, the HWNDMessageHandler::OnDpiChanged function handles WM_DPICHANGED messages. During its execution, it calls SetBoundsInternal, which calls the Windows API ::SetWindowPos:
2015: display::win::GetScreenWin()->UpdateDisplayInfos();
2016: SetBoundsInternal(gfx::Rect(*reinterpret_cast<RECT*>(l_param)), false);
2017: delegate_->HandleWindowScaleFactorChanged(scaling_factor);
As noted in Chromium codebase comments (ui/views/win/hwnd_message_handler.cc:691), ::SetWindowPos can trigger a native nested message loop, especially during complex DPI transitions.
If the browser is already in a state that allows application tasks to run during native nested loops (e.g., during a drag-and-drop operation where base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop is active), the message pump will process incoming tasks while blocked inside ::SetWindowPos.
If an IPC task to close the window is processed during this nested loop, it posts a task to HWNDMessageHandler::CloseNow. This task is immediately picked up by the message pump and executed. CloseNow calls ::DestroyWindow, which synchronously destroys the HWNDMessageHandler instance (via WM_NCDESTROY and DesktopNativeWidgetAura::OnHostClosed).
When the nested loop finishes and ::SetWindowPos returns, execution continues in SetBoundsInternal (accessing background_fullscreen_hack_ at line 3816) and subsequently OnDpiChanged (accessing delegate_ at line 2017). Both accesses dereference the freed this pointer, resulting in a use-after-free. Because HandleWindowScaleFactorChanged is a virtual method, this can potentially lead to a controlled vtable indirect call and arbitrary code execution in the browser process.
Potential Attack Scenario
While we have not verified this with a working Proof of Concept, an attacker might trigger this path via the following steps:
- A compromised renderer opens a popup window.
- The attacker’s script initiates an operation that enters a native nested loop with application tasks allowed (e.g., initiating a drag-and-drop).
- While the drag-and-drop loop is active, the attacker sends a
window.moveTo()IPC to move the popup across a DPI boundary. - This synchronously triggers
OnDpiChangedand the::SetWindowPoscall, which enters a second nested loop. - The attacker sends a
window.close()IPC targeting the popup. - The close task is processed by the second nested loop, destroying the window and the
HWNDMessageHandler. ::SetWindowPosreturns, and the freed handler is dereferenced.
Lack of Mitigations
- Missing WeakPtr Guards: While
SetBoundsInternaluses aWeakPtrafter notifying its delegate (line 3817), it does not protect the initial accesses immediately afterSetWindowPos.OnDpiChangedentirely lacks aWeakPtrguard. - MiraclePtr (BRP): The
raw_ptrprotection does not prevent this UAF because the implicitly capturedthispointer is a native C++ pointer and noraw_ptron the stack holds the object alive during this specific sequence to trigger quarantine.
Suggested Fix
Add base::WeakPtr guards in HWNDMessageHandler::OnDpiChanged and SetBoundsInternal to detect if the object has been destroyed during the SetWindowPos or UpdateDisplayInfos calls. For example, in OnDpiChanged:
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
display::win::GetScreenWin()->UpdateDisplayInfos();
SetBoundsInternal(gfx::Rect(*reinterpret_cast<RECT*>(l_param)), false);
if (!ref) return 0;
delegate_->HandleWindowScaleFactorChanged(scaling_factor);
Similar checks should be placed immediately after ::SetWindowPos in SetBoundsInternal.
Evaluated with Chrome root at commit: 661452647ddb2827305122ff3273bd5dea403f09
Results 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.