From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Sun, 26 Apr 2026 11:55:54 -0700 Subject: [PATCH] Bluetooth: hci_core: guard hci_send_cmd against closed device This patch is based on a WARNING as reported by syzbot at https://lore.kernel.org/r/69ed492c.050a0220.e51af.0005.GAE@google.com. 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, and hci_conn_hash_flush() cancels pending timers only afterward. If l2cap_info_timeout is already scheduled on the 'events' workqueue before hci_conn_hash_flush() can cancel it, it fires in the shutdown window. The chain l2cap_info_timeout -> l2cap_conn_start -> hci_conn_security -> hci_conn_auth -> hci_send_cmd then attempts to queue hci_cmd_work onto hdev->workqueue while it is in the __WQ_DRAINING or __WQ_DESTROYING state. The workqueue core detects this and fires a WARN_ONCE: 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 and returns -ENETDOWN before any allocation is attempted. Reported-by: syzbot+00f5a866124dc44cce14@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/69ed492c.050a0220.e51af.0005.GAE@google.com Oops-Analysis: http://oops.fenrus.org/reports/lkml/69ed492c.050a0220.e51af.0005.GAE_google.com/report.html Fixes: c347b765fe70 ("Bluetooth: Move command task to workqueue") Assisted-by: github-copilot:claude-sonnet-4.6 linux-kernel-oops-x86. Signed-off-by: Arjan van de Ven Cc: Marcel Holtmann Cc: Luiz Augusto von Dentz Cc: linux-bluetooth@vger.kernel.org Cc: linux-kernel@vger.kernel.org --- 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 c46c1236ebfa..4d4eba8a361a 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