Medium chrome Type Confusion 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType Confusion in Tab
DescriptionType Confusion in Tab
ComponentTab
Bug ClassType Confusion
Tracker520525732
Fix commit1ed90ec8ff93 (chromium/src) +2/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Files Changed

  • chrome/browser/android/tab_android.cc
From 1ed90ec8ff93b273e9785c69e927f0ea5dfa1d34 Mon Sep 17 00:00:00 2001
From: Calder Kitagawa <ckitagawa@chromium.org>
Date: Tue, 16 Jun 2026 09:13:17 -0700
Subject: [PATCH] [Tab] Use ToTabAndroidOrNull for FromTabHandle

Ensure valid conversion even if the TabHandle/TabInterface is for a
TabInterfaceAndroid.

Fixed: 520525732
Change-Id: I80f3d00713afd594956f9d50671a1029a689a519
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7951099
Auto-Submit: Calder Kitagawa <ckitagawa@chromium.org>
Reviewed-by: Fiaz Muhammad <mfiaz@google.com>
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Commit-Queue: Fiaz Muhammad <mfiaz@google.com>
Cr-Commit-Position: refs/heads/main@{#1647630}
---

diff --git a/chrome/browser/android/tab_android.cc b/chrome/browser/android/tab_android.cc
index 60a4f29..3107b9e 100644
--- a/chrome/browser/android/tab_android.cc
+++ b/chrome/browser/android/tab_android.cc
@@ -27,6 +27,7 @@
 #include "chrome/browser/android/compositor/tab_content_manager.h"
 #include "chrome/browser/android/media_state_observer.h"
 #include "chrome/browser/android/selection/chrome_selection_dropdown_menu_delegate.h"
+#include "chrome/browser/android/tab_android_conversions.h"
 #include "chrome/browser/android/tab_features.h"
 #include "chrome/browser/android/tab_web_contents_delegate_android.h"
 #include "chrome/browser/android/web_contents_theme_client.h"
@@ -127,7 +128,7 @@
 
 // static
 TabAndroid* TabAndroid::FromTabHandle(tabs::TabHandle handle) {
-  return static_cast<TabAndroid*>(handle.Get());
+  return tabs::ToTabAndroidOrNull(handle.Get());
 }
 
 // static
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Browser-Process Type Confusion in TabAndroid::FromTabHandle via Glic Actor

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 class-level type confusion vulnerability exists in the Android implementation of Glic Actor. The method TabAndroid::FromTabHandle performs an unsafe static downcast of a base pointer to TabAndroid* without type validation. This can allow a compromised guest renderer to inject a tab handle corresponding to TabInterfaceAndroid, leading to virtual method table corruption and potential arbitrary code execution in the browser process.

Affected files:

  • chrome/browser/android/tab_android.cc
  • chrome/browser/android/tab_android.h
  • chrome/browser/actor/android/actor_task_android.cc

Estimated timestamp from git blame: 2026-03-18

Abstract

There is a potential class-level type confusion vulnerability in TabAndroid::FromTabHandle on Android. The method performs an unsafe static downcast of a tabs::TabInterface* to TabAndroid*. However, in the Android tab collection model, tabs are often wrapped inside TabInterfaceAndroid instances (which inherit from tabs::TabInterface but are not subclasses of TabAndroid).

Because both concrete classes are stored in a sequential integer-backed handle database (SessionMappedTabHandleFactory), an attacker who can inject a raw handle integer corresponding to a TabInterfaceAndroid instance into a context expecting TabAndroid can trigger type confusion. If virtual methods are subsequently invoked on the type-confused pointer, it can lead to virtual table corruption and potential Remote Code Execution (RCE) in the browser process.

Vulnerability Analysis & Memory Layout

1. Sibling Class Relationship

TabAndroid and TabInterfaceAndroid are sibling classes that share a common base class, tabs::TabInterface, but do not inherit from each other:

  • TabAndroid (chrome/browser/android/tab_android.h lines 64-66):

    class TabAndroid : public tabs::TabInterface,
                       public TabAndroidDataProvider,
                       public base::SupportsUserData
    
  • TabInterfaceAndroid (chrome/browser/android/tab_interface_android.h line 20):

    class TabInterfaceAndroid : public tabs::TabInterface
    

2. Unsafe Static Downcast

In chrome/browser/android/tab_android.cc (lines 129-131), the method FromTabHandle is implemented as follows:

TabAndroid* TabAndroid::FromTabHandle(tabs::TabHandle handle) {
  return static_cast<TabAndroid*>(handle.Get());
}

Here, handle.Get() retrieves a tabs::TabInterface* from SessionMappedTabHandleFactory. Performing a static_cast<TabAndroid*> on a pointer that actually points to a TabInterfaceAndroid instance is invalid downcasting and results in type confusion.

3. Memory Effect and Offset Misalignment

TabAndroid utilizes multiple inheritance. When virtual methods declared on its second base class, TabAndroidDataProvider (such as GetAndroidId()), are called, the compiler applies a static “this-adjustment” offset to the pointer.

When this offset is applied to a type-confused TabInterfaceAndroid pointer, the offset lands inside other internal members of TabInterfaceAndroid (for example, ui::UnownedUserDataHost unowned_user_data_host_ at tab_interface_android.h line 84). De-referencing the resulting pointer to locate the virtual method table (vtable) fetches arbitrary heap data, resulting in a wild indirect call and control-flow hijacking inside the high-privilege Browser process.

Potential Injection Path

The Glic guest webview runs remote untrusted content in a separate sandboxed renderer process. A compromised guest renderer can interact with the trusted WebUI layer (chrome://glic) via the glicBrowser JS API:

  1. WebUI Client Forwarding: The WebUI JavaScript helper at chrome/browser/resources/glic/glic_api_impl/host/host_from_client.ts forwards guest-supplied tabId integers verbatim to the browser process via Mojo IPC interfaces.

  2. Mojo Ingress: GlicActorClientSession::PauseActorTask (chrome/browser/glic/actor/glic_actor_task_manager.cc lines 667-670) consumes the raw integer from Mojo and constructs a tabs::TabInterface::Handle:

    tabs::TabInterface::Handle handle;
    if (tab_handle.has_value()) {
      handle = tabs::TabInterface::Handle(*tab_handle);
    }
    
  3. Task Registration: The task manager registers the handle into the active task’s controlled tab list via task->AddTab(handle, ...) (chrome/browser/glic/actor/glic_actor_task_manager.cc line 690). The lookup is successful since TabInterfaceAndroid is registered under this handle in the factory.

  4. JNI Query Sink: When an Android system event (such as entering Picture-in-Picture or bringing a tab to front) occurs, the Java layer queries the native task for its tab list via ActorTaskAndroid::GetLastActedTabs (chrome/browser/actor/android/actor_task_android.cc lines 96-105):

    for (const auto& handle : tab_handles) {
      if (auto* tab_android = TabAndroid::FromTabHandle(handle)) {
        tab_ids.push_back(tab_android->GetAndroidId());
      }
    }
    

    The loop invokes TabAndroid::FromTabHandle(handle) which executes the type-confused cast, then calls tab_android->GetAndroidId(), triggering the wild indirect call.

Potential Steps to Trigger (Suggested/Theoretical)

Note: These are potential steps, as our tooling does not have the ability to run code or verify execution behavior on a live device.

  1. Launch Chromium on Android with --enable-features=GlicActor (or when the Glic Actor feature is active).
  2. Compromise the sandboxed Glic guest renderer running remote content.
  3. From the guest context, post a message to the WebUI to invoke pauseActorTask with an arbitrary integer handle corresponding to a live TabInterfaceAndroid wrapper (handles are sequential int32_t values).
  4. The WebUI forwards the handle to the browser process, registering the TabInterfaceAndroid handle into controlled_tabs_.
  5. Trigger an Android system event (e.g., toggling Picture-in-Picture) to force a JNI transition to ActorTaskAndroid::GetLastActedTabs, executing the virtual method call tab_android->GetAndroidId() on the confused pointer.

Mitigating Factors

  • Feature Gating: The Glic Actor functionality (features::kGlicActor) is currently FEATURE_DISABLED_BY_DEFAULT on Android (chrome/common/chrome_features.cc line 192).

Suggested Fix

To remediate this issue, direct static downcasts of tabs::TabInterface* to TabAndroid* should be prohibited. Instead, the safe conversion helpers defined in chrome/browser/android/tab_android_conversions.h must be used.

Modify TabAndroid::FromTabHandle inside chrome/browser/android/tab_android.cc to utilize tabs::ToTabAndroidOrNull:

// static
TabAndroid* TabAndroid::FromTabHandle(tabs::TabHandle handle) {
  return tabs::ToTabAndroidOrNull(handle.Get());
}

Evaluated with Chrome root at commit: e9507a33bb4148ee071aaaf8a7e9ad68770359bf


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