Medium watch ✔︎ verified CVSS 4.2 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
4.2
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Views
DescriptionInappropriate implementation in Views
ComponentViews
Sub-componentWindows
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety — factors set by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIConfirmed
Reviewer notes: none. Citations verified: StartDragAndDrop 67-140, no entry check on drag_drop_in_progress_ (set 99, cleared 131, destructor 62), DoDragDrop 113; diff adds CHECK(!drag_drop_in_progress_) as first statement. Fix is a CHECK (crash) not graceful return; UI-spoof/data-drop impact correctly inferred.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker498892595
Fix commit6d737c5ce20f (chromium/src) +1/-0
CWECWE-451
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:L
CISA KEVNot listed
CreditedGoogle
Reported2026-04-02 (per vendor advisory)
Fix landed2026-04-03 (public git)
Disclosed2026-05-12
Patch gap39 days — fix public in source before the advisory

Background

`DesktopDragDropClientWin`
the Windows Views drag-and-drop client that runs the native OLE drag loop for a desktop widget.
`StartDragAndDrop`
its entry point that optionally repositions the cursor for touch drags and then enters the blocking ::DoDragDrop loop.
`drag_drop_in_progress_`
the member flag tracking whether a drag loop is currently active.
Nested run loop
::DoDragDrop is a blocking native loop during which the UI thread can still process incoming messages.

Root Cause Analysis

In the pre-fix DesktopDragDropClientWin::StartDragAndDrop (ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc:67-140), the method never consulted drag_drop_in_progress_ on entry. For a touch drag it computed the touch point, checked is_touch_down() and that the cursor was over the source window (lines 83-91), and called desktop_host_->StartTouchDrag(...) at line 92 before setting drag_drop_in_progress_ = true at line 99 and entering ::DoDragDrop at line 113. Because ::DoDragDrop is a nested loop, a second StartDragAndDrop could be dispatched while the first was still active, and nothing stopped it from re-running the cursor-move and drag-start logic a second time. The invariant violated is “only one drag-and-drop loop may be active on this client at a time” — the flag existed (set at line 99, cleared at line 131, checked in the destructor at line 62) but was never enforced at the entry point.

The fix adds CHECK(!drag_drop_in_progress_); as the first statement of StartDragAndDrop, so a reentrant invocation aborts the process instead of starting a nested drag. This TIGHTENS the method by enforcing the single-active-drag invariant that was previously only tracked, never gated.

Key insight
The single-active-drag invariant was tracked by drag_drop_in_progress_ but never checked when StartDragAndDrop was entered, allowing a reentrant nested drag; the fix enforces it with a CHECK(!drag_drop_in_progress_) at the top of the function.

Attack Path

  1. Start a first drag A drag is initiated so StartDragAndDrop sets drag_drop_in_progress_ = true (desktop_drag_drop_client_win.cc:99) and blocks in ::DoDragDrop at line 113.
  2. Dispatch a second start reentrantly While the UI thread services messages inside that nested loop, a second StartDragAndDrop is delivered. (inferred)
  3. Nested drag runs pre-fix Lacking the entry guard, the second call re-runs the touch/cursor setup and attempts another drag within the first. (inferred)
  4. Fixed behavior With the patch, the second entry hits CHECK(!drag_drop_in_progress_) and terminates instead of proceeding.

Impact Assessment

Without the guard, a reentrant/nested invocation of the Windows drag-and-drop client could start a second native drag while one was active, which the advisory classifies as an inappropriate implementation in Views (medium severity, not exploited in the wild). The reporter’s scenario of using the nested drag to forcibly move the OS cursor and drop attacker-controlled data (e.g. a javascript: URL) onto overlapping UI relies on behavior not shown in this diff and is (inferred).

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

Audit Directions

  • Tracked-but-unenforced flags
    When an object keeps an “in progress” flag, verify the relevant entry points actually check it, not merely set and clear it.
  • Native nested loops
    Audit functions that enter blocking native loops (::DoDragDrop and similar) for reentrancy, since the UI thread keeps dispatching messages inside them.
  • Platform parity
    Compare each platform’s implementation of a shared interface (here Windows vs. Ash drag-drop) for a reentrancy guard present in one but missing in another.
From 6d737c5ce20ffa451419440fb52e617d9351fbf7 Mon Sep 17 00:00:00 2001
From: David Bienvenu <[email protected]>
Date: Fri, 03 Apr 2026 11:19:37 -0700
Subject: [PATCH] win: Guard against drag reentrancy

