kernel: fix crash with multicast-to-unicast and fraglist GRO
[openwrt/openwrt.git] / target / linux / generic / pending-6.1 / 682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch
diff --git a/target/linux/generic/pending-6.1/682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch b/target/linux/generic/pending-6.1/682-net-core-reject-skb_copy-_expand-for-fraglist-GSO-sk.patch
new file mode 100644 (file)
index 0000000..719cac9
--- /dev/null
@@ -0,0 +1,59 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sat, 27 Apr 2024 19:29:45 +0200
+Subject: [PATCH] net: core: reject skb_copy(_expand) for fraglist GSO skbs
+
+SKB_GSO_FRAGLIST skbs must not be linearized, otherwise they become
+invalid. Return NULL if such an skb is passed to skb_copy or
+skb_copy_expand, in order to prevent a crash on a potential later
+call to skb_gso_segment.
+
+Fixes: 3a1296a38d0c ("net: Support GRO/GSO fraglist chaining.")
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/net/core/skbuff.c
++++ b/net/core/skbuff.c
+@@ -1720,11 +1720,17 @@ static inline int skb_alloc_rx_flag(cons
+ struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
+ {
+-      int headerlen = skb_headroom(skb);
+-      unsigned int size = skb_end_offset(skb) + skb->data_len;
+-      struct sk_buff *n = __alloc_skb(size, gfp_mask,
+-                                      skb_alloc_rx_flag(skb), NUMA_NO_NODE);
++      struct sk_buff *n;
++      unsigned int size;
++      int headerlen;
++      if (WARN_ON_ONCE(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST))
++              return NULL;
++
++      headerlen = skb_headroom(skb);
++      size = skb_end_offset(skb) + skb->data_len;
++      n = __alloc_skb(size, gfp_mask,
++                      skb_alloc_rx_flag(skb), NUMA_NO_NODE);
+       if (!n)
+               return NULL;
+@@ -2037,12 +2043,17 @@ struct sk_buff *skb_copy_expand(const st
+       /*
+        *      Allocate the copy buffer
+        */
+-      struct sk_buff *n = __alloc_skb(newheadroom + skb->len + newtailroom,
+-                                      gfp_mask, skb_alloc_rx_flag(skb),
+-                                      NUMA_NO_NODE);
+-      int oldheadroom = skb_headroom(skb);
+       int head_copy_len, head_copy_off;
++      struct sk_buff *n;
++      int oldheadroom;
++
++      if (WARN_ON_ONCE(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST))
++              return NULL;
++      oldheadroom = skb_headroom(skb);
++      n = __alloc_skb(newheadroom + skb->len + newtailroom,
++                      gfp_mask, skb_alloc_rx_flag(skb),
++                      NUMA_NO_NODE);
+       if (!n)
+               return NULL;