Medium chrome Authorization Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in Bluetooth
DescriptionIncorrect authorization in Bluetooth
ComponentBluetooth
Sub-componentBluetooth
Bug ClassAuthorization Error
Tracker501533684
Fix commit327c13090601 (chromium/src) +12/-2418
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
CreateBluetoothAdapter
chromecast/browser/cast_content_browser_client.cc
modified
CrashHandlerHostLinux
chromecast/browser/cast_content_browser_client.h
modified
BluetoothAdapterCast
chromecast/browser/cast_content_browser_client.h
modified
CdmFactory
chromecast/browser/cast_content_browser_client.h
modified
weak_factory_
device/bluetooth/cast/bluetooth_adapter_cast.cc
modified

Files Changed

  • device/bluetooth/cast_bluetooth.gni
  • chromecast/browser/bluetooth/public/mojom/BUILD.gn
  • chromecast/browser/cast_browser_main_parts.cc
  • device/bluetooth/cast/OWNERS
  • device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc
  • device/bluetooth/BUILD.gn
  • device/bluetooth/cast/bluetooth_adapter_cast_unittest.cc
  • device/bluetooth/bluetooth_adapter_unittest.cc
  • device/bluetooth/cast/bluetooth_remote_gatt_service_cast.h
  • device/bluetooth/cast/bluetooth_adapter_cast.h
  • device/bluetooth/cast/DEPS
  • chromecast/browser/bluetooth/public/mojom/web_bluetooth.mojom
  • chromecast/browser/bluetooth/public/mojom/OWNERS
  • device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.h
  • device/bluetooth/cast/bluetooth_device_cast.h
  • device/bluetooth/bluetooth_device_unittest.cc
  • chromecast/browser/cast_content_browser_client.h
  • chromecast/browser/cast_content_browser_client.cc
  • device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
  • device/bluetooth/test/bluetooth_gatt_server_test.h
  • device/bluetooth/cast/bluetooth_utils.cc
  • device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
  • device/bluetooth/cast/bluetooth_utils_unittest.cc
  • chromecast/browser/BUILD.gn
  • device/bluetooth/test/bluetooth_test_cast.h
  • device/BUILD.gn
  • device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.h
  • device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
  • device/bluetooth/cast/bluetooth_adapter_cast.cc
  • device/bluetooth/test/bluetooth_test_cast.cc
  • device/bluetooth/cast/bluetooth_remote_gatt_service_cast.cc
  • device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
  • device/bluetooth/cast/bluetooth_utils.h
  • device/bluetooth/cast/bluetooth_device_cast.cc
From 327c1309060182dd57751d6f5edd22b183620775 Mon Sep 17 00:00:00 2001
From: Rob Pitkin <robpitkin@google.com>
Date: Fri, 11 Sep 2026 16:24:23 -0700
Subject: [PATCH] bluetooth: Remove Cast OS Web Bluetooth backend

This CL removes the obsolete Cast OS Web Bluetooth adapter and GATT
implementation under device/bluetooth/cast, as well as the unused
web_bluetooth.mojom interface in chromecast/browser/bluetooth.

Web Bluetooth on Cast OS is currently unused, and
CastContentBrowserClient::CreateBluetoothAdapter() is stubbed to fail
with NOTREACHED(). Instead of maintaining dead code and attempting to
patch latent GATT identifier collision vulnerabilities, this CL removes
the backend entirely. Cast OS builds now fall back to the standard no-op
platform adapter stub (bluetooth_adapter_stub.cc).

TAG=agy
CONV=73a20aa3-f94c-4a5b-b0fc-21ca8d566fe0

Fixed: b:501533684
Change-Id: Iff56b7ba1d8fcbce869a9f4296c6fb65c8c359a1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8334849
Reviewed-by: Matt Reynolds <mattreynolds@chromium.org>
Reviewed-by: Simeon Anfinrud <sanfin@chromium.org>
Commit-Queue: Rob Pitkin <robpitkin@chromium.org>
Reviewed-by: Alex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1696710}
---
diff --git a/chromecast/browser/bluetooth/public/mojom/web_bluetooth.mojom b/chromecast/browser/bluetooth/public/mojom/web_bluetooth.mojom
deleted file mode 100644
index 92e1258e..0000000
--- a/chromecast/browser/bluetooth/public/mojom/web_bluetooth.mojom
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-module chromecast.mojom;
-
-// When WebBluetooth is enabled for a Cast Activity, this interface must be
-// provided by the Activity's host, which may be running in a remote service.
-// This interface is responsible for granting the Activity access to Bluetooth
-// devices.
-interface BluetoothDeviceAccessProvider {
-  // Request that the host begin granting |client| access to approved devices.
-  RequestDeviceAccess(
-      pending_remote<BluetoothDeviceAccessProviderClient> client);
-};
-
-// The client for BluetoothDeviceAccessProvider, provided by the Activity,
-// and passed to the host via RequestDeviceAccess().
-interface BluetoothDeviceAccessProviderClient {
-  // Grant the Activity access to the device at |address|. This is a Bluetooth
-  // mac address in canonical format. This method may be invoked multiple times,
-  // or zero times, during this object's lifetime.
-  GrantAccess(string address);
-
-  // Grant the Activity access to any device discovered by the WebBluetooth
-  // stack. GrantAccess() will not be called on this interface after this method
-  // is called.
-  GrantAccessToAllDevices();
-};
\ No newline at end of file
diff --git a/chromecast/browser/cast_browser_main_parts.cc b/chromecast/browser/cast_browser_main_parts.cc
index 49384c81..a1c6acc 100644
--- a/chromecast/browser/cast_browser_main_parts.cc
+++ b/chromecast/browser/cast_browser_main_parts.cc
@@ -132,9 +132,6 @@
 #include "chromecast/graphics/cast_window_manager_default.h"  // nogncheck
 #endif
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
-#include "device/bluetooth/cast/bluetooth_adapter_cast.h"
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 
 #if !BUILDFLAG(IS_FUCHSIA)
 #include "chromecast/base/cast_sys_info_util.h"
@@ -551,12 +548,6 @@
             monitor->CreateVoter()));
   }
 
