Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Views
DescriptionUse after free in Views
ComponentViews
Bug ClassUAF
Tracker501403820
Fix commit71bdca9ded45 (chromium/src) +9/-13
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • ui/views/win/hwnd_message_handler.cc
  • ui/views/win/hwnd_message_handler.h
From 71bdca9ded45c489bf14fbb687b0d74049be981b Mon Sep 17 00:00:00 2001
From: David Bienvenu <davidbienvenu@chromium.org>
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 <davidbienvenu@chromium.org>
Reviewed-by: Allen Bauer <kylixrd@chromium.org>
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_;
 
Loading diff…

Original Bug Report

reported by vm...@google.com

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.cc
  • ui/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:

  1. Incomplete Removal during SetFullscreen: When transitioning to fullscreen, HWNDMessageHandler::SetFullscreen attempts 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 the Win+Shift+Arrow shortcut) to Monitor B, the window’s current monitor is B. A subsequent call to SetFullscreen (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.
  2. Incomplete Cleanup during Destruction: When the window is destroyed, HWNDMessageHandler::OnDestroy calls RemoveCurrentWindowFromFullscreenMonitorMap(). This function searches the map by value to remove the handler. However, it uses std::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.

  1. Prerequisites: The user has a Windows setup with at least three monitors (Monitor A, Monitor B, Monitor C).
  2. A malicious web page opens a popup/window on Monitor A and calls element.requestFullscreen().
  3. 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.
  4. 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}.
  5. 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.
  6. The attacker waits for the scheduler loop quarantine to expire, performing heap grooming to replace the freed HWNDMessageHandler object.
  7. The user focuses or clicks any other Chrome window located on Monitor C.
  8. The WM_ACTIVATE message fires, triggering CheckAndHandleBackgroundFullscreenOnMonitor, which dereferences the dangling pointer and executes the attacker’s payload.

Suggested Fix

  1. Fix Cleanup Logic: Update RemoveCurrentWindowFromFullscreenMonitorMap() in ui/views/win/hwnd_message_handler.cc to use std::erase_if or a loop to ensure all map entries matching this are removed, rather than just the first one.
  2. Use Smart Pointers: Refactor FullscreenWindowMonitorMap to use base::raw_ptr<HWNDMessageHandler> or base::WeakPtr<HWNDMessageHandler> to benefit from MiraclePtr protections or automatic invalidation.
  3. Fix Update Logic: In SetFullscreen, instead of only erasing the entry for the current monitor, it may be safer to call RemoveCurrentWindowFromFullscreenMonitorMap() 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.

View on issue tracker