On Wed, May 13, 2026 at 12:01 AM Sven Eckelmann <[email protected]> wrote:
>
> 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: [email protected]
> Fixes: 610bfc6bc99b ("batman-adv: Receive fragmented packets and merge")
> Reported-by: Yuan Tan <[email protected]>
> Reported-by: Yifan Wu <[email protected]>
> Reported-by: Juefei Pu <[email protected]>
> Reported-by: Xin Liu <[email protected]>
> Signed-off-by: Sven Eckelmann <[email protected]>
> ---
>  net/batman-adv/fragmentation.c | 35 +++++++++++++++++++++++++++++++++++
>  1 file changed, 35 insertions(+)
>
> diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c
> index f4e45cc2..42f3d4ee 100644
> --- a/net/batman-adv/fragmentation.c
> +++ b/net/batman-adv/fragmentation.c
> @@ -293,6 +293,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
> @@ -326,6 +351,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:
>
> --
> 2.47.3
>

Reviewed-by: Yuan Tan <[email protected]>

Reply via email to