-  // base::Unretained() is safe because the browser client will outlive any
-  // component in the browser; this factory method will not be called after
-  // the browser starts to tear down.
-  device::BluetoothAdapterCast::SetFactory(base::BindRepeating(
-      &CastContentBrowserClient::CreateBluetoothAdapter,
-      base::Unretained(cast_browser_process_->browser_client())));
 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
 
   cast_content_browser_client_->SetPersistentCookieAccessSettings(
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index b13ebac..e41d53a 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -125,9 +125,6 @@
 #include "chromecast/media/service/video_geometry_setter_service.h"
 #endif  // BUILDFLAG(ENABLE_CAST_RENDERER)
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
-#include "device/bluetooth/cast/bluetooth_adapter_cast.h"
-#endif
 
 namespace chromecast {
 namespace shell {
@@ -324,13 +321,6 @@
   return cast_browser_main_parts_->media_caps();
 }
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
-scoped_refptr<device::BluetoothAdapterCast>
-CastContentBrowserClient::CreateBluetoothAdapter() {
-  NOTREACHED() << "Bluetooth Adapter is not supported!";
-}
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
-
 void CastContentBrowserClient::SetMetricsClientId(
     const std::string& client_id) {}
 
diff --git a/chromecast/browser/cast_content_browser_client.h b/chromecast/browser/cast_content_browser_client.h
index 6406ff3..cecc449 100644
--- a/chromecast/browser/cast_content_browser_client.h
+++ b/chromecast/browser/cast_content_browser_client.h
@@ -43,10 +43,6 @@
 class CrashHandlerHostLinux;
 }
 
-namespace device {
-class BluetoothAdapterCast;
-}
-
 namespace media {
 class CdmFactory;
 }
@@ -147,14 +143,6 @@
   bool OverridesAudioManager() override;
   media::MediaCapsImpl* media_caps();
 
-#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
-  // Create a BluetoothAdapter for WebBluetooth support.
-  // TODO(slan): This further couples the browser to the Cast service. Remove
-  // this once the dedicated Bluetooth service has been implemented.
-  // (b/76155468)
-  virtual scoped_refptr<device::BluetoothAdapterCast> CreateBluetoothAdapter();
-#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_FUCHSIA)
-
   // chromecast::metrics::CastMetricsServiceDelegate implementation:
   void SetMetricsClientId(const std::string& client_id) override;
   void RegisterMetricsProviders(
diff --git a/device/bluetooth/cast/bluetooth_adapter_cast.cc b/device/bluetooth/cast/bluetooth_adapter_cast.cc
deleted file mode 100644
index e73b1e1..0000000
--- a/device/bluetooth/cast/bluetooth_adapter_cast.cc
+++ /dev/null
@@ -1,486 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "device/bluetooth/cast/bluetooth_adapter_cast.h"
-
-#include <utility>
-
-#include "base/functional/bind.h"
-#include "base/functional/callback_helpers.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/no_destructor.h"
-#include "base/notimplemented.h"
-#include "chromecast/device/bluetooth/bluetooth_util.h"
-#include "chromecast/device/bluetooth/le/gatt_client_manager.h"
-#include "chromecast/device/bluetooth/le/le_scan_manager.h"
-#include "chromecast/device/bluetooth/le/remote_characteristic.h"
-#include "chromecast/device/bluetooth/le/remote_device.h"
-#include "chromecast/device/bluetooth/le/remote_service.h"
-#include "device/bluetooth/bluetooth_device.h"
-#include "device/bluetooth/bluetooth_discovery_filter.h"
-#include "device/bluetooth/bluetooth_discovery_session_outcome.h"
-#include "device/bluetooth/cast/bluetooth_device_cast.h"
-#include "device/bluetooth/cast/bluetooth_utils.h"
-
-namespace device {
-namespace {
-
-BluetoothAdapterCast::FactoryCb& GetFactory() {
-  static base::NoDestructor<BluetoothAdapterCast::FactoryCb> factory_cb;
-  return *factory_cb;
-}
-
-}  // namespace
-
-BluetoothAdapterCast::DiscoveryParams::DiscoveryParams(
-    std::unique_ptr<device::BluetoothDiscoveryFilter> filter,
-    base::OnceClosure success_callback,
-    DiscoverySessionErrorCallback error_callback)
-    : filter(std::move(filter)),
-      success_callback(std::move(success_callback)),
-      error_callback(std::move(error_callback)) {}
-
-BluetoothAdapterCast::DiscoveryParams::DiscoveryParams(
-    DiscoveryParams&& params) noexcept = default;
-BluetoothAdapterCast::DiscoveryParams& BluetoothAdapterCast::DiscoveryParams::
-operator=(DiscoveryParams&& params) = default;
-BluetoothAdapterCast::DiscoveryParams::~DiscoveryParams() = default;
-
-BluetoothAdapterCast::BluetoothAdapterCast(
-    chromecast::bluetooth::GattClientManager* gatt_client_manager,
-    chromecast::bluetooth::LeScanManager* le_scan_manager)
-    : gatt_client_manager_(gatt_client_manager),
-      le_scan_manager_(le_scan_manager),
-      weak_factory_(this) {
-  DCHECK(gatt_client_manager_);
-  DCHECK(le_scan_manager_);
-  gatt_client_manager_->AddObserver(this);
-  le_scan_manager_->AddObserver(this);
-}
-
-BluetoothAdapterCast::~BluetoothAdapterCast() {
-  gatt_client_manager_->RemoveObserver(this);
-  le_scan_manager_->RemoveObserver(this);
-}
-
-void BluetoothAdapterCast::Initialize(base::OnceClosure callback) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  initialized_ = true;
-  std::move(callback).Run();
-}
-
-std::string BluetoothAdapterCast::GetAddress() const {
-  // TODO(slan|bcf): Right now, we aren't surfacing the address of the GATT
-  // client to the caller, because there is no apparent need and this
-  // information is potentially PII. Implement this when it's needed.
-  return std::string();
-}
-
-std::string BluetoothAdapterCast::GetName() const {
-  return name_;
-}
-
-void BluetoothAdapterCast::SetName(const std::string& name,
-                                   base::OnceClosure callback,
-                                   ErrorCallback error_callback) {
-  name_ = name;
-  std::move(callback).Run();
-}
-
-bool BluetoothAdapterCast::IsInitialized() const {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return initialized_;
-}
-
-bool BluetoothAdapterCast::IsPresent() const {
-  // The BluetoothAdapter is always present on Cast devices.
-  return true;
-}
-
-bool BluetoothAdapterCast::IsPowered() const {
-  return powered_;
-}
-
-void BluetoothAdapterCast::SetPowered(bool powered,
-                                      base::OnceClosure callback,
-                                      ErrorCallback error_callback) {
-  // This class cannot actually change the powered state of the BT stack.
-  // We simulate these changes for the benefit of testing. However, we may
-  // want to actually delegate this call to the bluetooth service, at least
-  // as a signal of the client's intent.
-  // TODO(slan): Determine whether this would be useful.
-  powered_ = powered;
-  NotifyAdapterPoweredChanged(powered_);
-  std::move(callback).Run();
-}
-
-bool BluetoothAdapterCast::IsDiscoverable() const {
-  DVLOG(2) << __func__ << " GATT server mode not supported";
-  return false;
-}
-
-void BluetoothAdapterCast::SetDiscoverable(bool discoverable,
-                                           base::OnceClosure callback,
-                                           ErrorCallback error_callback) {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  std::move(error_callback).Run();
-}
-
-bool BluetoothAdapterCast::IsDiscovering() const {
-  return num_discovery_sessions_ > 0;
-}
-
-BluetoothAdapter::UUIDList BluetoothAdapterCast::GetUUIDs() const {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  return UUIDList();
-}
-
-void BluetoothAdapterCast::CreateRfcommService(
-    const BluetoothUUID& uuid,
-    const ServiceOptions& options,
-    CreateServiceCallback callback,
-    CreateServiceErrorCallback error_callback) {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  std::move(error_callback).Run("Not Implemented");
-}
-
-void BluetoothAdapterCast::CreateL2capService(
-    const BluetoothUUID& uuid,
-    const ServiceOptions& options,
-    CreateServiceCallback callback,
-    CreateServiceErrorCallback error_callback) {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  std::move(error_callback).Run("Not Implemented");
-}
-
-void BluetoothAdapterCast::RegisterAdvertisement(
-    std::unique_ptr<BluetoothAdvertisement::Data> advertisement_data,
-    CreateAdvertisementCallback callback,
-    AdvertisementErrorCallback error_callback) {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  std::move(error_callback)
-      .Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
-}
-
-void BluetoothAdapterCast::SetAdvertisingInterval(
-    const base::TimeDelta& min,
-    const base::TimeDelta& max,
-    base::OnceClosure callback,
-    AdvertisementErrorCallback error_callback) {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  std::move(error_callback)
-      .Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
-}
-
-void BluetoothAdapterCast::ConnectDevice(
-    const std::string& address,
-    const std::optional<BluetoothDevice::AddressType>& address_type,
-    ConnectDeviceCallback callback,
-    ConnectDeviceErrorCallback error_callback) {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  std::move(error_callback).Run(/*error_message=*/std::string());
-}
-
-void BluetoothAdapterCast::ResetAdvertising(
-    base::OnceClosure callback,
-    AdvertisementErrorCallback error_callback) {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  std::move(error_callback)
-      .Run(BluetoothAdvertisement::ERROR_UNSUPPORTED_PLATFORM);
-}
-
-BluetoothLocalGattService* BluetoothAdapterCast::GetGattService(
-    const std::string& identifier) const {
-  NOTIMPLEMENTED() << __func__ << " GATT server mode not supported";
-  return nullptr;
-}
-
-base::WeakPtr<BluetoothAdapter> BluetoothAdapterCast::GetWeakPtr() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return weak_factory_.GetWeakPtr();
-}
-
-base::WeakPtr<BluetoothAdapterCast> BluetoothAdapterCast::GetCastWeakPtr() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return weak_factory_.GetWeakPtr();
-}
-
-bool BluetoothAdapterCast::SetPoweredImpl(bool powered) {
-  NOTREACHED() << "This method is not invoked when SetPowered() is overridden.";
-}
-
-void BluetoothAdapterCast::StartScanWithFilter(
-    [[maybe_unused]] std::unique_ptr<device::BluetoothDiscoveryFilter>
-        discovery_filter,
-    DiscoverySessionResultCallback callback) {
-  // The discovery filter is unused for now, as the Cast bluetooth stack does
-  // not expose scan filters yet. However, implementation of filtering would
-  // save numerous UI<->IO threadhops by eliminating unnecessary calls to
-  // GetDevice().
-  // TODO(bcf|slan): Wire this up once scan filters are implemented and remove
-  // the [[maybe_unused]].
-
-  auto split_callback = base::SplitOnceCallback(std::move(callback));
-
-  // Add this request to the queue.
-  pending_discovery_requests_.emplace(BluetoothAdapterCast::DiscoveryParams(
-      std::move(discovery_filter),
-      base::BindOnce(std::move(split_callback.first), /*is_error=*/false,
-                     UMABluetoothDiscoverySessionOutcome::SUCCESS),
-      base::BindOnce(std::move(split_callback.second), /*is_error=*/true)));
-
-  // If the queue length is greater than 1 (i.e. there was a pending request
-  // when this method was called), exit early. This request will be processed
-  // after the pending requests.
-  if (pending_discovery_requests_.size() > 1u)
-    return;
-
-  // There is no active discovery session, and no pending requests. Enable
-  // scanning.
-  le_scan_manager_->RequestScan(base::BindOnce(
-      &BluetoothAdapterCast::OnScanEnabled, weak_factory_.GetWeakPtr()));
-}
-
... (file diff truncated)
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/device/bluetooth/bluetooth_adapter_unittest.cc b/device/bluetooth/bluetooth_adapter_unittest.cc
index c34e446d..e6cbb5d 100644
--- a/device/bluetooth/bluetooth_adapter_unittest.cc
+++ b/device/bluetooth/bluetooth_adapter_unittest.cc
@@ -42,8 +42,6 @@
 #elif BUILDFLAG(IS_WIN)
 #include "base/win/windows_version.h"
 #include "device/bluetooth/test/bluetooth_test_win.h"
