Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Chrome for iOS
DescriptionUse after free in Chrome for iOS
ComponentChrome for iOS
Bug ClassUAF
Tracker508275293
Fix commit820b6b6d5544 (chromium/src) +15/-9
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
if
ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
modified
TabGroup
ios/chrome/browser/share_kit/model/share_kit_manage_configuration.h
modified
TabGroup
ios/chrome/browser/share_kit/model/share_kit_share_group_configuration.h
modified
if
ios/chrome/browser/share_kit/model/test_share_kit_service.mm
modified

Files Changed

  • ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h
  • ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
  • ios/chrome/browser/share_kit/model/share_kit_manage_configuration.h
  • ios/chrome/browser/share_kit/model/share_kit_share_group_configuration.h
  • ios/chrome/browser/share_kit/model/test_share_kit_service.mm
From 820b6b6d5544a732ac00d19c10b9184fa573f9b2 Mon Sep 17 00:00:00 2001
From: Matt Reichhoff <[email protected]>
Date: Wed, 06 May 2026 14:11:19 -0700
Subject: [PATCH] [iOS] Use WeakPtr for tab groups with ShareKit

Bug: 508275293
Change-Id: If0bde857b55a3815aa08ef44885c970d403be156
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7807110
Commit-Queue: Matt Reichhoff <[email protected]>
Reviewed-by: Andrew Liu <[email protected]>
Reviewed-by: Rohit Rao <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1626456}
---

diff --git a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h
index 5007beb7..c798ae6 100644
--- a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h
+++ b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.h
@@ -190,13 +190,13 @@
   // Configures the shareKit config for the share flow and starts the flow.
   void ConfigureAndShareTabGroup(const tab_groups::EitherGroupID& either_id,
                                  ResultWithGroupTokenCallback result,
-                                 const TabGroup* tab_group,
+                                 base::WeakPtr<const TabGroup> tab_group,
                                  UIImage* faviconsGridImage);
 
   // Configures the shareKit config for the manage flow and starts the flow.
   void ConfigureAndManageTabGroup(const tab_groups::EitherGroupID& either_id,
                                   ResultCallback result,
-                                  const TabGroup* tab_group,
+                                  base::WeakPtr<const TabGroup> tab_group,
                                   UIImage* faviconsGridImage);
 
   // Returns the join group image displayed in the join flow.
diff --git a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
index 8ae4021..6895b2fa 100644
--- a/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
+++ b/ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
@@ -385,7 +385,8 @@
 
   auto callback = base::BindOnce(
       &IOSCollaborationControllerDelegate::ConfigureAndShareTabGroup,
-      weak_ptr_factory_.GetWeakPtr(), either_id, std::move(result), tab_group);
+      weak_ptr_factory_.GetWeakPtr(), either_id, std::move(result),
+      tab_group->GetWeakPtr());
 
   favicons_grid_configurator_->FetchFaviconsGrid(tab_group,
                                                  std::move(callback));
@@ -419,7 +420,8 @@
 
   auto callback = base::BindOnce(
       &IOSCollaborationControllerDelegate::ConfigureAndManageTabGroup,
-      weak_ptr_factory_.GetWeakPtr(), either_id, std::move(result), tab_group);
+      weak_ptr_factory_.GetWeakPtr(), either_id, std::move(result),
+      tab_group->GetWeakPtr());
 
   favicons_grid_configurator_->FetchFaviconsGrid(tab_group,
                                                  std::move(callback));
