[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251225091921.14860-1-swilczek.lx@gmail.com>
Date: Thu, 25 Dec 2025 10:19:21 +0100
From: Szymon Wilczek <swilczek.lx@...il.com>
To: Marcel Holtmann <marcel@...tmann.org>,
Luiz Augusto von Dentz <luiz.dentz@...il.com>
Cc: linux-bluetooth@...r.kernel.org,
linux-kernel@...r.kernel.org,
syzbot+4d6b203d625d2f57d4ca@...kaller.appspotmail.com,
Szymon Wilczek <swilczek.lx@...il.com>
Subject: [PATCH v2] Bluetooth: vhci: Fix slab-use-after-free by cloning skb
Hillf Danton pointed out that the root cause of the UAF issue is the
lack of isolation between hci_core and vhci driver consumers.
vhci_send_frame() modifies the skb (via skb_push) and queues the
original skb to the readq for userspace consumption. This means the
hci_core caller sees a modified skb (corrupted headers/data pointer)
if it retains any reference. Furthermore, if vhci_read() frees the
skb immediately, hci_core might hit a Use-After-Free.
Other drivers (like btusb) create a new URB and context, isolating
the skb lifetime.
Fix this by cloning the skb in vhci_send_frame() before queueing.
The clone is modified and queued. The original skb is freed
immediately, satisfying the HCI driver contract to consume the skb
while ensuring the queued object is distinct from the one passed by
hci_core.
Reported-by: syzbot+4d6b203d625d2f57d4ca@...kaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=4d6b203d625d2f57d4ca
Signed-off-by: Szymon Wilczek <swilczek.lx@...il.com>
---
drivers/bluetooth/hci_vhci.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 2fef08254d78..f2901a4b5b3a 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -74,13 +74,20 @@ static int vhci_flush(struct hci_dev *hdev)
static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
struct vhci_data *data = hci_get_drvdata(hdev);
+ struct sk_buff *nskb;
- memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (!nskb)
+ return -ENOMEM;
+
+ memcpy(skb_push(nskb, 1), &hci_skb_pkt_type(skb), 1);
- skb_queue_tail(&data->readq, skb);
+ skb_queue_tail(&data->readq, nskb);
if (atomic_read(&data->initialized))
wake_up_interruptible(&data->read_wait);
+
+ kfree_skb(skb);
return 0;
}
--
2.52.0
Powered by blists - more mailing lists