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