Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in DataTransfer
DescriptionUse after free in DataTransfer
ComponentDataTransfer
Bug ClassUAF
Tracker517350808
Fix commit5ac500ccdb69 (chromium/src) +8/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • ui/base/clipboard/clipboard_format_type_win.cc
From 5ac500ccdb693cd2b56b0229d0876c6d1f89d429 Mon Sep 17 00:00:00 2001
From: Rohan Raja <roraja@microsoft.com>
Date: Mon, 15 Jun 2026 05:59:22 -0700
Subject: [PATCH] Fix data race in ClipboardFormatType on Windows

ClipboardFormatType::FileContentAtIndexType() inserts into a
process-global std::map<LONG, ClipboardFormatType> from both the browser
UI thread (SetFileContents, drag-over predicates) and base::ThreadPool
workers (ExtractVirtualFiles -> CopyFileContentsToHGlobal) without any
synchronization.

Serialize entry into FileContentAtIndexType() with a function-local
static base::NoDestructor<base::Lock> taken via base::AutoLock. This
matches the existing locking pattern used elsewhere in
ui/base/clipboard/ (Clipboard::ClipboardMapLock() in clipboard.cc,
clipboard_android.cc, clipboard_non_backed.cc).

Bug: 517350808
Change-Id: I97c18ed25c782d8c43458020f8913623c7a20ba1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7885158
Reviewed-by: Rakesh Goulikar <ragoulik@microsoft.com>
Reviewed-by: Sambamurthy Bandaru <sambamurthy.bandaru@microsoft.com>
Commit-Queue: Rohan Raja <roraja@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1646746}
---

diff --git a/ui/base/clipboard/clipboard_format_type_win.cc b/ui/base/clipboard/clipboard_format_type_win.cc
index bfa94d7..2b3a33a 100644
--- a/ui/base/clipboard/clipboard_format_type_win.cc
+++ b/ui/base/clipboard/clipboard_format_type_win.cc
@@ -15,6 +15,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
 #include "ui/base/clipboard/clipboard_constants.h"
 
 namespace ui {
@@ -234,6 +235,13 @@
 // static
 const ClipboardFormatType& ClipboardFormatType::FileContentAtIndexType(
     LONG index) {
+  // FileContentTypeMap() is accessed from both the browser UI thread and
+  // base::ThreadPool workers during virtual-file drag-and-drop, so serialize
+  // map mutation. base::NoDestructor only makes construction thread-safe;
+  // std::map::insert itself is not.
+  static base::NoDestructor<base::Lock> map_lock;
+  base::AutoLock auto_lock(*map_lock);
+
   auto& index_to_type_map = FileContentTypeMap();
 
   auto insert_or_assign_result = index_to_type_map.insert(
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential data race and heap corruption in ClipboardFormatType on Windows

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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential data race exists on a process-global static map in ClipboardFormatType::FileContentAtIndexType on Windows. The map is concurrently mutated and accessed without synchronization by background ThreadPool threads and the main UI thread during drag-and-drop operations. This can lead to red-black tree node pointer corruption and potential out-of-bounds reads/writes in the unsandboxed browser process.

Affected files:

  • ui/base/clipboard/clipboard_format_type_win.cc
  • ui/base/clipboard/clipboard_util_win.cc
  • ui/base/dragdrop/os_exchange_data_provider_win.cc
  • content/browser/web_contents/web_contents_view_aura.cc

Estimated timestamp from git blame: 2019-04-29

Root Cause Analysis

In ui/base/clipboard/clipboard_format_type_win.cc, the static method ClipboardFormatType::FileContentAtIndexType accesses and mutates a process-global static std::map<LONG, ClipboardFormatType> returned by FileContentTypeMap() without any synchronization:

// ui/base/clipboard/clipboard_format_type_win.cc
std::map<LONG, ClipboardFormatType>& ClipboardFormatType::FileContentTypeMap() {
  static base::NoDestructor<std::map<LONG, ClipboardFormatType>>
      index_to_type_map;
  return *index_to_type_map;
}

const ClipboardFormatType& ClipboardFormatType::FileContentAtIndexType(
    LONG index) {
  auto& index_to_type_map = FileContentTypeMap();
  auto insert_or_assign_result = index_to_type_map.insert(
      {index, ClipboardFormatType(
                  RegisterClipboardFormatChecked(CFSTR_FILECONTENTS), index,
                  TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE)});
  return insert_or_assign_result.first->second;
}

The magic-static initialization of the base::NoDestructor object ensures thread-safe construction of the container itself, but subsequent mutations using insert() are entirely unsynchronized.

Potential Concurrent Callers & Race Window

  1. Background ThreadPool Workers: When virtual files are dropped (e.g., dragged out of Outlook or a ZIP folder), GetVirtualFilesAsTempFiles in ui/base/clipboard/clipboard_util_win.cc posts an unsequenced background task to the ThreadPool to extract the files:

    base::ThreadPool::PostTaskAndReplyWithResult(
        FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
        base::BindOnce(&ExtractVirtualFiles, marshaled_stream, display_names), ...);
    

    Within ExtractVirtualFiles (running on a ThreadPool worker thread), the worker loops through the file indices and calls CopyFileContentsToHGlobal, which calls ClipboardFormatType::FileContentAtIndexType(index). The worker thread can block on synchronous COM/disk I/O operations (e.g., copying storage or reading streams) inside the loop, keeping the background extraction active for an extended duration.

  2. UI Thread: The UI thread routinely accesses this same map during drag-and-drop operations to check supported formats (via HasVirtualFilenames or HasFileContents, which query FileContentAtIndexType(0)). Furthermore, an attacker inside a compromised renderer can repeatedly send Mojo StartDragging IPCs, which call OSExchangeDataProviderWin::SetFileContents -> FileContentAtIndexType(0) on the UI thread.

If the background ThreadPool thread is modifying the map (e.g., inserting index = 1) while the UI thread concurrently queries or modifies index 0 (via a drag-over event or a renderer-initiated StartDragging IPC), a data race occurs.

Potential Impact

Because std::map is typically implemented as a red-black tree, concurrent unsynchronized insertions and lookups corrupt the tree’s internal node pointers (such as parent, left, and right pointers). Since these are standard C++ pointers and not raw_ptr<>, MiraclePtr (BackupRefPtr) does not apply to the internal STL container nodes. Any subsequent traversal of the corrupted tree can lead to out-of-bounds reads/writes or wild pointer dereferences on the heap in the unsandboxed browser process on Windows, potentially resulting in a sandbox escape and remote code execution.

Suggested Steps to Trigger (Potential)

  1. The user drags a group of virtual files (e.g. from Outlook) and drops them onto a Chrome tab to start the background ExtractVirtualFiles worker task.
  2. While the background task is running, a script in a compromised renderer repeatedly invokes StartDragging IPCs with file_contents populated to force the UI thread to call SetFileContents -> FileContentAtIndexType(0) on the main UI thread.
  3. This triggers concurrent insertions/accesses to index_to_type_map, corrupting the red-black tree structure and leading to heap corruption.

Note: Since our tooling cannot run active PoC code, these steps represent a potential and theoretical attack vector based on static analysis.

Proposed Fix

Access to the global static index_to_type_map inside ClipboardFormatType::FileContentAtIndexType must be synchronized. A simple and robust fix is to protect the static map modifications using a base::Lock:

// ui/base/clipboard/clipboard_format_type_win.cc
const ClipboardFormatType& ClipboardFormatType::FileContentAtIndexType(
    LONG index) {
  static base::NoDestructor<base::Lock> map_lock;
  base::AutoLock lock(*map_lock);
  auto& index_to_type_map = FileContentTypeMap();

  auto insert_or_assign_result = index_to_type_map.insert(
      {index, ClipboardFormatType(
                  RegisterClipboardFormatChecked(CFSTR_FILECONTENTS), index,
                  TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE)});
  return insert_or_assign_result.first->second;
}

Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8


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