Critical chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in UI
DescriptionInappropriate implementation in UI
ComponentUI
Bug ClassLogic Error
Tracker503551154
Fix commit16dcbc3d1476 (chromium/src) +11/-12
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-19

Changed Functions

FunctionChangeNotes
if
ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
modified

Files Changed

  • ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
  • ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
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_;
Loading diff…

Original Bug Report

reported by rj...@google.com

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.cc
  • ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h
  • ui/views/widget/desktop_aura/desktop_window_tree_host_win.cc
  • content/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:

  1. 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).
  2. Initial Drag: The user is tricked into initiating a touch-based drag on W1. aura::Env::GetInstance()->SetTouchDown(true) is called.
  3. W1 Loop: W1’s renderer sends a StartDragging IPC. The browser calls DesktopDragDropClientWin::StartDragAndDrop. W1 passes the CHECK(!drag_drop_in_progress_), sets its flag, and enters the blocking ::DoDragDrop loop.
  4. 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.
  5. Malicious Nested Drag: W2’s renderer sends a second StartDragging IPC, specifying kTouch as the source and P_victim as the location.
  6. Reentrancy: Because of the ScopedAllowApplicationTasksInNativeNestedLoop applied during W1’s drag, the UI thread pulls W2’s IPC and executes it.
  7. Bypass: W2 calls DesktopDragDropClientWin::StartDragAndDrop. Because W2 is a different instance than W1, its drag_drop_in_progress_ flag is false, bypassing the CHECK.
  8. 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 invokes ui::SendMouseEvent, which uses the Windows ::SendInput API to forcibly teleport the OS cursor to P_victim and simulate a LEFTDOWN event.
  9. Artificial Release: W2 sets its flag and calls ::DoDragDrop. Windows OLE rejects the nested loop immediately. W2 proceeds to cleanup, calling FinishTouchDrag, which uses ::SendInput to simulate a LEFTUP event at P_victim.
  10. Malicious Drop: W2’s execution finishes. The UI thread unwinds back to W1’s active ::DoDragDrop loop. W1 processes the synthetic LEFTUP event injected by W2 and drops W1’s malicious payload exactly at P_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

  1. Global Flag: The drag_drop_in_progress_ flag in DesktopDragDropClientWin should be made a thread-local or static variable to ensure that no two instances can initiate a drag operation simultaneously on the same thread.
  2. Remove IPC Processing: Investigate if the ScopedAllowApplicationTasksInNativeNestedLoop in WebContentsViewAura::StartDragging can 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.

View on issue tracker