CVE-2026-9110
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc |
modified |
Files Changed
ui/views/widget/desktop_aura/desktop_drag_drop_client_win.ccui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
Patch
From 16dcbc3d1476a35a652d71c034c851fa6da109d0 Mon Sep 17 00:00:00 2001
From: David Bienvenu <davidbienvenu@chromium.org>
Date: Wed, 13 May 2026 07:44:42 -0700
Subject: [PATCH] win: Use static to keep track of in progress drags.
Prevent multiple drags from different windows.
Bug: 503551154
Change-Id: I7bb052b88a7eb4fe06a827a55bbb05db2391e8f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7841115
Reviewed-by: Keren Zhu <kerenzhu@chromium.org>
Commit-Queue: David Bienvenu <davidbienvenu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1629961}
---
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc b/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
index b096048e..6d31bee 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
@@ -6,6 +6,7 @@
#include <memory>
+#include "base/auto_reset.h"
#include "base/notimplemented.h"
#include "base/scoped_observation.h"
#include "base/threading/hang_watcher.h"
@@ -47,19 +48,21 @@
scoped_observation_;
};
+bool g_is_dragging = false;
+
} // namespace
DesktopDragDropClientWin::DesktopDragDropClientWin(
aura::Window* root_window,
HWND window,
DesktopWindowTreeHostWin* desktop_host)
- : drag_drop_in_progress_(false), desktop_host_(desktop_host) {
+ : desktop_host_(desktop_host) {
drop_target_ = new DesktopDropTargetWin(root_window);
drop_target_->Init(window);
}
DesktopDragDropClientWin::~DesktopDragDropClientWin() {
- if (drag_drop_in_progress_) {
+ if (g_is_dragging) {
DragCancel();
}
}
@@ -71,7 +74,7 @@
const gfx::Point& screen_location,
int allowed_operations,
ui::mojom::DragEventSource source) {
- CHECK(!drag_drop_in_progress_);
+ CHECK(!g_is_dragging);
gfx::Point touch_screen_point;
if (source == ui::mojom::DragEventSource::kTouch) {
source_window->GetHost()->ConvertDIPToPixels(&touch_screen_point);
@@ -97,7 +100,6 @@
SourceWindowObserver source_window_observer(source_window);
base::WeakPtr<DesktopDragDropClientWin> alive(weak_factory_.GetWeakPtr());
- drag_drop_in_progress_ = true;
drag_source_ = ui::DragSourceWin::Create();
Microsoft::WRL::ComPtr<ui::DragSourceWin> drag_source_copy = drag_source_;
drag_source_copy->set_data(data.get());
@@ -105,6 +107,7 @@
true);
DWORD effect;
+ base::AutoReset<bool> drag_scoper(&g_is_dragging, true);
// Never consider the current scope as hung. The hang watching deadline (if
// any) is not valid since the user can take unbounded time to complete the
@@ -128,10 +131,6 @@
}
drag_source_copy->set_data(nullptr);
- if (alive) {
- drag_drop_in_progress_ = false;
- }
-
if (result != DRAGDROP_S_DROP) {
effect = DROPEFFECT_NONE;
}
@@ -141,11 +140,13 @@
}
void DesktopDragDropClientWin::DragCancel() {
- drag_source_->CancelDrag();
+ if (drag_source_) {
+ drag_source_->CancelDrag();
+ }
}
bool DesktopDragDropClientWin::IsDragDropInProgress() {
- return drag_drop_in_progress_;
+ return g_is_dragging;
}
void DesktopDragDropClientWin::AddObserver(
diff --git a/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h b/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
index 69bf4e0..374f7ba 100644
--- a/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
+++ b/ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
@@ -61,8 +61,6 @@
}
private:
- bool drag_drop_in_progress_;
-
Microsoft::WRL::ComPtr<ui::DragSourceWin> drag_source_;
scoped_refptr<DesktopDropTargetWin> drop_target_;
Original Bug Report
Potential Drag-and-Drop Reentrancy Protection Bypass via Multiple Windows
Flapjack, 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: The reentrancy protection in DesktopDragDropClientWin is incomplete because it relies on a per-instance flag, allowing multiple windows to bypass the check. An attacker can use a second window to trigger a nested drag operation, manipulate the OS cursor, and perform unauthorized drops. This allows a compromised renderer to drop arbitrary payloads at sensitive locations, potentially leading to UXSS or RCE.
Affected files:
ui/views/widget/desktop_aura/desktop_drag_drop_client_win.ccui/views/widget/desktop_aura/desktop_drag_drop_client_win.hui/views/widget/desktop_aura/desktop_window_tree_host_win.cccontent/browser/web_contents/web_contents_view_aura.cc
Estimated timestamp from git blame: 2026-04-03
Vulnerability Description
A potential vulnerability exists in the Windows drag-and-drop implementation where the protection against reentrancy can be bypassed using multiple browser windows.
While DesktopDragDropClientWin::StartDragAndDrop attempts to prevent nested drag loops using CHECK(!drag_drop_in_progress_), drag_drop_in_progress_ is an instance variable. Because DesktopWindowTreeHostWin creates a unique DesktopDragDropClientWin instance for each window, the check fails to prevent a second window on the same UI thread from initiating a drag operation while the first is still active.
Furthermore, WebContentsViewAura::StartDragging instantiates a ScopedAllowApplicationTasksInNativeNestedLoop. This allows Mojo IPCs (such as new StartDragging requests) to be handled while the UI thread is blocked inside the system-modal ::DoDragDrop loop.
Potential Exploit Scenario
While we do not have a working Proof of Concept, the following sequence demonstrates how a compromised renderer could exploit this:
- Setup: A compromised renderer opens two windows: a small foreground popup (W1) and a large background window (W2) whose bounds encompass an attacker-chosen sensitive drop target (
P_victim). - Initial Drag: The user is tricked into initiating a touch-based drag on W1.
aura::Env::GetInstance()->SetTouchDown(true)is called. - W1 Loop: W1’s renderer sends a
StartDraggingIPC. The browser callsDesktopDragDropClientWin::StartDragAndDrop. W1 passes theCHECK(!drag_drop_in_progress_), sets its flag, and enters the blocking::DoDragDroploop. - Cursor Shift: While the browser UI thread is blocked in
DoDragDrop, W1’s renderer uses JavaScript (window.moveTo()) to move W1 off-screen. The physical touch point (and OS cursor) is now hovering over W2. - Malicious Nested Drag: W2’s renderer sends a second
StartDraggingIPC, specifyingkTouchas the source andP_victimas the location. - Reentrancy: Because of the
ScopedAllowApplicationTasksInNativeNestedLoopapplied during W1’s drag, the UI thread pulls W2’s IPC and executes it. - Bypass: W2 calls
DesktopDragDropClientWin::StartDragAndDrop. Because W2 is a different instance than W1, itsdrag_drop_in_progress_flag is false, bypassing theCHECK. - Cursor Hijacking: W2 verifies the touch state (which is still true from step 2) and that the cursor is over itself (true from step 4). It then calls
StartTouchDrag(P_victim). This method invokesui::SendMouseEvent, which uses the Windows::SendInputAPI to forcibly teleport the OS cursor toP_victimand simulate aLEFTDOWNevent. - Artificial Release: W2 sets its flag and calls
::DoDragDrop. Windows OLE rejects the nested loop immediately. W2 proceeds to cleanup, callingFinishTouchDrag, which uses::SendInputto simulate aLEFTUPevent atP_victim. - Malicious Drop: W2’s execution finishes. The UI thread unwinds back to W1’s active
::DoDragDroploop. W1 processes the syntheticLEFTUPevent injected by W2 and drops W1’s malicious payload exactly atP_victim.
Impact
This sequence allows an attacker to achieve the same impact as previously identified reentrancy vulnerabilities: UI spoofing, saving malicious bookmarklets (UXSS), or dropping arbitrary files onto overlapping native applications (RCE).
Suggested Fix
- Global Flag: The
drag_drop_in_progress_flag inDesktopDragDropClientWinshould be made a thread-local or static variable to ensure that no two instances can initiate a drag operation simultaneously on the same thread. - Remove IPC Processing: Investigate if the
ScopedAllowApplicationTasksInNativeNestedLoopinWebContentsViewAura::StartDraggingcan be removed. Preventing IPC processing during the drag-and-drop loop eliminates the root cause of this reentrancy class.
Evaluated with Chrome root at commit: c0eb5541aebfa4ea08806eaf6e94bcc69f87ab2f
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.