Overview

Critical
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Bluetooth
DescriptionUse after free in Bluetooth
ComponentBluetooth
Bug ClassUAF
Tracker516987814
Fix commit75b964de97d2 (chromium/src) +23/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-08

Changed Functions

FunctionChangeNotes
if
device/bluetooth/bluetooth_adapter_mac.mm
modified

Files Changed

  • device/bluetooth/bluetooth_adapter_mac.mm
  • device/bluetooth/bluetooth_classic_device_mac.mm
From 75b964de97d2781d1f8329f2fb754862dc212c63 Mon Sep 17 00:00:00 2001
From: Alvin Ji <[email protected]>
Date: Fri, 29 May 2026 13:59:07 -0700
Subject: [PATCH] bluetooth: Prevent UAF in macOS Bluetooth listeners

Implement the FB13705522 workaround for BluetoothDevicesConnectListener
and BluetoothDeviceDisconnectListener on macOS. This ensures that these
listeners stay alive past any already-enqueued notifications on the main
run loop, preventing Use-After-Free (UAF) when the C++ owner is
destroyed.

Bug: 516987814
Change-Id: I542200f213f499370dc89a7cf68dde9fc6299285
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7884382
Reviewed-by: Matt Reynolds <[email protected]>
Commit-Queue: Alvin Ji <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1638719}
---

diff --git a/device/bluetooth/bluetooth_adapter_mac.mm b/device/bluetooth/bluetooth_adapter_mac.mm
index f300e8d3..b71deca 100644
--- a/device/bluetooth/bluetooth_adapter_mac.mm
+++ b/device/bluetooth/bluetooth_adapter_mac.mm
@@ -100,6 +100,9 @@
 
 - (void)deviceConnected:(IOBluetoothUserNotification*)notification
                  device:(IOBluetoothDevice*)device {
+  if (!_ui_task_runner) {
+    return;
+  }
   _ui_task_runner->PostTask(
       FROM_HERE,
       base::BindOnce(&device::BluetoothAdapterMac::OnConnectNotification,
@@ -108,6 +111,17 @@
 
 - (void)stopListening {
   [_connectNotification unregister];
+  _ui_task_runner = nullptr;
+  _adapter = nullptr;
+
+  // Keep self alive for a brief period to allow any already-enqueued
+  // notifications on the main run loop to fire safely (and become no-ops
+  // since _ui_task_runner is now null) rather than hitting a deallocated
+  // object. See FB13705522.
+  __strong auto strongSelf = self;
+  dispatch_async(dispatch_get_main_queue(), ^{
+    (void)strongSelf;
+  });
 }
 
 @end
diff --git a/device/bluetooth/bluetooth_classic_device_mac.mm b/device/bluetooth/bluetooth_classic_device_mac.mm
index 659328c..99544514 100644
--- a/device/bluetooth/bluetooth_classic_device_mac.mm
+++ b/device/bluetooth/bluetooth_classic_device_mac.mm
@@ -87,6 +87,15 @@
   // destruction will see a null |_device| and become a no-op instead of
   // dereferencing a freed object.
   _device = nullptr;
+
+  // Keep self alive for a brief period to allow any already-enqueued
+  // notifications on the main run loop to fire safely (and become no-ops
+  // since _device is now null) rather than hitting a deallocated object.
+  // See FB13705522.
+  __strong auto strongSelf = self;
+  dispatch_async(dispatch_get_main_queue(), ^{
+    (void)strongSelf;
+  });
 }
 
 @end
Loading diff…

Original Bug Report

reported by [email protected]

Potential UAF in BluetoothDevicesConnectListener on macOS due to uncancelled enqueued notifications

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 macOS browser process because BluetoothDevicesConnectListener does not safely prolong its lifetime when stopListening is called. Due to a known macOS behavior where unregistering does not cancel already-enqueued run loop events, a late notification can execute on a deallocated listener. This could potentially allow an attacker to trigger code execution in the unsandboxed browser process.

Affected files:

  • device/bluetooth/bluetooth_adapter_mac.mm
  • device/bluetooth/bluetooth_adapter_mac.h

Estimated timestamp from git blame: 2024-06-07

Technical Details

In device/bluetooth/bluetooth_adapter_mac.mm, the helper class BluetoothDevicesConnectListener registers itself with the macOS IOBluetooth framework to receive device connection notifications. It is owned solely via a strong reference (connect_listener_) by BluetoothAdapterMac.

As documented in Apple Feedback report FB13705522 (and handled in sibling classes like BluetoothSocketMac), calling unregister on an IOBluetoothUserNotification successfully stops future notifications from being registered but does not cancel notifications that the system has already enqueued on the main run loop.

When BluetoothAdapterMac is destroyed, its destructor calls [connect_listener_ stopListening] and sets connect_listener_ = nil. This synchronously deallocates the BluetoothDevicesConnectListener instance. If a connection notification was already enqueued on the main run loop, the loop will subsequently attempt to deliver it to the deallocated listener address via objc_msgSend, causing a Use-After-Free (UAF) in the unsandboxed browser process.

Potential Trigger Path

Note: The following steps are theoretical and represent a potential attack vector, as our tooling cannot currently execute or run proof-of-concept code.

  1. Listener Registration: A web page in a sandboxed renderer requests a Web Bluetooth operation (e.g., navigator.bluetooth.requestDevice), which prompts the browser process to initialize BluetoothAdapterMac. This instantiates BluetoothDevicesConnectListener, registering it for connection notifications.
  2. Notification Enqueue: A proximate Bluetooth Classic device initiates a connection, causing IOBluetooth to queue a notification on the main run loop targeting the listener.
  3. Adapter Destruction: The renderer frame is closed or navigated, destroying WebBluetoothServiceImpl. This releases the last reference to the ref-counted BluetoothAdapterMac instance, synchronously executing its destructor.
  4. Listener Deallocation: ~BluetoothAdapterMac calls [connect_listener_ stopListening] and sets the pointer to nil, immediately reclaiming the listener’s memory.
  5. Use-After-Free: When the main run loop drains and processes the pending notification, it attempts to dispatch the event to the deallocated listener, resulting in a UAF.

Sibling Workaround Pattern

Other macOS Bluetooth classes in Chrome (such as BluetoothSocketMac in device/bluetooth/bluetooth_socket_mac.mm) proactively mitigate this OS-level limitation by clearing their back-pointers and asynchronously dispatching a block to the main queue that holds a strong reference to self to safely outlive any enqueued messages:

- (void)stopListening {
  [_rfcommNewChannelNotification unregister];
  _socket = nullptr;
  __strong auto strongSelf = self;
  dispatch_async(dispatch_get_main_queue(), ^{
    (void)strongSelf;
  });
}

Proposed Fix

Modify BluetoothDevicesConnectListener::stopListening in device/bluetooth/bluetooth_adapter_mac.mm to match this safety pattern:

- (void)stopListening {
  [_connectNotification unregister];
  _adapter.reset();
  __strong auto strongSelf = self;
  dispatch_async(dispatch_get_main_queue(), ^{
    (void)strongSelf;
  });
}

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.

View on issue tracker