]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: ISO: fix malformed ISO_END/CONT handling
authorPauli Virtanen <pav@iki.fi>
Wed, 1 Jul 2026 15:46:38 +0000 (18:46 +0300)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 6 Jul 2026 14:46:58 +0000 (10:46 -0400)
Core specification (Part C vol 4 sec 5.4.5) does not exclude empty
ISO_CONT, ISO_END packets.  We currently reject them if they are last.

If controller sends malformed sequence

    ISO_START -> rx_len = 4, ISO_CONT skb->len 4, ISO_START

that ends payload in ISO_CONT, we leak conn->rx_skb. If controller sends
too long ISO_END, we panic on skb_put. If controller sends too short
ISO_END we accept it.

Fix by marking unfinished ISO_START via conn->rx_skb != NULL.  Check
skb->len properly before skb_put.  Combine the ISO_CONT/END code paths
as they require the same initial checks. Reject too short ISO_END
packets.

Fixes: 84c24fb151fc ("Bluetooth: ISO: drop ISO_END frames received without prior ISO_START")
Fixes: ccf74f2390d6 ("Bluetooth: Add BTPROTO_ISO socket type")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/iso.c

index cd7c7c9ea4fc616f2941acf1c518e04c806ee6f3..2e95a153912c5de477d747fe0188b39f05f7a538 100644 (file)
@@ -2540,7 +2540,7 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
        switch (pb) {
        case ISO_START:
        case ISO_SINGLE:
-               if (conn->rx_len) {
+               if (conn->rx_skb || conn->rx_len) {
                        BT_ERR("Unexpected start frame (len %d)", skb->len);
                        kfree_skb(conn->rx_skb);
                        conn->rx_skb = NULL;
@@ -2621,12 +2621,14 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
                break;
 
        case ISO_CONT:
-               BT_DBG("Cont: frag len %d (expecting %d)", skb->len,
+       case ISO_END:
+               BT_DBG("%s: frag len %d (expecting %d)",
+                      (pb == ISO_END) ? "End" : "Cont", skb->len,
                       conn->rx_len);
 
-               if (!conn->rx_len) {
-                       BT_ERR("Unexpected continuation frame (len %d)",
-                              skb->len);
+               if (!conn->rx_skb) {
+                       BT_ERR("Unexpected ISO %s frame (len %d)",
+                              (pb == ISO_END) ? "End" : "Cont", skb->len);
                        goto drop;
                }
 
@@ -2642,17 +2644,9 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
                skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
                                          skb->len);
                conn->rx_len -= skb->len;
-               break;
 
-       case ISO_END:
-               if (!conn->rx_len) {
-                       BT_ERR("Unexpected end frame (len %d)", skb->len);
-                       goto drop;
-               }
-
-               skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
-                                         skb->len);
-               conn->rx_len -= skb->len;
+               if (pb == ISO_CONT)
+                       break;
 
                if (!conn->rx_len) {
                        struct sk_buff *rx_skb = conn->rx_skb;
@@ -2663,6 +2657,13 @@ int iso_recv(struct hci_dev *hdev, u16 handle, struct sk_buff *skb, u16 flags)
                         */
                        conn->rx_skb = NULL;
                        iso_recv_frame(conn, rx_skb);
+               } else {
+                       BT_ERR("ISO fragment incomplete (len %d, expected %d)",
+                              skb->len, conn->rx_len);
+                       kfree_skb(conn->rx_skb);
+                       conn->rx_skb = NULL;
+                       conn->rx_len = 0;
+                       goto drop;
                }
                break;
        }