Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in TabStrip
DescriptionUse after free in TabStrip
ComponentTabStrip
Bug ClassUAF
Tracker523717010
Fix commit0d6c4bb20548 (chromium/src) +6/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.cc
modified

Files Changed

  • chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.cc
From 0d6c4bb20548c3d95893cc8242eb6d538bef64d2 Mon Sep 17 00:00:00 2001
From: Kunal Daftari <kunaldaftari@google.com>
Date: Mon, 15 Jun 2026 11:47:19 -0700
Subject: [PATCH] [Vertical Tabs] Adding liveness check for OnGestureEvent

In VerticalTabDragHandlerImpl, it is possible that the browser window
could be destroyed while blocked inside RunMenuAt. In this case, EndDrag
could run on a freed object, so we add a safety check after calling
OnGestureEvent.

Bug: 523717010
Change-Id: I35d652d22f5ab19c7385fff4a710d93bf045ae81
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7940976
Reviewed-by: Kaan Alsan <alsan@chromium.org>
Commit-Queue: Kunal Daftari <kunaldaftari@google.com>
Cr-Commit-Position: refs/heads/main@{#1646970}
---

diff --git a/chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.cc b/chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.cc
index 7480f105..de5d483 100644
--- a/chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.cc
+++ b/chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.cc
@@ -564,7 +564,13 @@
         views::View* source_view = source_node->view();
         ui::GestureEvent converted_event(*event, static_cast<View*>(this),
                                          source_view);
+
+        // OnGestureEvent could start a blocking loop, so this may be destroyed.
+        auto ref = weak_factory_.GetWeakPtr();
         source_view->OnGestureEvent(&converted_event);
+        if (!ref) {
+          return;
+        }
       }
 
       EndDrag(EndDragReason::kCancel);
Loading diff…

Original Bug Report

reported by rj...@google.com

Potential Use-After-Free in VerticalTabDragHandlerImpl::OnGestureEvent

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 vulnerability exists in the browser process when handling gesture events on vertical tabs. A nested event loop used to display a context menu can allow the destruction of the drag handler object. Returning from the loop and calling a virtual method on the freed object could lead to remote code execution.

Affected files:

  • chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.cc

Estimated timestamp from git blame: Unknown (Google3 checkout)

Summary

A potential Use-After-Free (UAF) vulnerability exists in VerticalTabDragHandlerImpl::OnGestureEvent within the browser process. When handling a kGestureLongTap event, the code forwards the gesture to a child view, which can trigger a synchronous context menu and a nested event loop. If the browser window is closed during this loop, the VerticalTabDragHandlerImpl object is destroyed. Upon returning from the loop, the code calls a virtual method on the freed object, resulting in a UAF.

Vulnerability Details

In chrome/browser/ui/views/tabs/vertical/vertical_tab_drag_handler.cc, VerticalTabDragHandlerImpl::OnGestureEvent handles kGestureLongTap by forwarding the event to the source tab view:

563:       if (source_node && source_node->view()) {
564:         views::View* source_view = source_node->view();
565:         ui::GestureEvent converted_event(*event, static_cast<View*>(this),
566:                                          source_view);
567:         source_view->OnGestureEvent(&converted_event);
568:       }
569: 
570:       EndDrag(EndDragReason::kCancel);

The call at line 567 routes into VerticalTabView::OnGestureEvent, which initiates VerticalTabStripController::ShowContextMenuForNode. This method eventually calls RunMenuAt, which spins a nested event loop to wait for user interaction with the context menu.

If the browser window is closed while blocked in this nested event loop (e.g., via a malicious script calling window.close()), the BrowserView hierarchy is torn down. Because VerticalTabDragHandlerImpl inherits from views::View and is a child of the tab strip hierarchy, it is destroyed and freed.

Once the context menu is dismissed (due to the window closing), the nested loop terminates, and control unwinds back to line 569. At line 570, the code calls EndDrag(EndDragReason::kCancel). EndDrag is a virtual method. Executing this call on the freed this pointer results in a UAF, allowing an attacker who controls the heap to potentially achieve Remote Code Execution (RCE) and a sandbox escape.

Note: A similar risk may exist at line 579 where ContinueDrag is called. While ContinueDrag has internal liveness checks, OnGestureEvent proceeds to access members or call methods without verifying if this survived.

Potential Attack Scenario

Please note: These are suggested steps based on static analysis; our tooling does not currently have the ability to run code or construct a working exploit.

  1. An attacker hosts a malicious page that opens a new popup window.
  2. The attacker uses social engineering or UI redressing to trick the user into long-tapping a vertical tab in the popup window.
  3. The long tap triggers the context menu, spinning up the nested event loop in the browser process.
  4. A pre-scheduled JavaScript timer on the attacker’s page executes window.close() on the popup window, freeing the VerticalTabDragHandlerImpl.
  5. The attacker’s script concurrently sprays the heap to overwrite the freed object’s vtable pointer.
  6. The nested loop unwinds, and the browser process executes the virtual EndDrag method on the freed object, jumping to the attacker’s payload.

Suggested Fix

Implement a base::WeakPtr liveness check in VerticalTabDragHandlerImpl::OnGestureEvent. Before calling source_view->OnGestureEvent or ContinueDrag, acquire a WeakPtr to this. After the call returns, check if the WeakPtr is still valid, and return early if the object has been destroyed.

base::WeakPtr<VerticalTabDragHandlerImpl> weak_this = weak_factory_.GetWeakPtr();
source_view->OnGestureEvent(&converted_event);
if (!weak_this) return;

Evaluated with Chrome root at commit: 65b3256311f3ab6fb9870eaa522de7e6dd2663bb


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.

View on issue tracker