-#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
-#include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
 #elif BUILDFLAG(IS_FUCHSIA)
@@ -1705,8 +1703,7 @@
 
 #endif  // BUILDFLAG(IS_WIN)
 
-#if (BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)) && \
-    !defined(USE_CAST_BLUETOOTH_ADAPTER)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #define MAYBE_RegisterLocalGattServices RegisterLocalGattServices
 #else
 #define MAYBE_RegisterLocalGattServices DISABLED_RegisterLocalGattServices
@@ -1744,8 +1741,7 @@
                       GetGattErrorCallback(Call::EXPECTED));
 }
 
-#if (BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)) && \
-    !defined(USE_CAST_BLUETOOTH_ADAPTER)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #define MAYBE_RegisterMultipleServices RegisterMultipleServices
 #else
 #define MAYBE_RegisterMultipleServices DISABLED_RegisterMultipleServices
@@ -1794,8 +1790,7 @@
   EXPECT_TRUE(ServiceSetsEqual(RegisteredGattServices(), {}));
 }
 
-#if (BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)) && \
-    !defined(USE_CAST_BLUETOOTH_ADAPTER)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #define MAYBE_DeleteServices DeleteServices
 #else
 #define MAYBE_DeleteServices DISABLED_DeleteServices
diff --git a/device/bluetooth/bluetooth_device_unittest.cc b/device/bluetooth/bluetooth_device_unittest.cc
index f1dc97f..044f94b 100644
--- a/device/bluetooth/bluetooth_device_unittest.cc
+++ b/device/bluetooth/bluetooth_device_unittest.cc
@@ -30,8 +30,6 @@
 #include "device/bluetooth/test/bluetooth_test_mac.h"
 #elif BUILDFLAG(IS_WIN)
 #include "device/bluetooth/test/bluetooth_test_win.h"
