From: Sven Eckelmann <[email protected]>

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]>
Reviewed-by: Yuan Tan <[email protected]>
Signed-off-by: Sven Eckelmann <[email protected]>
Signed-off-by: Simon Wunderlich <[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 1152c2ce0c1ea..4a594aa2ebf66 100644
--- a/net/batman-adv/fragmentation.c
+++ b/net/batman-adv/fragmentation.c
@@ -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:
-- 
2.47.3

Reply via email to