From: Ting-Han Hou <ue081723@gmail.com>
To: stable@vger.kernel.org
Cc: Zihan Xi <zihanx@nebusec.ai>, Vega <vega@nebusec.ai>,
Luxing Yin <root@tr0jan.top>,
Willem de Bruijn <willemb@google.com>,
Paolo Abeni <pabeni@redhat.com>
Subject: [PATCH 6.18.y] net: gso: limit recursive IP-in-IP segmentation
Date: Sat, 24 Oct 2026 20:27:01 +0800 [thread overview]
Message-ID: <20261024122701.554-1-ue081723@gmail.com> (raw)
In-Reply-To: <2026100305-credible-reference-2a7c@gregkh>
From: Zihan Xi <zihanx@nebusec.ai>
[ Upstream commit 006c8b834aed8b02bd8fb9fb05e9e7ead37d1763 ]
IP-in-IP GSO can re-enter inet_gso_segment() or ipv6_gso_segment()
for each nested IP header. encap_level tracks header bytes, not callback
depth, so a deep chain can exhaust the kernel stack. Making
inet_gso_segment() stackable introduced unbounded IPv4 nesting; IPIP
GSO/TSO later made the path reachable. The IPv6 stackable path was
introduced separately and uses the same guard.
Count IPv4 and IPv6 GSO handler entries in skb_gso_cb, initialized once
per top-level GSO operation and preserved across GRE/UDP context changes.
Use the existing IP_TUNNEL_RECURSION_LIMIT for both handlers. The first
five entries pass, and the sixth returns -EINVAL before dispatching
another GSO callback.
Fixes: 3347c9602955 ("ipv4: gso: make inet_gso_segment() stackable")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Closes: https://lore.kernel.org/all/cover.1790157745.git.zihanx@nebusec.ai/
Assisted-by: LLM
Co-developed-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Luxing Yin <root@tr0jan.top>
Signed-off-by: Zihan Xi <zihanx@nebusec.ai>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Link: https://patch.msgid.link/20260924051521.32568-2-zihanx@nebusec.ai
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
[ Ting-Han: ipv6_gso_segment() still calls ipv6_hopopt_jumbo_remove()
right after skb_reset_network_header() in this version; keep the new
recursion check directly after skb_reset_network_header() as upstream
does, before the jumbo removal ]
Signed-off-by: Ting-Han Hou <ue081723@gmail.com>
---
Tested on top of v6.18.58. The patch also applies cleanly on top of
the current 6.18 queue (6.18.59-rc1).
x86_64 defconfig + kvm_guest.config with KASAN, PROVE_LOCKING,
DEBUG_ATOMIC_SLEEP, DEBUG_LIST and UBSAN, built with and without the
patch and booted in QEMU/KVM. The guest sends 32 MiB of TCP over a
veth pair, over one ipip tunnel, over one ip6ip6 tunnel and over ipip
in ipip, with tx-ipxip4/6-segmentation turned off on the veth pair so
that the tunnel GSO packets are segmented in software. With and
without the patch all data arrives intact, there are no drops or
kernel warnings, and kprobes show two inet_gso_segment() or
ipv6_gso_segment() calls per __skb_gso_segment() call in the tunnel
cases, so these packets reach a count of 2 in the new check. No new
W=1 or sparse warnings in the changed .c files.
Not tested: the deep nesting reproducer from the upstream report, so
the -EINVAL path itself was not exercised.
Tool use: this backport was prepared with an LLM coding assistant
(Claude Code with Claude Opus 5.5), asked to backport 006c8b834aed to
the stable trees it failed to apply to and to test it. It resolved
the conflicts, wrote the note in the changelog and this text, wrote
the test scripts and the guest init, and ran the builds and QEMU tests
above. I reviewed the patch and the test results before sending.
include/net/gso.h | 8 ++++++++
net/core/gso.c | 1 +
net/ipv4/af_inet.c | 3 +++
net/ipv6/ip6_offload.c | 4 ++++
4 files changed, 16 insertions(+)
diff --git a/include/net/gso.h b/include/net/gso.h
index 29975440cad51..0749230d414ec 100644
--- a/include/net/gso.h
+++ b/include/net/gso.h
@@ -19,10 +19,18 @@ struct skb_gso_cb {
int encap_level;
__wsum csum;
__u16 csum_start;
+ /* Number of IPv4/IPv6 GSO handler entries for this packet. */
+ u8 recursion_counter;
};
#define SKB_GSO_CB_OFFSET 32
#define SKB_GSO_CB(skb) ((struct skb_gso_cb *)((skb)->cb + SKB_GSO_CB_OFFSET))
+static inline bool gso_recursion_inc_test(struct sk_buff *skb,
+ unsigned int limit)
+{
+ return ++SKB_GSO_CB(skb)->recursion_counter > limit;
+}
+
static inline int skb_tnl_header_len(const struct sk_buff *inner_skb)
{
return (skb_mac_header(inner_skb) - inner_skb->head) -
diff --git a/net/core/gso.c b/net/core/gso.c
index bcd156372f4df..e96ef63500648 100644
--- a/net/core/gso.c
+++ b/net/core/gso.c
@@ -117,6 +117,7 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb,
SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb);
SKB_GSO_CB(skb)->encap_level = 0;
+ SKB_GSO_CB(skb)->recursion_counter = 0;
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 1d1a03fad1f9f..5f6f6019b298e 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1364,6 +1364,9 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb,
int id;
skb_reset_network_header(skb);
+ if (unlikely(gso_recursion_inc_test(skb,
+ IP_TUNNEL_RECURSION_LIMIT)))
+ goto out;
nhoff = skb_network_header(skb) - skb_mac_header(skb);
if (unlikely(!pskb_may_pull(skb, sizeof(*iph))))
goto out;
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index fce91183797a6..db71d557d6f5a 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -17,6 +17,7 @@
#include <net/udp.h>
#include <net/gro.h>
#include <net/gso.h>
+#include <net/ip_tunnels.h>
#include "ip6_offload.h"
@@ -120,6 +121,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
bool gso_partial;
skb_reset_network_header(skb);
+ if (unlikely(gso_recursion_inc_test(skb,
+ IP_TUNNEL_RECURSION_LIMIT)))
+ goto out;
err = ipv6_hopopt_jumbo_remove(skb);
if (err)
return ERR_PTR(err);
base-commit: 2926d88bb7fc4608fd942453d929c39e4e5ec006
--
2.53.0
prev parent reply other threads:[~2026-10-24 12:27 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-03 13:17 FAILED: patch "[PATCH] net: gso: limit recursive IP-in-IP segmentation" failed to apply to 6.18-stable tree gregkh
2026-10-24 12:27 ` Ting-Han Hou [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261024122701.554-1-ue081723@gmail.com \
--to=ue081723@gmail.com \
--cc=pabeni@redhat.com \
--cc=root@tr0jan.top \
--cc=stable@vger.kernel.org \
--cc=vega@nebusec.ai \
--cc=willemb@google.com \
--cc=zihanx@nebusec.ai \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox