CVE-2026-17692
Overview
Files Changed
ui/base/clipboard/clipboard_format_type_win.cc
Patch
From 5ac500ccdb693cd2b56b0229d0876c6d1f89d429 Mon Sep 17 00:00:00 2001 From: Rohan Raja <[email protected]> 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 <[email protected]> Reviewed-by: Sambamurthy Bandaru <[email protected]> Commit-Queue: Rohan Raja <[email protected]> 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(
Original Bug Report
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.ccui/base/clipboard/clipboard_util_win.ccui/base/dragdrop/os_exchange_data_provider_win.cccontent/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
-
Background ThreadPool Workers: When virtual files are dropped (e.g., dragged out of Outlook or a ZIP folder),
GetVirtualFilesAsTempFilesinui/base/clipboard/clipboard_util_win.ccposts 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 callsCopyFileContentsToHGlobal, which callsClipboardFormatType::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. -
UI Thread: The UI thread routinely accesses this same map during drag-and-drop operations to check supported formats (via
HasVirtualFilenamesorHasFileContents, which queryFileContentAtIndexType(0)). Furthermore, an attacker inside a compromised renderer can repeatedly send MojoStartDraggingIPCs, which callOSExchangeDataProviderWin::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)
- The user drags a group of virtual files (e.g. from Outlook) and drops them onto a Chrome tab to start the background
ExtractVirtualFilesworker task. - While the background task is running, a script in a compromised renderer repeatedly invokes
StartDraggingIPCs withfile_contentspopulated to force the UI thread to callSetFileContents->FileContentAtIndexType(0)on the main UI thread. - 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.