Chrome · Bluetooth
CVE-2026-17986
Logic Error in Bluetooth
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifcontent/browser/bluetooth/web_bluetooth_service_impl_unittest.cc |
modified | |
TEST_Fcontent/browser/bluetooth/web_bluetooth_service_impl_unittest.cc |
modified |
Files Changed
content/browser/bluetooth/web_bluetooth_service_impl.cccontent/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
Patch
From 1c56c2b5dc9a01bdd62896a2bfd5e3394a7d1acc Mon Sep 17 00:00:00 2001 From: Alvin Ji <[email protected]> Date: Tue, 16 Jun 2026 17:33:59 -0700 Subject: [PATCH] bluetooth: Verify bluetooth is allowed in ForgetDevice This CL adds the missing GetBluetoothAllowed() check to WebBluetoothServiceImpl::ForgetDevice. This prevents compromised renderers from bypassing Permissions-Policy or MayUseBluetooth checks by calling this entry point. Change-Id: I02c89437b7e5f7ef9fa7c3cf2a6a72bbeb6906a1 Bug: 519981896 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7904063 Reviewed-by: Matt Reynolds <[email protected]> Commit-Queue: Alvin Ji <[email protected]> Cr-Commit-Position: refs/heads/main@{#1648006} --- diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc index ccc16bb8..87d0e0f 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.cc +++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc @@ -855,6 +855,10 @@ void WebBluetoothServiceImpl::ForgetDevice( const blink::WebBluetoothDeviceId& device_id, ForgetDeviceCallback callback) { + if (GetBluetoothAllowed() != blink::mojom::WebBluetoothResult::SUCCESS) { + std::move(callback).Run(); + return; + } CHECK(back_forward_cache_feature_handle_.IsValid()); if (!base::FeatureList::IsEnabled( features::kWebBluetoothNewPermissionsBackend)) { diff --git a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc index 4880d79..be04cdd 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc +++ b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc @@ -372,6 +372,23 @@ has_device_permission_ = value; } + bool MayUseBluetooth(RenderFrameHost* frame) override { + return may_use_bluetooth_; + } + + void set_may_use_bluetooth(bool value) { may_use_bluetooth_ = value; } + + AllowWebBluetoothResult AllowWebBluetooth( + content::BrowserContext* browser_context, + const url::Origin& requesting_origin, + const url::Origin& embedding_origin) override { + return allow_web_bluetooth_; + } + + void set_allow_web_bluetooth(AllowWebBluetoothResult value) { + allow_web_bluetooth_ = value; + } + void RunBluetoothScanningPromptEventCallback( BluetoothScanningPrompt::Event event) { if (!prompt_) { @@ -383,6 +400,9 @@ private: bool has_device_permission_ = false; + bool may_use_bluetooth_ = true; + AllowWebBluetoothResult allow_web_bluetooth_ = + AllowWebBluetoothResult::kAllow; raw_ptr<FakeBluetoothScanningPrompt, AcrossTasksDanglingUntriaged> prompt_ = nullptr; }; @@ -694,6 +714,17 @@ &service_ptr_.ExtractAsDangling()->render_frame_host()); } + void ForgetDevice( + const blink::WebBluetoothDeviceId& device_id, + blink::mojom::WebBluetoothService::ForgetDeviceCallback callback) { + service_ptr_->ForgetDevice(device_id, std::move(callback)); + } + + std::string GetAllowedDeviceAddress( + const blink::WebBluetoothDeviceId& device_id) { + return service_ptr_->allowed_devices().GetDeviceAddress(device_id); + } + scoped_refptr<FakeBluetoothAdapter> adapter_; raw_ptr<WebBluetoothServiceImpl> service_ptr_ = nullptr; mojo::Remote<blink::mojom::WebBluetoothService> service_; @@ -1537,4 +1568,20 @@ EXPECT_EQ(write_future.Get(), WebBluetoothResult::BLOCKLISTED_WRITE); } +TEST_F(WebBluetoothServiceImplTest, ForgetDevice_NotAllowed) { + blink::WebBluetoothDeviceId device_id = + AddTestDevice(battery_device_bundle()); + EXPECT_FALSE(GetAllowedDeviceAddress(device_id).empty()); + + browser_client_.bluetooth_delegate()->set_may_use_bluetooth(false); + + base::test::TestFuture<void> future; + ForgetDevice(device_id, future.GetCallback()); + EXPECT_TRUE(future.Wait()); + + // The device should STILL be in allowed devices because ForgetDevice should + // return early. + EXPECT_FALSE(GetAllowedDeviceAddress(device_id).empty()); +} + } // namespace content
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
index 4880d79..be04cdd 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
@@ -372,6 +372,23 @@
has_device_permission_ = value;
}
+ bool MayUseBluetooth(RenderFrameHost* frame) override {
+ return may_use_bluetooth_;
+ }
+
+ void set_may_use_bluetooth(bool value) { may_use_bluetooth_ = value; }
+
+ AllowWebBluetoothResult AllowWebBluetooth(
+ content::BrowserContext* browser_context,
+ const url::Origin& requesting_origin,
+ const url::Origin& embedding_origin) override {
+ return allow_web_bluetooth_;
+ }
+
+ void set_allow_web_bluetooth(AllowWebBluetoothResult value) {
+ allow_web_bluetooth_ = value;
+ }
+
void RunBluetoothScanningPromptEventCallback(
BluetoothScanningPrompt::Event event) {
if (!prompt_) {
@@ -383,6 +400,9 @@
private:
bool has_device_permission_ = false;
+ bool may_use_bluetooth_ = true;
+ AllowWebBluetoothResult allow_web_bluetooth_ =
+ AllowWebBluetoothResult::kAllow;
raw_ptr<FakeBluetoothScanningPrompt, AcrossTasksDanglingUntriaged> prompt_ =
nullptr;
};
@@ -694,6 +714,17 @@
&service_ptr_.ExtractAsDangling()->render_frame_host());
}
+ void ForgetDevice(
+ const blink::WebBluetoothDeviceId& device_id,
+ blink::mojom::WebBluetoothService::ForgetDeviceCallback callback) {
+ service_ptr_->ForgetDevice(device_id, std::move(callback));
+ }
+
+ std::string GetAllowedDeviceAddress(
+ const blink::WebBluetoothDeviceId& device_id) {
+ return service_ptr_->allowed_devices().GetDeviceAddress(device_id);
+ }
+
scoped_refptr<FakeBluetoothAdapter> adapter_;
raw_ptr<WebBluetoothServiceImpl> service_ptr_ = nullptr;
mojo::Remote<blink::mojom::WebBluetoothService> service_;
@@ -1537,4 +1568,20 @@
EXPECT_EQ(write_future.Get(), WebBluetoothResult::BLOCKLISTED_WRITE);
}
+TEST_F(WebBluetoothServiceImplTest, ForgetDevice_NotAllowed) {
+ blink::WebBluetoothDeviceId device_id =
+ AddTestDevice(battery_device_bundle());
+ EXPECT_FALSE(GetAllowedDeviceAddress(device_id).empty());
+
+ browser_client_.bluetooth_delegate()->set_may_use_bluetooth(false);
+
+ base::test::TestFuture<void> future;
+ ForgetDevice(device_id, future.GetCallback());
+ EXPECT_TRUE(future.Wait());
+
+ // The device should STILL be in allowed devices because ForgetDevice should
+ // return early.
+ EXPECT_FALSE(GetAllowedDeviceAddress(device_id).empty());
+}
+
} // namespace content
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page