CVE-2026-12455
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc |
modified |
Files Changed
chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc
Patch
From f8761f903162de5da5ef4a524f8d08a17472373a Mon Sep 17 00:00:00 2001 From: Vince Lugli <[email protected]> Date: Mon, 08 Jun 2026 13:09:03 -0700 Subject: [PATCH] Fix for potential UAF in TabDragController::DragBrowserToNewTabStrip Using a WeakPtr to determine if the drag controller has been delete while moving to a new tab strip. Check weak_this after potential transfer, release, and detach/attach events. Returns Liveness::kDeleted if weak_this is ever null. Bug: 517069848 Change-Id: Ie6bb082194f86e1294781427d335516a40c63e9e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7904496 Reviewed-by: Kaan Alsan <[email protected]> Reviewed-by: David Pennington <[email protected]> Commit-Queue: Vince Lugli <[email protected]> Cr-Commit-Position: refs/heads/main@{#1643425} --- diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc index 5f026ed..f3a732c 100644 --- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc +++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc @@ -1023,6 +1023,8 @@ TRACE_EVENT1("views", "TabDragController::DragBrowserToNewTabStrip", "point_in_screen", point_in_screen.ToString()); + base::WeakPtr<TabDragController> weak_this = weak_factory_.GetWeakPtr(); + dragging_tabs_session_ = nullptr; if (!target_context) { @@ -1036,6 +1038,8 @@ GetAttachedBrowserWidget()->GetGestureRecognizer()->TransferEventsTo( attached_native_view, target_context->GetWidget()->GetNativeView(), ui::TransferTouchesBehavior::kDontCancel); + + CHECK(weak_this); #endif if (current_state_ == DragState::kDraggingWindow) { @@ -1066,6 +1070,7 @@ // control returns to RunMoveLoop(). VLOG(1) << "EndMoveLoop in DragBrowserToNewTabStrip"; browser_widget->EndMoveLoop(); + CHECK(weak_this); // Ideally we would always swap the tabs now, but on non-ash Windows, it // seems that running the move loop implicitly activates the window when @@ -1081,6 +1086,7 @@ // capture. DetachAndAttachToNewContext(ReleaseCapture::kDontReleaseCapture, target_context); + CHECK(weak_this); // Enter kWaitingToExitRunLoop until we actually have exited the nested // run loop. Otherwise, we might attempt to start another nested run loop, @@ -1110,6 +1116,7 @@ // behaviour. DetachAndAttachToNewContext(ReleaseCapture::kDontReleaseCapture, target_context); + CHECK(weak_this); StartDraggingTabsSession(false, point_in_screen); attached_context_->GetWidget()->Activate();
Original Bug Report
Potential Use-After-Free in TabDragController::DragBrowserToNewTabStrip
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 Use-After-Free (UAF) vulnerability exists in the browser process due to reentrant destruction during event transfer. When touch-dragging a tab, calling GestureRecognizer::TransferEventsTo synchronously dispatches synthetic events that can destroy the active TabDragController or its target context. Subsequent accesses to members on the deleted this pointer or the target_context raw pointer lead to memory corruption.
Affected files:
chrome/browser/ui/views/tabs/dragging/tab_drag_controller.ccchrome/browser/ui/views/tabs/tab_strip.cc
Estimated timestamp from git blame: 2015-03-18
Detailed Description
A potential Use-After-Free (UAF) vulnerability exists in the browser process of Google Chrome on Aura-based platforms (Windows, ChromeOS, and Linux with touch input) within TabDragController::DragBrowserToNewTabStrip.
Root Cause and Mechanism
When touch-dragging a tab across browser windows, TabDragController::DragBrowserToNewTabStrip invokes GestureRecognizer::TransferEventsTo() synchronously to transfer event stream ownership between the attached view and the target view’s widget:
// chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc (lines 1028-1030)
GetAttachedBrowserWidget()->GetGestureRecognizer()->TransferEventsTo(
attached_native_view, target_context->GetWidget()->GetNativeView(),
ui::TransferTouchesBehavior::kDontCancel);
Inside GestureRecognizerImpl::TransferEventsTo (ui/events/gestures/gesture_recognizer_impl.cc, line 158), the code cancels active touches on other consumers by dispatching synthetic touch cancel events (ui::TouchEvent with kTouchCancelled) through the complete Aura event pipeline. This dispatch is fully synchronous and reentrant.
If one of these synchronous events causes the tab drag session to abort (for example, by triggering a gesture end event or a capture loss), the drag controller terminates. Specifically, ending the drag runs EndDragImpl, which executes owning_context->DestroyDragController(), resetting the std::unique_ptr<TabDragController> inside TabStrip (at chrome/browser/ui/views/tabs/tab_strip.cc:479) and destroying this.
Upon returning from TransferEventsTo(), DragBrowserToNewTabStrip immediately accesses fields on the deleted this pointer (such as current_state_ on line 1033) and later accesses the raw target_context pointer (which may have been freed during window closure or event cancellation on line 1047), leading to Use-After-Free memory corruption.
Comparison with Guarded Paths
An identical call to TransferEventsTo() located in TabDragController::DetachIntoNewBrowserAndRunMoveLoop() is properly protected with a WeakPtr liveness check and a reentrancy guard:
// chrome/browser/ui/views/tabs/dragging/tab_drag_controller.cc (lines 1603-1618)
{
auto ref = weak_factory_.GetWeakPtr();
base::WeakAutoReset<TabDragController, bool> reentrant_destruction_guard(
ref, &TabDragController::expect_stay_alive_, true);
...
attached_widget->GetGestureRecognizer()->TransferEventsTo(
attached_widget->GetNativeView(), dragged_widget->GetNativeView(),
ui::TransferTouchesBehavior::kDontCancel);
CHECK(ref) << "Drag session was ended as part of transferring events...";
}
However, the call inside DragBrowserToNewTabStrip lacks any such liveness checks or guards.
Potential Steps to Reproduce
Since our analysis is static and our tooling does not run live code, the following are suggested/potential steps to trigger this condition:
- Open two Chrome browser windows on an Aura platform with touch support enabled.
- Initiate a touch drag of a tab from the source window to the destination window.
- Trigger a gesture cancellation or nested event loop processing (for example, via window close IPCs or focus changes coordinated by a compromised renderer process) precisely while
TransferEventsTois processing synchronous event dispatch. - Observe if a crash or memory corruption occurs due to the subsequent evaluation of
current_state_on line 1033.
Suggested Remediation
Add a base::WeakPtr liveness check immediately following the TransferEventsTo call inside DragBrowserToNewTabStrip to exit safely if the controller was destroyed:
#if defined(USE_AURA)
base::WeakPtr<TabDragController> ref(weak_factory_.GetWeakPtr());
// Only Aura windows are gesture consumers.
GetAttachedBrowserWidget()->GetGestureRecognizer()->TransferEventsTo(
attached_native_view, target_context->GetWidget()->GetNativeView(),
ui::TransferTouchesBehavior::kDontCancel);
if (!ref) {
return Liveness::kDeleted;
}
#endif
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.