Medium watch CVSS 4.2 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
4.2
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient data validation in DataTransfer
DescriptionInsufficient data validation in DataTransfer
ComponentDataTransfer
Bug ClassInput Validation
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
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.
Tracker498765082
Fix commitd96c0d8181b6 (chromium/src) +10/-0
CWECWE-20 (Improper input validation)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-04-02 (per vendor advisory)
Fix landed2026-04-24 (public git)
Disclosed2026-05-05
Patch gap11 days — fix public in source before the advisory

Background

`StartDragging`
the browser-process entry point (per the commit subject, in WebContentsViewAura and WebContentsViewMac) that begins a drag-and-drop operation requested by the renderer; the enclosing function name is not shown in the @@ hunk headers and is taken from the commit subject.
Reentrancy via nested run loops
on both platforms StartDragging spins a nested message loop (the mac comment notes “allowing nested tasks … also allows Close()”), so renderer-driven events can re-enter the same code before the outer call finishes.
`drag_security_info_` / `drag_source_start_rwh_`
per-view state tracking an in-flight drag: drag_security_info_.did_initiate() records that this view started a drag, and drag_source_start_rwh_ holds the source RenderWidgetHostImpl for the active mac drag.

Root Cause Analysis

On both aura and mac, StartDragging could be entered again while a drag it had already begun was still running inside its nested loop, because neither path checked whether a drag was already in progress before mutating drag state. The aura path proceeds to set up drag state and (if there is no DragDropClient) call web_contents_->SystemDragEnded(source_rwh), while the mac path reads source_rfh.GetLastCommittedOrigin() and re-enters logic that can Close()/deallocate this; a reentrant call therefore manipulates or tears down drag state that an outer frame is still relying on.

The fix adds an early return at the top of each function: aura bails when drag_security_info_.did_initiate() is already set, and mac bails when drag_source_start_rwh_ is already populated. The mac path additionally calls drag_source_start_rwh_.reset() after the drag completes so the guard is cleared for legitimate subsequent drags. This tightens the entry condition, enforcing the invariant that only one drag may be initiated per view at a time.

Key insight
The core mistake was allowing StartDragging to be re-entered while a drag it started was still active, letting a renderer manipulate drag state mid-flight; the fix rejects reentrant invocations with an early return keyed on existing in-flight drag state.

Attack Path

  1. Begin a legitimate drag A malicious renderer requests a drag, entering browser-side StartDragging, which records in-flight state and spins a nested run loop.
  2. Re-enter during the nested loop While that loop is pumping, the renderer drives another drag-start (or related event) so StartDragging is invoked a second time before the first returns.
  3. Corrupt or exploit drag state The reentrant call manipulates the same drag state (per the added comment, “an attempt to exploit drag state”); on mac this path can even reach Close() and deallocate this while the outer call still references it.
  4. Memory-safety / state-confusion consequence (inferred) The patch does not demonstrate a concrete use-after-free or read/write primitive, so any such outcome beyond corrupted drag state is (inferred).

Impact Assessment

A compromised or malicious renderer can issue reentrant drag-start requests to confuse browser-process drag state, matching the advisory’s Insufficient data validation in DataTransfer at medium severity. The bug is in the browser process on aura (Linux/ChromeOS) and mac and requires the renderer to initiate overlapping drags. The advisory does not report exploitation in the wild, and escalation to memory corruption is (inferred) rather than shown by the diff.

Changed Functions

FunctionChangeNotes
if
content/browser/web_contents/web_contents_view_mac.mm
modified

Files Changed

  • content/browser/web_contents/web_contents_view_aura.cc
  • content/browser/web_contents/web_contents_view_mac.mm

Audit Directions

  • Nested-loop reentrancy
    review browser-process handlers that spin nested run loops (drag/drop, modal dialogs, context menus) for missing in-progress guards that let a renderer re-enter and mutate state.
  • Self-deallocation during nested tasks
    flag paths whose comments warn that nested tasks may Close()/delete this, and verify every post-nested-loop access re-checks liveness.
  • Platform-parallel view code
    when one WebContentsView* platform backend gains a guard, audit the other backends (aura, mac, and others) for the same unguarded entry point.
From d96c0d8181b6829ef9d9a258080ac87d9b72a72b Mon Sep 17 00:00:00 2001
From: Joel Hockey <joelhockey@chromium.org>
Date: Fri, 24 Apr 2026 13:27:16 -0700
Subject: [PATCH] Disallow reentrant StartDragging() for aura and mac

Bug: 498765082
Change-Id: I6c3cfd64925deb4c8e40f22574226fcc340e9738
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7792498
Auto-Submit: Joel Hockey <joelhockey@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1620425}
---

diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc
index aca68d7..a71c6b9b 100644
--- a/content/browser/web_contents/web_contents_view_aura.cc
+++ b/content/browser/web_contents/web_contents_view_aura.cc
@@ -1205,6 +1205,10 @@
   aura::Window* root_window = GetNativeView()->GetRootWindow();
   RenderWidgetHostImpl* const source_rwh =
       static_cast<RenderWidgetHostImpl*>(source_rfh.GetRenderWidgetHost());
+  // Disallow reentrant drag which could be an attempt to exploit drag state.
+  if (drag_security_info_.did_initiate()) {
+    return;
+  }
   if (!aura::client::GetDragDropClient(root_window)) {
     web_contents_->SystemDragEnded(source_rwh);
     return;
diff --git a/content/browser/web_contents/web_contents_view_mac.mm b/content/browser/web_contents/web_contents_view_mac.mm
index ff26bbb..2f4f6d6 100644
--- a/content/browser/web_contents/web_contents_view_mac.mm
+++ b/content/browser/web_contents/web_contents_view_mac.mm
@@ -210,6 +210,10 @@
     const blink::mojom::DragEventSourceInfo& event_info) {
   RenderWidgetHostImpl* source_rwh =
       static_cast<RenderWidgetHostImpl*>(source_rfh.GetRenderWidgetHost());
+  // Disallow reentrant drag which could be an attempt to exploit drag state.
+  if (drag_source_start_rwh_) {
+    return;
+  }
   url::Origin source_origin = source_rfh.GetLastCommittedOrigin();
   // By allowing nested tasks, the code below also allows Close(),
   // which would deallocate |this|.  The same problem can occur while
@@ -692,6 +696,8 @@
       transformed_screen_point.x(), transformed_screen_point.y(),
       static_cast<ui::mojom::DragOperation>(drag_operation),
       drag_source_start_rwh_.get());