-#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
-#include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
 #elif BUILDFLAG(IS_FUCHSIA)
@@ -633,8 +631,7 @@
 
 // TODO(crbug.com/41310506): Remove #if once the BlueZ caching behavior is
 // changed.
-#if (BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)) && \
-    !defined(USE_CAST_BLUETOOTH_ADAPTER)
+#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
   // On ChromeOS and Linux, BlueZ persists all service data meaning if
   // a device stops advertising service data for a UUID, BlueZ will
   // still return the cached value for that UUID.
@@ -2223,13 +2220,11 @@
   BluetoothDevice* device = SimulateLowEnergyDevice(1);
   EXPECT_EQ(BLUETOOTH_TRANSPORT_LE, device->GetType());
 
-#if !defined(USE_CAST_BLUETOOTH_ADAPTER)
   BluetoothDevice* device2 = SimulateLowEnergyDevice(6);
   EXPECT_EQ(BLUETOOTH_TRANSPORT_DUAL, device2->GetType());
 
   BluetoothDevice* device3 = SimulateClassicDevice();
   EXPECT_EQ(BLUETOOTH_TRANSPORT_CLASSIC, device3->GetType());
-#endif  // !defined(USE_CAST_BLUETOOTH_ADAPTER)
 }
 #endif  // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX) ||
         // BUILDFLAG(IS_ANDROID)
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
index e3b75cf..d0b1e14 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
@@ -28,8 +28,6 @@
 #include "device/bluetooth/test/bluetooth_test_mac.h"
 #elif BUILDFLAG(IS_WIN)
 #include "device/bluetooth/test/bluetooth_test_win.h"
-#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
-#include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
 #elif BUILDFLAG(IS_FUCHSIA)
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
index 8268186..3d2f704 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
@@ -19,8 +19,6 @@
 #include "device/bluetooth/test/bluetooth_test_mac.h"
 #elif BUILDFLAG(IS_WIN)
 #include "device/bluetooth/test/bluetooth_test_win.h"
