[PATCH] Bluetooth: hci_core: Serialize fragmented ISO packet queueing

From: Chengfeng Ye

Date: Sat Sep 26 2026 - 13:30:14 EST


The fragmented path in hci_queue_iso() calls __skb_queue_tail() without
holding conn->data_q.lock. The socket lock serializes senders, but the
transmit worker can concurrently remove packets from this queue using
skb_dequeue().

After an insertion saves the old tail pointer, hci_sched_iso() can dequeue
that packet and pass it to the driver. The driver can free the packet
before the insertion resumes and writes to the old tail's next pointer,
causing a use-after-free. Concurrent updates can also corrupt the queue.

KASAN reported:

BUG: KASAN: slab-use-after-free in hci_send_iso+0xc7d/0xe10
Call Trace:
hci_send_iso+0xc7d/0xe10
iso_sock_sendmsg+0x710/0x900
__sys_sendto+0x34a/0x3a0

Allocated by task 86:
__alloc_skb+0xdd/0x820
alloc_skb_with_frags+0x7e/0x770
sock_alloc_send_pskb+0x67a/0x820
bt_skb_sendmsg.constprop.0+0xc0/0x6c0
iso_sock_sendmsg+0x44e/0x900

Freed by task 90:
kmem_cache_free+0xcb/0x3d0
vhci_read+0x33f/0x4d0
vfs_read+0x177/0xa20

Hold the queue lock across the entire fragment batch, as hci_queue_acl()
does, to serialize insertion against dequeue while preserving fragment
ordering.

Fixes: 26afbd826ee3 ("Bluetooth: Add initial implementation of CIS connections")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Chengfeng Ye <nicoyip.dev@xxxxxxxxx>
---
net/bluetooth/hci_core.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d183efaf9063..9ae1b25d7f60 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -3325,6 +3325,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,

skb_shinfo(skb)->frag_list = NULL;

+ spin_lock_bh(&queue->lock);
+
__skb_queue_tail(queue, skb);

do {
@@ -3339,6 +3341,8 @@ static void hci_queue_iso(struct hci_conn *conn, struct sk_buff_head *queue,

__skb_queue_tail(queue, skb);
} while (list);
+
+ spin_unlock_bh(&queue->lock);
}

bt_dev_dbg(hdev, "hcon %p queued %d", conn, skb_queue_len(queue));
--
2.43.0