batman-adv: frag: disallow unicast fragment in fragment

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: 610bfc6bc9 ("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>
This commit is contained in:
Sven Eckelmann 2026-05-13 09:01:36 +02:00
parent 6c65cf23d4
commit bc62216dc8
No known key found for this signature in database
GPG Key ID: 4D0F772BD314F5CB

View File

@ -304,6 +304,31 @@ batadv_frag_merge_packets(struct hlist_head *chain)
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: