Thread (6 messages) 6 messages, 4 authors, 20d ago

Re: [PATCH net 1/1] net: gso: limit recursive IP-in-IP segmentation

From: zihan xi <hidden>
Date: 2026-09-14 03:43:27
Also in: lkml, stable

On Mon, Sep 14, 2026 at 7:08 AM Eric Dumazet [off-list ref] wrote:
On Sun, Sep 13, 2026 at 3:46 PM Willem de Bruijn
[off-list ref] wrote:
quoted
Zihan Xi wrote:
quoted
IPIP GSO/TSO support makes IP-in-IP GSO dispatch re-enter
inet_gso_segment() or ipv6_gso_segment() for every nested IP header. The
only state that tracks this nesting is encap_level, which records header
bytes and has no recursion bound. A sufficiently deep chain can consume the
kernel stack before a transport GSO callback is reached.

The unbounded callback nesting was introduced when inet_gso_segment() was
made stackable by "ipv4: gso: make inet_gso_segment() stackable". GRE GSO
support predated that change, and IP-in-IP GSO/TSO support later made the
affected path reachable.

Track the number of IP GSO callbacks in skb_gso_cb and reject the 15th
callback entry. Thus 14 callback entries are allowed to complete;
GSO_RECURSION_LIMIT is the rejection threshold, not the number of
successful callbacks. Initialize the counter for each top-level GSO
operation and check it in both IPv4 and IPv6 handlers so mixed IP-in-IP
nesting is bounded.

Fixes: 3347c9602955 ("ipv4: gso: make inet_gso_segment() stackable")
Cc: stable@vger.kernel.org
Reported-by: Vega <redacted>
Assisted-by: LLM
Co-developed-by: Luxing Yin <redacted>
Signed-off-by: Luxing Yin <redacted>
Signed-off-by: Zihan Xi <redacted>
---
 include/net/gso.h      | 9 +++++++++
 net/core/gso.c         | 1 +
 net/ipv4/af_inet.c     | 3 +++
 net/ipv6/ip6_offload.c | 3 +++
 4 files changed, 16 insertions(+)
diff --git a/include/net/gso.h b/include/net/gso.h
index 29975440cad5..2665acbb9205 100644
--- a/include/net/gso.h
+++ b/include/net/gso.h
@@ -19,10 +19,19 @@ struct skb_gso_cb {
      int     encap_level;
      __wsum  csum;
      __u16   csum_start;
+     /* Number of GSO callbacks this packet already went through. */
+     u8      recursion_counter;
 };
 #define SKB_GSO_CB_OFFSET    32
 #define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_GSO_CB_OFFSET))

+#define GSO_RECURSION_LIMIT  15      /* First callback depth to reject. */
+static inline int gso_recursion_inc_test(struct sk_buff *skb)
What is 15 based on? Is that where in your test stack overflow occurs?

A realistic practical limit would likely already be smaller.
An alternative would be to limit total header sizes (nhoff) to 256 bytes or so.
Thanks for the feedback.
What is 15 based on? Is that where in your test stack overflow occurs?
No. The reproducer uses a much deeper header chain, and the test did not
establish 15 as a meaningful boundary. It was an arbitrary callback-depth
limit in the initial version, so I have removed that approach.
An alternative would be to limit total header sizes (nhoff) to 256 bytes or so.
Following this suggestion, I changed the fix to bound the cumulative nhoff
in both inet_gso_segment() and ipv6_gso_segment(). If nhoff exceeds 256
bytes, the existing -EINVAL path is used.

This bounds recursive IP GSO by cumulative header size rather than by an
assumed callback count, without adding state to skb_gso_cb. The 256-byte
value is a practical upper bound on the cumulative header offset, not a
measured stack-budget threshold.

I am rebuilding this revision and will rerun the reproducer against both
the fixed and unpatched kernels before sending the next version.

Thanks,
Zihan
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help