CVE-2026-11117
Overview
Files Changed
ui/views/win/hwnd_message_handler.ccui/views/win/hwnd_message_handler.h
Patch
From 71bdca9ded45c489bf14fbb687b0d74049be981b Mon Sep 17 00:00:00 2001 From: David Bienvenu <[email protected]> Date: Tue, 21 Apr 2026 08:29:09 -0700 Subject: [PATCH] Cleanup HWNDMessageHandler fullscreen monitor map on close Fix suggested by Gemini. Bug: 501403820 Change-Id: I5d76d7ba4e9b90502543261bd1a48f4187e951a0 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7779204 Commit-Queue: David Bienvenu <[email protected]> Reviewed-by: Allen Bauer <[email protected]> Cr-Commit-Position: refs/heads/main@{#1618211} --- diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc index d6546c2..3a665b97 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -442,6 +442,10 @@ // Clear pointer to this in `hwnd()`'s user data, to prevent installed hooks // from calling back into this after deletion. ClearUserData(); + + // If the window is a fullscreen window then remove its references from the + // full screen window map. + RemoveCurrentWindowFromFullscreenMonitorMap(); } void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) { @@ -1022,12 +1026,7 @@ void HWNDMessageHandler::SetFullscreen(bool fullscreen, int64_t target_display_id) { // Erase any prior reference to this window in the fullscreen window map. - HMONITOR monitor = ::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY); - FullscreenWindowMonitorMap::iterator iter = - fullscreen_monitor_map_.Get().find(monitor); - if (iter != fullscreen_monitor_map_.Get().end()) { - fullscreen_monitor_map_.Get().erase(iter); - } + RemoveCurrentWindowFromFullscreenMonitorMap(); background_fullscreen_hack_ = false; auto ref = msg_handler_weak_factory_.GetWeakPtr(); @@ -3909,12 +3908,8 @@ } void HWNDMessageHandler::RemoveCurrentWindowFromFullscreenMonitorMap() { - auto& map = fullscreen_monitor_map_.Get(); - const auto i = std::ranges::find( - map, this, &FullscreenWindowMonitorMap::value_type::second); - if (i != map.end()) { - map.erase(i); - } + std::erase_if(fullscreen_monitor_map_.Get(), + [this](const auto& kv) { return kv.second == this; }); } void HWNDMessageHandler::UpdateFullscreenMonitorMap() { diff --git a/ui/views/win/hwnd_message_handler.h b/ui/views/win/hwnd_message_handler.h index ec1cc32f..49f0740 100644 --- a/ui/views/win/hwnd_message_handler.h +++ b/ui/views/win/hwnd_message_handler.h @@ -897,7 +897,8 @@ // This is a map of the HMONITOR to full screeen window instance. It is safe // to keep a raw pointer to the HWNDMessageHandler instance as we track the // window destruction and ensure that the map is cleaned up. - using FullscreenWindowMonitorMap = std::map<HMONITOR, HWNDMessageHandler*>; + using FullscreenWindowMonitorMap = + std::map<HMONITOR, raw_ptr<HWNDMessageHandler>>; static base::LazyInstance<FullscreenWindowMonitorMap>::DestructorAtExit fullscreen_monitor_map_;
Original Bug Report
Potential Browser-process UAF in HWNDMessageHandler fullscreen monitor map
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.
Overview: A potential Use-After-Free (UAF) exists in the Windows browser process due to improper management of the fullscreen_monitor_map_. External window movements combined with multi-monitor fullscreen requests can create duplicate map entries, only one of which is cleaned up on destruction. The remaining dangling raw pointer is dereferenced when another window on that monitor is activated.
Affected files:
ui/views/win/hwnd_message_handler.ccui/views/win/hwnd_message_handler.h
Estimated timestamp from git blame: 2026-02-28
Description
A potential Use-After-Free vulnerability exists in the Windows Browser process within ui/views/win/hwnd_message_handler.cc.
HWNDMessageHandler uses a static map (fullscreen_monitor_map_) to track fullscreen windows per monitor (std::map<HMONITOR, HWNDMessageHandler*>). This map explicitly uses bare pointers.
There are two logical flaws that combine to create the UAF:
- Incomplete Removal during
SetFullscreen: When transitioning to fullscreen,HWNDMessageHandler::SetFullscreenattempts to remove any prior entry for the window by looking up its current monitor handle (::MonitorFromWindow). If the window was previously made fullscreen on Monitor A, but then moved by the OS (e.g., via theWin+Shift+Arrowshortcut) to Monitor B, the window’s current monitor is B. A subsequent call toSetFullscreen(e.g., targeting Monitor C via the Window Management API) will fail to find and erase the old entry for Monitor A, and will insert a new entry for Monitor C. The map now contains duplicate entries for the same handler object. - Incomplete Cleanup during Destruction: When the window is destroyed,
HWNDMessageHandler::OnDestroycallsRemoveCurrentWindowFromFullscreenMonitorMap(). This function searches the map by value to remove the handler. However, it usesstd::ranges::find, which only returns the first matching iterator, and erases it without looping. If duplicate entries exist, only the first is removed, leaving a dangling pointer in the global map.
When another Chrome window on the affected monitor receives focus, CheckAndHandleBackgroundFullscreenOnMonitor looks up the dangling pointer by its monitor handle. It calls iter->second->hwnd() and the virtual method iter->second->OnBackgroundFullscreen(), leading to a UAF dereference and potential Remote Code Execution (RCE) in the browser process.
Note: HWNDMessageHandler utilizes ADVANCED_MEMORY_SAFETY_CHECKS, which provides a scheduler loop quarantine. However, an attacker can reliably outlive this quarantine before triggering the vulnerability. Furthermore, because the map uses raw pointers rather than raw_ptr, MiraclePtr (BRP) does not protect this specific reference.
Theoretical Reproduction Steps
Please note: These are suggested/potential steps based on code analysis, as our tooling agent cannot execute live code.
- Prerequisites: The user has a Windows setup with at least three monitors (Monitor A, Monitor B, Monitor C).
- A malicious web page opens a popup/window on Monitor A and calls
element.requestFullscreen(). - The user moves the fullscreen window to Monitor B using the OS shortcut (
Win+Shift+Right Arrow). Chrome’s map is not updated; it still tracks the window on Monitor A. - The malicious web page calls
element.requestFullscreen({ screen: monitor_c_details })to transition the window to Monitor C.- Chrome checks the map for Monitor B (the current monitor), finds nothing, and erases nothing.
- Chrome moves the window and adds a new entry for Monitor C. The map now contains both
{Monitor A -> Handler}and{Monitor C -> Handler}.
- The malicious web page calls
window.close(), destroying the window.RemoveCurrentWindowFromFullscreenMonitorMap()executes, finding and erasing only the first matching entry (e.g., Monitor A).- The entry for Monitor C remains in the map as a dangling pointer.
- The attacker waits for the scheduler loop quarantine to expire, performing heap grooming to replace the freed
HWNDMessageHandlerobject. - The user focuses or clicks any other Chrome window located on Monitor C.
- The
WM_ACTIVATEmessage fires, triggeringCheckAndHandleBackgroundFullscreenOnMonitor, which dereferences the dangling pointer and executes the attacker’s payload.
Suggested Fix
- Fix Cleanup Logic: Update
RemoveCurrentWindowFromFullscreenMonitorMap()inui/views/win/hwnd_message_handler.ccto usestd::erase_ifor a loop to ensure all map entries matchingthisare removed, rather than just the first one. - Use Smart Pointers: Refactor
FullscreenWindowMonitorMapto usebase::raw_ptr<HWNDMessageHandler>orbase::WeakPtr<HWNDMessageHandler>to benefit from MiraclePtr protections or automatic invalidation. - Fix Update Logic: In
SetFullscreen, instead of only erasing the entry for the current monitor, it may be safer to callRemoveCurrentWindowFromFullscreenMonitorMap()to ensure any stale entries for this instance are cleared before adding the new monitor mapping.
Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b
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.