]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Bluetooth: bnep: fix incorrect length parsing in bnep_rx_frame() extension handling
authorDudu Lu <phx0fer@gmail.com>
Wed, 15 Apr 2026 09:39:53 +0000 (17:39 +0800)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 6 May 2026 20:19:09 +0000 (16:19 -0400)
In bnep_rx_frame(), the BNEP_FILTER_NET_TYPE_SET and
BNEP_FILTER_MULTI_ADDR_SET extension header parsing has two bugs:

1) The 2-byte length field is read with *(u16 *)(skb->data + 1), which
   performs a native-endian read. The BNEP protocol specifies this field
   in big-endian (network byte order), and the same file correctly uses
   get_unaligned_be16() for the identical fields in
   bnep_ctrl_set_netfilter() and bnep_ctrl_set_mcfilter().

2) The length is multiplied by 2, but unlike BNEP_SETUP_CONN_REQ where
   the length byte counts UUID pairs (requiring * 2 for two UUIDs per
   entry), the filter extension length field already represents the total
   data size in bytes. This is confirmed by bnep_ctrl_set_netfilter()
   which reads the same field as a byte count and divides by 4 to get
   the number of filter entries.

   The bogus * 2 means skb_pull advances twice as far as it should,
   either dropping valid data from the next header or causing the pull
   to fail entirely when the doubled length exceeds the remaining skb.

Fix by splitting the pull into two steps: first use skb_pull_data() to
safely pull and validate the 3-byte fixed header (ctrl type + length),
then pull the variable-length data using the properly decoded length.

Fixes: bf8b9a9cb77b ("Bluetooth: bnep: Add support to extended headers of control frames")
Signed-off-by: Dudu Lu <phx0fer@gmail.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/bnep/core.c

index d44987d4515c0b6eb4766b40e25b4ba5b689584b..853c8d7644b5580600e9a8c4b5e0f2be4328f726 100644 (file)
@@ -330,11 +330,18 @@ static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
                                goto badframe;
                        break;
                case BNEP_FILTER_MULTI_ADDR_SET:
-               case BNEP_FILTER_NET_TYPE_SET:
-                       /* Pull: ctrl type (1 b), len (2 b), data (len bytes) */
-                       if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2))
+               case BNEP_FILTER_NET_TYPE_SET: {
+                       u8 *hdr;
+
+                       /* Pull ctrl type (1 b) + len (2 b) */
+                       hdr = skb_pull_data(skb, 3);
+                       if (!hdr)
+                               goto badframe;
+                       /* Pull data (len bytes); length is big-endian */
+                       if (!skb_pull(skb, get_unaligned_be16(&hdr[1])))
                                goto badframe;
                        break;
+               }
                default:
                        kfree_skb(skb);
                        return 0;