]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
batman-adv: frag: disallow unicast fragment in fragment
authorSven Eckelmann <sven@narfation.org>
Wed, 13 May 2026 07:01:36 +0000 (09:01 +0200)
committerSven Eckelmann <sven@narfation.org>
Fri, 15 May 2026 08:41:49 +0000 (10:41 +0200)
batadv_frag_skb_buffer() is called by batadv_batman_skb_recv() when a
BATADV_UNICAST_FRAG packet is received. Once all fragments are collected
and the packet is reassembled, batadv_recv_frag_packet() calls
batadv_batman_skb_recv() again to process the defragmented payload.

A malicious sender can craft a BATADV_UNICAST_FRAG packet whose reassembled
payload is itself a BATADV_UNICAST_FRAG packet (matryoshka-style nesting).
Each nesting level recurses through batadv_batman_skb_recv() without bound,
growing the kernel stack until it is exhausted.

Since refragmentation or fragments in fragments are not actually allowed,
discard all packets which are still BATADV_UNICAST_FRAG packets after the
defragmentation process.

Cc: stable@kernel.org
Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Reviewed-by: Yuan Tan <yuantan098@gmail.com>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
net/batman-adv/fragmentation.c

index 1152c2ce0c1ea2f004b0c165b977748fdab1c0bc..4a594aa2ebf6652fd7db76b13d1d5a9d788bdaad 100644 (file)
@@ -304,6 +304,31 @@ free:
        return skb_out;
 }
 
+/**
+ * batadv_skb_is_frag() - check if newly merged skb is gain a unicast packet
+ * @skb: newly merged skb
+ *
+ * Return: if newly skb is of type BATADV_UNICAST_FRAG
+ */
+static bool batadv_skb_is_frag(struct sk_buff *skb)
+{
+       struct batadv_ogm_packet *batadv_ogm_packet;
+
+       /* packet should hold at least type and version */
+       if (unlikely(!pskb_may_pull(skb, 2)))
+               return false;
+
+       batadv_ogm_packet = (struct batadv_ogm_packet *)skb->data;
+
+       if (batadv_ogm_packet->version != BATADV_COMPAT_VERSION)
+               return false;
+
+       if (batadv_ogm_packet->packet_type != BATADV_UNICAST_FRAG)
+               return false;
+
+       return true;
+}
+
 /**
  * batadv_frag_skb_buffer() - buffer fragment for later merge
  * @skb: skb to buffer
@@ -337,6 +362,16 @@ bool batadv_frag_skb_buffer(struct sk_buff **skb,
        if (!skb_out)
                goto out_err;
 
+       /* fragment in fragment is not allowed. otherwise it is possible
+        * to exhaust the stack when receiving a matryoshka-style
+        * "fragments in a fragment packet"
+        */
+       if (batadv_skb_is_frag(skb_out)) {
+               kfree_skb(skb_out);
+               skb_out = NULL;
+               goto out_err;
+       }
+
 out:
        ret = true;
 out_err: