During the shutdown process of a Bluetooth device, hci_dev_close_sync() is called. This function clears the HCI_UP flag, flushes pending RX and TX works, and drains the workqueue to prevent lockdep issues during cleanup. After the workqueue is drained, it flushes the connections. Because the connections are flushed after the workqueue is drained, there is a race window where the workqueue is draining but the sockets are still in the BT_CONNECTED state. If a concurrent thread calls sendmsg() on an active L2CAP, SCO, or ISO socket during this window, the socket state is still considered connected. The sendmsg() call will eventually reach hci_send_acl(), hci_send_sco(), or hci_send_iso(), which unconditionally attempt to queue the transmission work (hdev->tx_work) on the draining workqueue. This triggers a warning in __queue_work() because non-chained work cannot be queued on a draining workqueue. [ cut here ] workqueue: cannot queue hci_tx_work on wq hci0 WARNING: kernel/workqueue.c:2306 at __queue_work+0xd4a/0x1090 kernel/workqueue.c:2305 RIP: 0010:__queue_work+0xd66/0x1090 kernel/workqueue.c:2305 Call Trace: queue_work_on+0x106/0x1c0 kernel/workqueue.c:2452 l2cap_chan_send+0x168a/0x22f0 net/bluetooth/l2cap_core.c:-1 l2cap_sock_sendmsg+0x33a/0x4d0 net/bluetooth/l2cap_sock.c:1180 sock_sendmsg_nosec+0x13a/0x180 net/socket.c:775 __sock_sendmsg net/socket.c:790 [inline] ____sys_sendmsg+0x54e/0x850 net/socket.c:2684 ___sys_sendmsg+0x2a5/0x360 net/socket.c:2738 __sys_sendmmsg+0x273/0x4d0 net/socket.c:2827 __do_sys_sendmmsg net/socket.c:2854 [inline] __se_sys_sendmmsg net/socket.c:2851 [inline] __x64_sys_sendmmsg+0xa0/0xc0 net/socket.c:2851 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x15f/0x560 arch/x86/entry/syscall_64.c:94 To fix this issue, modify hci_send_acl(), hci_send_sco(), and hci_send_iso() to check if the HCI_UP flag is set on the device before attempting to queue the transmission work. The HCI_UP flag is cleared early in hci_dev_close_sync(), well before the workqueue is drained. If the flag is not set, the work is not queued. The skb is safely appended to the channel's or connection's data_q, which will be safely purged shortly after when hci_conn_hash_flush() executes, ensuring no memory leaks occur. Fixes: 76727c02c1e1 ("Bluetooth: Call drain_workqueue() before resetting state") Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot Reported-by: syzbot+b6919040d9958e2fc1ae@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=b6919040d9958e2fc1ae Link: https://syzkaller.appspot.com/ai_job?id=59121999-1293-40a6-aeeb-713e2d7dc16b To: To: "Luiz Augusto von Dentz" To: "Marcel Holtmann" To: "Johan Hedberg" Cc: --- diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5ba9fe826..d9f091af3 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3242,7 +3242,8 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) hci_queue_acl(chan, &chan->data_q, skb, flags); - queue_work(hdev->workqueue, &hdev->tx_work); + if (test_bit(HCI_UP, &hdev->flags)) + queue_work(hdev->workqueue, &hdev->tx_work); } /* Send SCO data */ @@ -3267,7 +3268,8 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb) bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(&conn->data_q)); - queue_work(hdev->workqueue, &hdev->tx_work); + if (test_bit(HCI_UP, &hdev->flags)) + queue_work(hdev->workqueue, &hdev->tx_work); } /* Send ISO data */ @@ -3338,7 +3340,8 @@ void hci_send_iso(struct hci_conn *conn, struct sk_buff *skb) hci_queue_iso(conn, &conn->data_q, skb); - queue_work(hdev->workqueue, &hdev->tx_work); + if (test_bit(HCI_UP, &hdev->flags)) + queue_work(hdev->workqueue, &hdev->tx_work); } /* ---- HCI TX task (outgoing data) ---- */ base-commit: 075b74841bd0065a3bda3440873c747938e69b68 -- This is an AI-generated patch subject to moderation. Reply with '#syz upstream' to Sign-off the patch as a human author and send it to the upstream kernel mailing lists. Reply with '#syz reject' to reject it ('#syz unreject' to undo). See https://goo.gle/syzbot-ai-patches for information about AI-generated patches. The person who has signed off on the patch is responsible for addressing comments. syzbot engineers can be reached at syzkaller@googlegroups.com.