Use CHECK to protect against drag reentrancy.

Bug: 498892595
Change-Id: I87cd36e6844c9e28db4d3d232c9dd8ab04412f11
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7726485
Reviewed-by: Robert Liao <[email protected]>
Commit-Queue: David Bienvenu <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1609873}
---

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 8aeee53..b096048e 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
@@ -71,6 +71,7 @@
     const gfx::Point& screen_location,
     int allowed_operations,
     ui::mojom::DragEventSource source) {
+  CHECK(!drag_drop_in_progress_);
   gfx::Point touch_screen_point;
   if (source == ui::mojom::DragEventSource::kTouch) {
     source_window->GetHost()->ConvertDIPToPixels(&touch_screen_point);
Loading diff…

Original Bug Report

reported by [email protected]

Potential Reentrancy in Windows Drag-and-Drop allows UI Spoofing/Arbitrary Data Drop

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 security team.

Overview: The Windows implementation of drag-and-drop lacks a reentrancy guard. A compromised renderer can initiate a second, nested drag-and-drop operation while a first is already in progress, manipulating the OS cursor and dropping arbitrary data (like javascript: URLs) onto targeted overlapping applications or browser UI.

Affected files:

  • ui/views/widget/desktop_aura/desktop_drag_drop_client_win.cc
  • 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: 2025-08-20

Summary

A lack of state validation in DesktopDragDropClientWin::StartDragAndDrop allows a compromised renderer to trigger a nested, reentrant native drag-and-drop loop. This allows an attacker to bypass security checks that ensure the user is actively touching the screen, forcibly move the OS cursor, and execute a data ‘drop’ (including javascript: URLs) at an attacker-controlled screen coordinate that overlaps with the browser’s web bounds.

Technical Details

When a touch-based drag-and-drop is initiated by a web page, the browser calls DesktopDragDropClientWin::StartDragAndDrop. This function checks aura::Env::GetInstance()->is_touch_down() and verifies the cursor is over the source window. It then calls DesktopWindowTreeHostWin::StartTouchDrag(location), which uses the Windows ::SendInput API to forcibly move the OS cursor to the specified location. Finally, it enters the native ::DoDragDrop message loop, which is a blocking, nested run loop.

Critically, WebContentsViewAura::StartDragging instantiates base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop before calling this. This means that while blocked in ::DoDragDrop, the browser UI thread can still process incoming Mojo IPC messages.

Because DesktopDragDropClientWin::StartDragAndDrop does not check if drag_drop_in_progress_ is already true (unlike Ash, which explicitly blocks reentrancy), a compromised renderer can exploit this:

  1. The attacker (e.g., via a compromised renderer with an OOPIF) sends a LocalFrameHost::StartDragging IPC to start a touch drag at a safe coordinate P_safe. The browser enters the ::DoDragDrop loop and moves the OS cursor to P_safe.
  2. While blocked in ::DoDragDrop, touch release events are intercepted and not dispatched to WindowEventDispatcher. Consequently, the global is_touch_down() state freezes as true.
  3. The attacker sends a second LocalFrameHost::StartDragging IPC specifying a target location P_victim (a coordinate within the WebContents bounds, but potentially overlapping a sensitive UI element like the bookmarks bar or another window).
  4. The browser processes this second IPC reentrantly. The frozen is_touch_down() state and the current OS cursor position (P_safe) satisfy the security checks in the nested StartDragAndDrop call.
  5. StartTouchDrag(P_victim) is called, forcibly moving the OS cursor to P_victim using ::SendInput.
  6. The nested ::DoDragDrop fails immediately (Windows OLE disallows nested loops on the same thread). The cleanup code synthesizes a LEFTUP mouse event at P_victim.
  7. The outer ::DoDragDrop loop receives this LEFTUP event, interprets it as a user drop at the new cursor location (P_victim), and drops the attacker’s payload (e.g., a javascript: URL).

Impact

An attacker can drop arbitrary payload data (like javascript: URLs, which bypass the FilterURL check specifically to allow bookmarklets) onto any UI element or application that overlaps the WebContents bounds. This can lead to UXSS (saving a malicious bookmarklet) or dropping data onto an overlapping native application.

Proposed Fix

Add a reentrancy guard to DesktopDragDropClientWin::StartDragAndDrop. Similar to Ash’s DragDropController, it should check if a drag is already active and return early:

if (drag_drop_in_progress_) {
  return ui::PreferredDragOperation(
      ui::DragDropTypes::DropEffectToDragOperation(DROPEFFECT_NONE));
}

Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33


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