[PATCH v3 2/3] Bluetooth: hci_core: Hold the request lock for the power-on update
From: Cen Zhang
Date: Sat Oct 31 2026 - 03:33:49 EST
When the controller is already up in the HCI_AUTO_OFF window, for
example after set_public_address() or set_external_config() queued
hdev->power_on, hci_power_on() runs hci_powered_update_sync() directly.
The update registers the SMP channels and sends HCI commands through
__hci_cmd_sync() and its variants, which require hdev->req_lock. Commit
cf75ad8b41d2 ("Bluetooth: hci_sync: Convert MGMT_SET_POWERED") dropped
the request lock that this path held around the update.
hdev->power_on runs on hdev->req_workqueue, which orders it with the
other request works, but not with request lock users in process context
such as the HCIDEVRESET ioctl or an rfkill block. Such a user can close
the controller after the HCI_UP test or while the update runs, and the
update then registers the SMP channels again after hci_dev_close_sync()
unregistered them:
hdev->power_on HCIDEVRESET ioctl
test HCI_UP
clear HCI_AUTO_OFF
hci_req_sync_lock()
hci_dev_close_sync()
smp_unregister()
hci_powered_update_sync()
smp_register()
send HCI commands
Take the request lock again before testing that the controller is up,
as hci_power_on_sync() runs the same update with the lock held when
management powers on the controller through the cmd_sync queue.
Fixes: cf75ad8b41d2 ("Bluetooth: hci_sync: Convert MGMT_SET_POWERED")
Signed-off-by: Cen Zhang <zzzccc427@xxxxxxxxx>
---
Assisted-by: LLM
net/bluetooth/hci_core.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 2a29cfdee9728..55bad385e78c4 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1006,14 +1006,17 @@ static void hci_power_on(struct work_struct *work)
BT_DBG("%s", hdev->name);
+ hci_req_sync_lock(hdev);
if (test_bit(HCI_UP, &hdev->flags) &&
hci_dev_test_flag(hdev, HCI_MGMT) &&
hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
cancel_delayed_work(&hdev->power_off);
err = hci_powered_update_sync(hdev);
+ hci_req_sync_unlock(hdev);
mgmt_power_on(hdev, err);
return;
}
+ hci_req_sync_unlock(hdev);
err = hci_dev_do_open(hdev);
if (err < 0) {