-#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
-#include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
 #elif BUILDFLAG(IS_FUCHSIA)
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
index aa4b2f9..b36f427 100644
--- a/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
@@ -16,8 +16,6 @@
 #include "device/bluetooth/test/bluetooth_test_mac.h"
 #elif BUILDFLAG(IS_WIN)
 #include "device/bluetooth/test/bluetooth_test_win.h"
-#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
-#include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
 #elif BUILDFLAG(IS_FUCHSIA)
diff --git a/device/bluetooth/cast/bluetooth_adapter_cast_unittest.cc b/device/bluetooth/cast/bluetooth_adapter_cast_unittest.cc
deleted file mode 100644
index c4ac669..0000000
--- a/device/bluetooth/cast/bluetooth_adapter_cast_unittest.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "device/bluetooth/cast/bluetooth_adapter_cast.h"
-
-#include "base/functional/callback_helpers.h"
-#include "base/test/gtest_util.h"
-#include "base/test/mock_callback.h"
-#include "base/test/task_environment.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-class BluetoothAdapterCastTest : public testing::Test {
- public:
-  BluetoothAdapterCastTest() = default;
-
-  BluetoothAdapterCastTest(const BluetoothAdapterCastTest&) = delete;
-  BluetoothAdapterCastTest& operator=(const BluetoothAdapterCastTest&) = delete;
-
-  ~BluetoothAdapterCastTest() override {
-    BluetoothAdapterCast::ResetFactoryForTest();
-  }
-
- private:
-  base::test::TaskEnvironment task_environment_;
-};
-
-TEST_F(BluetoothAdapterCastTest, TestSetFactory) {
-  // Test that the callback set with SetFactory() is called by Create().
-  base::MockCallback<BluetoothAdapterCast::FactoryCb> callback;
-  BluetoothAdapterCast::SetFactory(callback.Get());
-
-  // Call the method once.
-  EXPECT_CALL(callback, Run());
-  BluetoothAdapterCast::Create();
-
-  // Call it again.
-  EXPECT_CALL(callback, Run());
-  BluetoothAdapterCast::Create();
-}
-
-#if DCHECK_IS_ON()
-TEST_F(BluetoothAdapterCastTest, TestSetFactoryTwiceCrashes) {
-  // Test that calling SetFactory() more than once causes a crash.
-  base::MockCallback<BluetoothAdapterCast::FactoryCb> callback;
-  BluetoothAdapterCast::SetFactory(callback.Get());
-
-  // The factory has already been set. Crash.
-  EXPECT_DCHECK_DEATH(BluetoothAdapterCast::SetFactory(callback.Get()));
-}
-
-TEST_F(BluetoothAdapterCastTest, TestNoSetFactoryCrashes) {
-  // Test that calling BluetoothAdapterCast::Create() without calling
-  // SetFactory() causes a crash.
-  EXPECT_DCHECK_DEATH(BluetoothAdapterCast::Create());
-}
-#endif  // DCHECK_IS_ON()
-
-}  // namespace device
diff --git a/device/bluetooth/cast/bluetooth_utils_unittest.cc b/device/bluetooth/cast/bluetooth_utils_unittest.cc
deleted file mode 100644
index ae681bb1..0000000
--- a/device/bluetooth/cast/bluetooth_utils_unittest.cc
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "device/bluetooth/cast/bluetooth_utils.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace device {
-
-TEST(BluetoothUtilsTest, TestGetCanonicalBluetoothAddress) {
-  // Test that the correct canonical address is returned for a variety of
-  // addresses.
-  ASSERT_EQ("AA:BB:CC:DD:EE:FF", GetCanonicalBluetoothAddress(
-                                     {{0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa}}));
-  ASSERT_EQ("44:55:66:77:88:99", GetCanonicalBluetoothAddress(
-                                     {{0x99, 0x88, 0x77, 0x66, 0x55, 0x44}}));
-  ASSERT_EQ("00:00:00:00:00:00", GetCanonicalBluetoothAddress(
-                                     {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}));
-}
-
-TEST(BluetoothUtilsTest, TestUuidToBluetoothUUID_128bit) {
-  // Test a 128-bit UUID.
-  BluetoothUUID uuid =
-      UuidToBluetoothUUID({{0x12, 0x3e, 0x45, 0x67, 0xe8, 0x9b, 0x12, 0xd3,
-                            0xa4, 0x56, 0x42, 0x66, 0x55, 0x44, 0x00, 0x00}});
-  ASSERT_TRUE(uuid.IsValid());
-  ASSERT_EQ(BluetoothUUID::kFormat128Bit, uuid.format());
-  ASSERT_EQ("123e4567-e89b-12d3-a456-426655440000", uuid.value());
-  ASSERT_EQ("123e4567-e89b-12d3-a456-426655440000", uuid.canonical_value());
-}
-
-TEST(BluetoothUtilsTest, TestUuidToBluetoothUUID_16bit) {
-  // Test a 16-bit UUID. Note that since chromecast::bluetooth_v2_shlib::Uuid
-  // always has 128 bits, the underlying value of every BluetoothUUID returned
-  // from this function will look like "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".
-  // For information on how a 16-bit UUID is represented as a 128-bit UUID, see
-  // http://www.argenox.com/a-ble-advertising-primer.
-
-  // Get BluetoothUUID for 0xFE34.
-  BluetoothUUID uuid =
-      UuidToBluetoothUUID({{0x00, 0x00, 0xfe, 0x34, 0x00, 0x00, 0x10, 0x00,
-                            0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB}});
-  ASSERT_TRUE(uuid.IsValid());
-  ASSERT_EQ(BluetoothUUID::kFormat128Bit, uuid.format());
-  ASSERT_EQ("0000fe34-0000-1000-8000-00805f9b34fb", uuid.value());
-  ASSERT_EQ("0000fe34-0000-1000-8000-00805f9b34fb", uuid.canonical_value());
-}
-
-TEST(BluetoothUtilsTest, TestGetCanonicalBluetoothUuid) {
-  std::string uuid = GetCanonicalBluetoothUuid(
-      {{0x12, 0x3e, 0x45, 0x67, 0xe8, 0x9b, 0x12, 0xd3, 0xa4, 0x56, 0x42, 0x66,
-        0x55, 0x44, 0x00, 0x00}});
-  ASSERT_EQ("123e4567-e89b-12d3-a456-426655440000", uuid);
-}
-
-}  // namespace device
diff --git a/device/bluetooth/test/bluetooth_gatt_server_test.h b/device/bluetooth/test/bluetooth_gatt_server_test.h
index ee1dede..6a3ff34 100644
--- a/device/bluetooth/test/bluetooth_gatt_server_test.h
+++ b/device/bluetooth/test/bluetooth_gatt_server_test.h
@@ -19,8 +19,6 @@
 #include "device/bluetooth/test/bluetooth_test_mac.h"
 #elif BUILDFLAG(IS_WIN)
 #include "device/bluetooth/test/bluetooth_test_win.h"