+
+  drag_source_start_rwh_.reset();
 }
 
 void WebContentsViewMac::DraggingEntered(DraggingInfoPtr dragging_info,
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Site Isolation bypass via StartDragging reentrancy in WebContentsViewAndroid

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: A lack of active-drag checks in WebContentsViewAndroid::StartDragging allows a compromised renderer to overwrite the browser’s drag security state. By sending a malicious StartDragging IPC during an active legitimate drag, an attacker can bypass Site Isolation to intercept cross-site drag data and gain unauthorized read access to dropped local files.

Affected files:

  • content/browser/web_contents/web_contents_view_android.cc
  • content/browser/renderer_host/render_frame_host_impl.cc
  • content/browser/renderer_host/render_widget_host_impl.cc
  • content/browser/web_contents/web_contents_view_drag_security_info.cc

Estimated timestamp from git blame: 2026-01-23

Summary

There is a potential vulnerability in how Android Chrome handles concurrent drag-and-drop operations. Specifically, WebContentsViewAndroid::StartDragging lacks reentrancy guards or checks for an already-active drag. This allows a compromised renderer (e.g., a cross-site iframe) to spam the StartDragging IPC while a legitimate drag from the main frame is ongoing. Doing so overwrites the browser’s internal security state, bypassing the IsValidDragTarget protection (introduced in crbug.com/59081) and allowing the attacker to intercept sensitive cross-site drag data and gain unauthorized file access.

Vulnerability Details

When a drag starts, WebContentsViewAndroid::StartDragging caches the initiating frame’s SiteInstanceGroup ID into drag_security_info_ and calls the Android OS via native_view->StartDragAndDrop(). This cached ID is later used by IsValidDragTarget to ensure that data dragged from one origin cannot be dropped into a cross-origin frame on the same page.

However, because there are no browser-side state checks preventing concurrent StartDragging IPCs, a compromised renderer can send its own StartDragging IPC immediately after a legitimate drag begins. When the browser processes this malicious IPC:

  1. It unconditionally overwrites current_source_rwh_for_drag_ with the attacker’s RenderWidgetHostImpl.
  2. It calls drag_security_info_.OnDragInitiated, which unconditionally overwrites the cached site_instance_group_id_ with the attacker’s ID, completely poisoning the security state.
  3. It attempts to call native_view->StartDragAndDrop() a second time. Because the OS already has an active drag, this returns false.

Crucially, the failure path for StartDragAndDrop calls OnSystemDragEnded, which notifies the renderer but does not reset drag_security_info_. The secure state reset only happens in OnDragEnded, which only fires when the OS successfully finishes a drag.

Consequently, the browser is left with an active legitimate drag, but its security tracker is locked to the attacker’s ID. When the user drops the item onto the attacker’s iframe, IsValidDragTarget compares the target’s ID against the poisoned cached ID. The check passes, and the browser dispatches the drop to the attacker.

Impact

  • Site Isolation Bypass: A compromised renderer can exfiltrate sensitive data (text, URLs, images) dragged from other origins within the same WebContents.
  • Unauthorized File Access / Partial Sandbox Escape: If the user drags a local file into the browser, RenderWidgetHostImpl::DragTargetDrop calls GrantFileAccessFromDropData. Because the drop was erroneously validated, the browser explicitly grants the attacker’s renderer process read access to the local file via ChildProcessSecurityPolicyImpl::GrantReadFile.
  • Denial of Service: Overwriting current_source_rwh_for_drag_ prevents the victim frame from receiving the DragSourceEndedAt event, leaving the frame permanently suppressing input.

Suggested Exploitation Steps

(Note: These are potential steps based on code analysis; our tooling agent cannot execute working exploits.)

  1. Navigate to https://victim.com, which embeds a cross-site iframe from https://attacker.com.
  2. Assume the attacker.com renderer process is compromised and can send arbitrary Mojo IPCs.
  3. The user starts a physical drag operation on a sensitive element in the victim.com main frame.
  4. The browser process begins the drag and caches the SiteInstanceGroup ID for victim.com.
  5. The compromised attacker.com renderer immediately sends a blink::mojom::LocalFrameHost::StartDragging IPC to the browser.
  6. The browser unconditionally processes the IPC, overwriting the cached SiteInstanceGroup ID with attacker.com’s ID. The subsequent OS drag call fails, but the poisoned state persists.
  7. The user drops the item onto the attacker.com iframe.
  8. The browser’s IsValidDragTarget check passes because the state now matches the attacker’s ID.
  9. The attacker.com renderer receives the victim’s drag data and any associated file access grants.

Suggested Fix

  1. Add Active Drag Checks: In WebContentsViewAndroid::StartDragging (and potentially other platform implementations), check if a drag is already active before processing a new StartDragging request. If an OS drag is in progress, reject the incoming IPC or ignore it.
  2. Robust Cleanup: Ensure that if native_view->StartDragAndDrop() fails, the resulting cleanup (e.g., OnSystemDragEnded) properly resets drag_security_info_ by calling drag_security_info_.OnDragEnded() to clear the poisoned state.

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