High chrome Type Confusion 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType Confusion in Chrome Tabs
DescriptionType Confusion in Chrome Tabs
ComponentChrome Tabs
Bug ClassType Confusion
Tracker501669642
Fix commit9c6f841378a6 (chromium/src) +20/-18
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc
From 9c6f841378a62932321cc4ec4b5d6485f719819d Mon Sep 17 00:00:00 2001
From: Yuheng Huang <[email protected]>
Date: Tue, 05 May 2026 16:06:28 -0700
Subject: [PATCH] Replace static_cast with GetAs for DataSharingUI

Bug: 501669642
Change-Id: I8171b499247ac68fdaece046c156675b3b8bc99b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7817259
Reviewed-by: Shakti Sahu <[email protected]>
Commit-Queue: Yuheng Huang <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1625787}
---

diff --git a/chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc b/chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc
index 5ff23de..511dcf54 100644
--- a/chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc
+++ b/chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc
@@ -42,10 +42,10 @@
       [](data_sharing_pb::ReadGroupsParams params, ReadGroupsCallback callback,
          DataSharingSDKDelegateDesktop* delegate,
          content::WebContents* web_contents) {
-        DataSharingPageHandler* handler =
-            static_cast<DataSharingUI*>(
-                web_contents->GetWebUI()->GetController())
-                ->page_handler();
+        DataSharingPageHandler* handler = web_contents->GetWebUI()
+                                              ->GetController()
+                                              ->GetAs<DataSharingUI>()
+                                              ->page_handler();
         CHECK(handler);
         auto mojom_params = data_sharing::mojom::ReadGroupsParams::New();
         for (const auto& group_param : params.group_params()) {
@@ -82,10 +82,10 @@
          base::OnceCallback<void(const absl::Status&)> callback,
          DataSharingSDKDelegateDesktop* delegate,
          content::WebContents* web_contents) {
-        DataSharingPageHandler* handler =
-            static_cast<DataSharingUI*>(
-                web_contents->GetWebUI()->GetController())
-                ->page_handler();
+        DataSharingPageHandler* handler = web_contents->GetWebUI()
+                                              ->GetController()
+                                              ->GetAs<DataSharingUI>()
+                                              ->page_handler();
         CHECK(handler);
         handler->LeaveGroup(
             params.group_id(),
@@ -103,10 +103,10 @@
          base::OnceCallback<void(const absl::Status&)> callback,
          DataSharingSDKDelegateDesktop* delegate,
          content::WebContents* web_contents) {
-        DataSharingPageHandler* handler =
-            static_cast<DataSharingUI*>(
-                web_contents->GetWebUI()->GetController())
-                ->page_handler();
+        DataSharingPageHandler* handler = web_contents->GetWebUI()
+                                              ->GetController()
+                                              ->GetAs<DataSharingUI>()
+                                              ->page_handler();
         CHECK(handler);
         handler->DeleteGroup(
             params.group_id(),
@@ -151,7 +151,8 @@
   // If the API is already initialized, run the callback here, otherwise add the
   // callback to the queue and run it when `ApiInitComplete` is called.
   DataSharingUI* data_sharing_ui =
-      static_cast<DataSharingUI*>(web_contents_->GetWebUI()->GetController());
+      web_contents_->GetWebUI()->GetController()->GetAs<DataSharingUI>();
+  CHECK(data_sharing_ui);
   if (data_sharing_ui->IsApiInitialized()) {
     std::move(callback).Run(web_contents_.get());
   } else {
@@ -168,7 +169,8 @@
   // At this point the page handler should be created.
   // Invoke the callbacks and clear the subscriptions.
   DataSharingUI* data_sharing_ui =
-      static_cast<DataSharingUI*>(web_contents_->GetWebUI()->GetController());
+      web_contents_->GetWebUI()->GetController()->GetAs<DataSharingUI>();
+  CHECK(data_sharing_ui);
   data_sharing_ui->SetDelegate(nullptr);
   CHECK(data_sharing_ui->page_handler());
   callbacks_.Notify(web_contents_.get());
@@ -238,10 +240,10 @@
          ReadGroupWithTokenCallback callback,
          DataSharingSDKDelegateDesktop* delegate,
          content::WebContents* web_contents) {
-        DataSharingPageHandler* handler =
-            static_cast<DataSharingUI*>(
-                web_contents->GetWebUI()->GetController())
-                ->page_handler();
+        DataSharingPageHandler* handler = web_contents->GetWebUI()
+                                              ->GetController()
+                                              ->GetAs<DataSharingUI>()
+                                              ->page_handler();
         CHECK(handler);
         auto mojom_param = data_sharing::mojom::ReadGroupWithTokenParam::New();
         mojom_param->group_id = params.group_id();
Loading diff…

Original Bug Report

reported by [email protected]

Browser Process Type Confusion in DataSharingSDKDelegateDesktop

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 Chrome Security team.

Overview: DataSharingSDKDelegateDesktop performs an unchecked static_cast to DataSharingUI on a WebUIController. A compromised chrome-untrusted renderer can navigate this WebContents to another WebUI, changing the controller type and leading to out-of-bounds memory access in the browser process.

Affected files:

  • chrome/browser/data_sharing/desktop/data_sharing_sdk_delegate_desktop.cc
  • chrome/browser/ui/webui/data_sharing/data_sharing_ui.h

Estimated timestamp from git blame: 2024-11-12

Summary

A potential type confusion vulnerability exists in the browser process due to unchecked static_cast operations in DataSharingSDKDelegateDesktop. This class manages a hidden WebContents to host chrome-untrusted://data-sharing. If a compromised renderer navigates this WebContents to a different chrome-untrusted:// host, the WebUIController changes to a different subclass. Subsequent SDK calls perform an invalid static_cast, leading to spatial out-of-bounds (OOB) memory corruption and potentially a sandbox escape.

Vulnerability Details

DataSharingSDKDelegateDesktop creates a hidden WebContents and loads chrome-untrusted://data-sharing. It retrieves the WebUIController from this WebContents and casts it to DataSharingUI* using a raw static_cast (e.g., in MaybeLoadWebContents).

In Chromium’s security model, chrome-untrusted:// renderers host untrusted content and are assumed compromisable. Once compromised, the data-sharing renderer can initiate a top-level cross-origin navigation to another untrusted WebUI, such as chrome-untrusted://print/.

Because the process was previously granted request permissions for the entire chrome-untrusted scheme upon the initial commit, ChildProcessSecurityPolicyImpl::CanRequestURL allows this navigation. The cross-origin navigation triggers a process swap, and the new process commits the chrome-untrusted://print/ URL.

Consequently, the WebContents’s WebUIController is replaced with an instance of PrintPreviewUIUntrusted. On a 64-bit architecture, PrintPreviewUIUntrusted is only 16 bytes in size, whereas DataSharingUI is approximately 112 bytes.

DataSharingSDKDelegateDesktop maintains a 1-minute keep-alive for the WebContents. If another SDK call is triggered (e.g., by another click on a tabshare URL) within this window, MaybeLoadWebContents re-executes. Since web_contents_ is non-null, it performs the invalid static_cast on the new controller and calls data_sharing_ui->IsApiInitialized().

Impact

The type confusion leads to spatial OOB memory access on the browser’s PartitionAlloc heap:

  1. OOB Read: IsApiInitialized() reads page_handler_ from an offset of approximately 48 bytes, which is past the 16-byte bounds of the object.
  2. OOB Write: If the OOB read yields null (e.g., through heap grooming), the code executes data_sharing_ui->SetDelegate(this). This writes a valid DataSharingSDKDelegateDesktop* pointer to the delegate_ member at an offset of approximately 104 bytes.

This OOB write corrupts adjacent heap memory. Because the corruption is spatial and targets a live object (rather than a Use-After-Free), MiraclePtr (BRP) may not mitigate the issue if the attacker grooms the overwritten memory to avoid invalid refcount decrements. This can be leveraged for arbitrary code execution in the browser process.

Potential Reproduction Steps

(Note: These are suggested steps; our tooling has not executed a working proof-of-concept.)

  1. From a compromised chrome-untrusted://data-sharing renderer (created via a data sharing action), execute window.location.href = 'chrome-untrusted://print/';.
  2. The browser permits the navigation and replaces the WebUIController with PrintPreviewUIUntrusted.
  3. Within 60 seconds, trigger a second SDK call (e.g., another tabshare link click).
  4. The browser process performs the invalid static_cast and attempts to write to the delegate_ field, corrupting adjacent heap memory.

Suggested Fix

Avoid using raw static_cast on WebUIController objects. Since DataSharingUI defines WEB_UI_CONTROLLER_TYPE_DECL(), you can safely downcast using the GetAs method:

DataSharingUI* data_sharing_ui = 
    web_contents_->GetWebUI()->GetController()->GetAs<DataSharingUI>();
if (!data_sharing_ui) {
  // Handle error: the WebContents has navigated away.
  return;
}

Alternatively, consider adding a WebContentsObserver to DataSharingSDKDelegateDesktop to nullify web_contents_ if it navigates away from chrome-untrusted://data-sharing or if the WebContents is destroyed.

Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b


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