@@ -785,7 +787,7 @@
 void IOSCollaborationControllerDelegate::ConfigureAndShareTabGroup(
     const tab_groups::EitherGroupID& either_id,
     ResultWithGroupTokenCallback result,
-    const TabGroup* tab_group,
+    base::WeakPtr<const TabGroup> tab_group,
     UIImage* faviconsGridImage) {
   if (!tab_group || !faviconsGridImage) {
     std::move(result).Run(CollaborationControllerDelegate::Outcome::kFailure,
@@ -817,7 +819,7 @@
 void IOSCollaborationControllerDelegate::ConfigureAndManageTabGroup(
     const tab_groups::EitherGroupID& either_id,
     ResultCallback result,
-    const TabGroup* tab_group,
+    base::WeakPtr<const TabGroup> tab_group,
     UIImage* faviconsGridImage) {
   if (!tab_group || !faviconsGridImage) {
     std::move(result).Run(CollaborationControllerDelegate::Outcome::kFailure);
diff --git a/ios/chrome/browser/share_kit/model/share_kit_manage_configuration.h b/ios/chrome/browser/share_kit/model/share_kit_manage_configuration.h
index b115676..f455302 100644
--- a/ios/chrome/browser/share_kit/model/share_kit_manage_configuration.h
+++ b/ios/chrome/browser/share_kit/model/share_kit_manage_configuration.h
@@ -7,6 +7,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import "base/memory/weak_ptr.h"
+
 @protocol SceneCommands;
 enum class ShareKitFlowOutcome;
 class TabGroup;
@@ -23,7 +25,7 @@
 @property(nonatomic, copy) NSString* collabID;
 
 // Local tab group.
-@property(nonatomic, assign) const TabGroup* tabGroup;
+@property(nonatomic, assign) base::WeakPtr<const TabGroup> tabGroup;
 
 // The group image preview.
 @property(nonatomic, copy) UIImage* groupImage;
diff --git a/ios/chrome/browser/share_kit/model/share_kit_share_group_configuration.h b/ios/chrome/browser/share_kit/model/share_kit_share_group_configuration.h
index e7204ea..20dcf842 100644
--- a/ios/chrome/browser/share_kit/model/share_kit_share_group_configuration.h
+++ b/ios/chrome/browser/share_kit/model/share_kit_share_group_configuration.h
@@ -7,6 +7,8 @@
 
 #import <UIKit/UIKit.h>
 
+#import "base/memory/weak_ptr.h"
+
 @protocol SceneCommands;
 enum class ShareKitFlowOutcome;
 class TabGroup;
@@ -18,7 +20,7 @@
 @property(nonatomic, weak) UIViewController* baseViewController;
 
 // Local tab group.
-@property(nonatomic, assign) const TabGroup* tabGroup;
+@property(nonatomic, assign) base::WeakPtr<const TabGroup> tabGroup;
 
 // The group image preview.
 @property(nonatomic, copy) UIImage* groupImage;
diff --git a/ios/chrome/browser/share_kit/model/test_share_kit_service.mm b/ios/chrome/browser/share_kit/model/test_share_kit_service.mm
index 3d40b8a..ad4a5dc 100644
--- a/ios/chrome/browser/share_kit/model/test_share_kit_service.mm
+++ b/ios/chrome/browser/share_kit/model/test_share_kit_service.mm
@@ -141,7 +141,7 @@
 
 NSString* TestShareKitService::ShareTabGroup(
     ShareKitShareGroupConfiguration* config) {
-  const TabGroup* tab_group = config.tabGroup;
+  const TabGroup* tab_group = config.tabGroup.get();
   if (!tab_group) {
     return nil;
   }
Loading diff…

Original Bug Report

reported by [email protected]

Potential UAF in ShareKit via TabGroup pointer bypassing MiraclePtr

Flapjack, 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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A Use-After-Free (UAF) vulnerability exists in the iOS collaboration flow when a TabGroup is closed during an asynchronous favicon fetch. A dangling pointer is assigned to an Objective-C configuration object without raw_ptr protection, bypassing MiraclePtr and leading to a potential UAF when accessed by the ShareKit service.

Affected files:

  • ios/chrome/browser/collaboration/model/ios_collaboration_controller_delegate.mm
  • ios/chrome/browser/share_kit/model/share_kit_share_group_configuration.h
  • ios/chrome/browser/share_kit/model/share_kit_manage_configuration.h

Estimated timestamp from git blame: 2025-02-28

Technical Details

A potential Use-After-Free (UAF) vulnerability has been identified in the iOS collaboration and sharing flow. The issue occurs when the TabGroup backing a share operation is destroyed while an asynchronous visual data fetch is pending, leading to a MiraclePtr bypass.

When a user initiates sharing for a tab group, IOSCollaborationControllerDelegate::ShowShareDialog is called. This method retrieves the local TabGroup* and initiates an asynchronous fetch of the group’s favicons via favicons_grid_configurator_->FetchFaviconsGrid. A callback is created using base::BindOnce to handle the completion of this fetch:

  auto callback = base::BindOnce(
      &IOSCollaborationControllerDelegate::ConfigureAndShareTabGroup,
      weak_ptr_factory_.GetWeakPtr(), either_id, std::move(result), tab_group);

The FetchFaviconsGrid operation can take up to 5 seconds (enforced by a dispatch_after timeout), during which the UI is not blocked. If the tab group is deleted during this window (e.g., via the user closing it in the UI, or a remote sync deletion event), the C++ TabGroup object is destroyed immediately.

While the TabGroup is destroyed, the memory is temporarily protected (quarantined) by MiraclePtr because the base::BindOnce state holds an internal raw_ptr to it. However, the cancellation flow triggered by the group’s deletion is flawed. IOSCollaborationControllerDelegate::Cancel attempts to cancel the downstream UI by passing session_id_ to ShareKitService. Because the flow is still pending the favicon fetch, session_id_ is nil, meaning the operation is not properly aborted.

When the favicon fetch completes (or the 5-second timeout fires), the callback executes ConfigureAndShareTabGroup. It takes the quarantined tab_group pointer and assigns it to a ShareKitShareGroupConfiguration object:

@property(nonatomic, assign) const TabGroup* tabGroup;

Because this property uses the Objective-C assign semantic, the pointer is stored as a raw, unprotected memory address. The configuration object is then passed to the downstream ShareKitService.

Once the ConfigureAndShareTabGroup method finishes, the base::BindOnce callback state is destroyed. The internal raw_ptr goes out of scope, the MiraclePtr reference count drops to zero, and the TabGroup memory is completely unquarantined and freed to the system allocator.

The ShareKitService now holds a raw pointer to fully freed memory. If it accesses config.tabGroup asynchronously to extract metadata for the UI, a Use-After-Free occurs in the highly-privileged browser process.

Potential Exploitation Steps (Theoretical)

Note: These are suggested steps based on static analysis; a working Proof of Concept has not been executed.

  1. An attacker initiates the tab group sharing flow, triggering the 5-second asynchronous favicon fetch.
  2. Immediately, the attacker triggers the deletion of the tab group (e.g., via an incoming remote sync update, or programmatically closing the group).
  3. The TabGroup C++ object is destroyed, but its memory is temporarily held in quarantine by the pending callback’s BindState.
  4. The callback executes, assigning the raw pointer to the Objective-C ShareKitShareGroupConfiguration object.
  5. The callback finishes, destroying the BindState and releasing the memory from quarantine.
  6. The attacker heavily allocates memory in the browser process to reclaim the exact memory chunk previously used by the TabGroup with attacker-controlled data.
  7. The downstream ShareKitService asynchronously accesses the tabGroup pointer, dereferencing the attacker-controlled memory and triggering the UAF.

Suggested Remediation

  1. Update ShareKitShareGroupConfiguration and ShareKitManageConfiguration to store the tab group as a base::WeakPtr<const TabGroup> instead of a raw assign pointer. Provide a C++ setter/getter if necessary to bridge between Objective-C and the C++ WeakPtr.
  2. In IOSCollaborationControllerDelegate::ConfigureAndShareTabGroup (and ConfigureAndManageTabGroup), pass the TabGroup as a base::WeakPtr and verify its validity (if (!tab_group) return;) before attempting to construct the configuration object and initiate the downstream ShareKit session.

Evaluated with Chrome root at commit: cc901875d53bf4e4fe0e01f02843871da4106e70


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