-#elif defined(USE_CAST_BLUETOOTH_ADAPTER)
-#include "device/bluetooth/test/bluetooth_test_cast.h"
 #elif BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
 #include "device/bluetooth/test/bluetooth_test_bluez.h"
 #elif BUILDFLAG(IS_FUCHSIA)
diff --git a/device/bluetooth/test/bluetooth_test_cast.cc b/device/bluetooth/test/bluetooth_test_cast.cc
deleted file mode 100644
index 0fb1d601..0000000
--- a/device/bluetooth/test/bluetooth_test_cast.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2018 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "device/bluetooth/test/bluetooth_test_cast.h"
-
-#include "base/functional/callback_helpers.h"
-#include "chromecast/device/bluetooth/bluetooth_util.h"
-#include "chromecast/device/bluetooth/le/mock_gatt_client_manager.h"
-#include "chromecast/device/bluetooth/le/remote_device.h"
-#include "device/bluetooth/cast/bluetooth_adapter_cast.h"
-
-using ::testing::ByMove;
-using ::testing::Return;
-
-namespace device {
-
-class BluetoothTestCast::GattClientManager
-    : public chromecast::bluetooth::MockGattClientManager {
- public:
-  GattClientManager() = default;
-  ~GattClientManager() override = default;
-
-  // chromecast::bluetooth::GattClientManager implementation:
-  void GetDevice(
-      const chromecast::bluetooth_v2_shlib::Addr& addr,
-      base::OnceCallback<void(
-          scoped_refptr<chromecast::bluetooth::RemoteDevice>)> cb) override {
-    auto it = addr_to_remote_device_.find(addr);
-    if (it != addr_to_remote_device_.end()) {
-      std::move(cb).Run(it->second);
-      return;
... (truncated)
Loading diff…

Original Bug Report

reported by vm...@google.com

Latent cross-device data leak in Cast OS Web Bluetooth via non-unique GATT identifiers

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: The Cast OS Bluetooth implementation uses bare UUIDs instead of unique handles for GATT identifiers. This causes collisions in WebBluetoothServiceImpl’s internal maps if multiple devices share a characteristic UUID. In a scenario where Web Bluetooth is enabled, this allows an attacker to intercept GATT notifications from unconnected devices.

Affected files:

  • device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc
  • device/bluetooth/cast/bluetooth_remote_gatt_service_cast.cc
  • device/bluetooth/cast/bluetooth_remote_gatt_descriptor_cast.cc
  • content/browser/bluetooth/web_bluetooth_service_impl.cc

Estimated timestamp from git blame: 2018-04-04

Summary

A logic vulnerability exists in the Cast OS specific implementation of the Chromium Bluetooth stack. The implementation fails to provide unique identifiers for GATT services, characteristics, and descriptors, relying entirely on bare UUIDs. If Web Bluetooth were enabled on Cast OS, this lack of uniqueness would cause collisions in the browser’s permission tracking maps (WebBluetoothServiceImpl), leading to a potential cross-device information leak where a site granted permission to one device could intercept notifications from another device sharing the same characteristic UUID.

Note: Current standard builds of Cast OS disable Web Bluetooth via a NOTREACHED() macro in CastContentBrowserClient::CreateBluetoothAdapter(). Therefore, this is currently a latent vulnerability in the underlying Cast Bluetooth stack rather than an actively exploitable browser flaw.

Technical Details

The device::BluetoothGattCharacteristic interface requires GetIdentifier() to return a string that is unique across the entire adapter. However, on Cast OS (is_castos), the implementation in device/bluetooth/cast/bluetooth_remote_gatt_characteristic_cast.cc violates this contract by returning only the canonical UUID:

std::string BluetoothRemoteGattCharacteristicCast::GetIdentifier() const {
  return GetUUID().canonical_value();
}

Similar non-unique implementations exist for services and descriptors in the same directory.

WebBluetoothServiceImpl relies on these identifiers being unique. When a renderer requests characteristics, the browser populates an internal map (characteristic_id_to_service_id_) using std::unordered_map::insert (content/browser/bluetooth/web_bluetooth_service_impl.cc:1034).

If two physically separate devices expose a characteristic with the same UUID (e.g., the standard Heart Rate UUID 0x2A37), a collision occurs. In Release builds, the insert fails silently because the key already exists, leaving the map pointing to the first device’s metadata.

When any device on the adapter sends a notification, WebBluetoothServiceImpl::GattCharacteristicValueChanged filters it by checking if the identifier (the UUID) exists in the map. Because the maps are polluted by the collision, notifications from any device with that UUID will pass the filter and be routed to the active Mojo session established for the first device, resulting in a data leak.

Potential Exploitation Steps

Assuming Web Bluetooth is enabled or reachable on the Cast OS device:

  1. An attacker-controlled web page requests and is granted Web Bluetooth permission to connect to Device A.
  2. The page discovers services and characteristics on Device A. The browser maps the characteristic’s UUID to Device A’s service in its internal caching maps.
  3. The page calls startNotifications() on a standard characteristic (e.g., Heart Rate, UUID 0x2A37), establishing a notification session mapped to that UUID.
  4. Sometime later, Device B (which the attacker does not have permission to access) sends a GATT notification for its own Heart Rate characteristic.
  5. The Cast OS Bluetooth stack routes the notification to the BluetoothAdapter and then globally to WebBluetoothServiceImpl.
  6. WebBluetoothServiceImpl checks if the incoming characteristic’s identifier (the UUID) is in its map. It finds the entry created for Device A and passes the filter.
  7. The browser blindly forwards Device B’s raw, sensitive notification data to the attacker’s renderer process via the established Mojo session.

Suggested Fix

Update BluetoothRemoteGattCharacteristicCast::GetIdentifier(), BluetoothRemoteGattServiceCast::GetIdentifier(), and BluetoothRemoteGattDescriptorCast::GetIdentifier() to return a genuinely unique string.

The underlying chromecast::bluetooth::RemoteCharacteristic provides a platform-specific handle() (typically the 16-bit GATT attribute handle) which is unique per device connection. The identifier should be constructed by combining the device MAC address, the service handle, and the characteristic handle, similar to how it is implemented in device/bluetooth/bluetooth_remote_gatt_characteristic_mac.mm or device/bluetooth/bluez/bluetooth_remote_gatt_characteristic_bluez.cc.

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