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.