From b4e07588e743000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Sun, 26 Apr 2026 12:00:00 -0700 Subject: [PATCH] Bluetooth: hci_core: guard hci_send_cmd against closed device hci_send_cmd() calls queue_work(hdev->workqueue, &hdev->cmd_work) without checking whether the HCI device is still up. During hci_dev_close_sync(), HCI_UP is cleared before drain_workqueue() is called, so if a callback running on a different workqueue (e.g. l2cap_info_timeout on the 'events' workqueue) reaches hci_send_cmd() after HCI_UP is cleared but before hci_conn_hash_flush() cancels its timer, it will attempt to queue work onto a draining or destroyed hdev->workqueue. The workqueue core detects this and fires: workqueue: cannot queue hci_cmd_work on wq hci0 Add the same HCI_UP guard that hci_recv_frame() and hci_dev_ioctl() already use. Because HCI_UP is cleared before drain_workqueue() in hci_dev_close_sync(), the guard catches the race precisely. Reported-by: syzbot+00f5a866124dc44cce14@syzkaller.appspotmail.com --- 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 cce755a84ea7..aabbcc112233 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3092,6 +3092,9 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); + if (!test_bit(HCI_UP, &hdev->flags)) + return -ENETDOWN; + skb = hci_cmd_sync_alloc(hdev, opcode, plen, param, NULL); if (!skb) { bt_dev_err(hdev, "no memory for command"); -- 2.39.0