From: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, kuba@kernel.org, edumazet@kernel.org,
pabeni@redhat.com, horms@kernel.org,
steffen.klassert@secunet.com,
Willem de Bruijn <willemb@google.com>,
stable@vger.kernel.org, Hassan Sultan <hassansultan@google.com>
Subject: [PATCH net] net: gso: fall back to skb_segment when fraglist cannot satisfy checksum
Date: Wed, 11 Nov 2026 17:10:33 -0500 [thread overview]
Message-ID: <20261111221129.829218-1-willemdebruijn.kernel@gmail.com> (raw)
From: Willem de Bruijn <willemb@google.com>
skb_segment_list() keeps the original transport headers and checksum
state of its frag_list members. It ignores the checksum bits in features
and does not initialize the SKB_GSO_CB() checksum state. This breaks in
two cases:
1. Outer tunnel checksums. __skb_udp_tunnel_segment() and
gre_gso_segment() compute them with gso_make_checksum(), which
reads SKB_GSO_CB() fields that hold stale skb->cb contents. The
outer checksum also needs the inner payload sum, which
skb_segment_list() does not compute.
2. CHECKSUM_PARTIAL packets, for example from veth or tun, sent to a
device that cannot checksum them. skb_segment_list() does not fix
this up, unlike skb_segment().
Fall back to more generic skb_segment() in these cases, as already done
for modified geometry and SKB_GSO_DODGY. Expand to when
- skb->encapsulation is set (IPIP, ESP)
- skb_tnl_header_len() is non-zero (UDP, GRE and MPLS tunnels)
- the features cannot checksum a CHECKSUM_PARTIAL packet.
skb_segment is a strict superset of skb_segment_list functionality.
This is always safe.
This also moves encapsulated packets that were handled correctly, such
as those in tunnels without outer checksums, to the slower
skb_segment() path. Fraglist GRO is off by default.
Discovered through analysis by an LLM. Reproduced on an unmodified
kernel and verified with LLM-generated reproducers in virtme-ng.
Fixes: 9fd1ff5d2ac7 ("udp: Support UDP fraglist GRO/GSO.")
Fixes: bee88cd5bd83 ("net: add support for segmenting TCP fraglist GSO packets")
Cc: stable@vger.kernel.org
Reported-by: Hassan Sultan <hassansultan@google.com>
Signed-off-by: Willem de Bruijn <willemb@google.com>
---
net/ipv4/tcp_offload.c | 5 ++++-
net/ipv4/udp_offload.c | 7 ++++++-
net/ipv6/tcpv6_offload.c | 6 +++++-
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
index b84fcd07c723..127035ba4faa 100644
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -108,7 +108,10 @@ static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
struct tcphdr *th = tcp_hdr(skb);
if ((skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size) &&
- !(skb_shinfo(skb)->gso_type & SKB_GSO_DODGY))
+ !(skb_shinfo(skb)->gso_type & SKB_GSO_DODGY) &&
+ !skb->encapsulation && !skb_tnl_header_len(skb) &&
+ (skb->ip_summed != CHECKSUM_PARTIAL ||
+ can_checksum_protocol(features, htons(ETH_P_IP))))
return __tcp4_gso_segment_list(skb, features);
skb->ip_summed = CHECKSUM_NONE;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index cd9d7426b227..8e9610d35083 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -516,7 +516,12 @@ struct sk_buff *__udp_gso_segment(struct sk_buff *gso_skb,
if (skb_shinfo(gso_skb)->gso_type & SKB_GSO_FRAGLIST) {
/* Detect modified geometry and pass those to skb_segment. */
if ((skb_pagelen(gso_skb) - sizeof(*uh) == skb_shinfo(gso_skb)->gso_size) &&
- !(skb_shinfo(gso_skb)->gso_type & SKB_GSO_DODGY))
+ !(skb_shinfo(gso_skb)->gso_type & SKB_GSO_DODGY) &&
+ !gso_skb->encapsulation && !skb_tnl_header_len(gso_skb) &&
+ (gso_skb->ip_summed != CHECKSUM_PARTIAL ||
+ can_checksum_protocol(features,
+ is_ipv6 ? htons(ETH_P_IPV6) :
+ htons(ETH_P_IP))))
return __udp_gso_segment_list(gso_skb, features, is_ipv6);
ret = __skb_linearize(gso_skb);
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
index eec3778855eb..5b830d244660 100644
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -9,6 +9,7 @@
#include <linux/skbuff.h>
#include <net/inet6_hashtables.h>
#include <net/gro.h>
+#include <net/gso.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/ip6_checksum.h>
@@ -180,7 +181,10 @@ static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
struct tcphdr *th = tcp_hdr(skb);
if ((skb_pagelen(skb) - th->doff * 4 == skb_shinfo(skb)->gso_size) &&
- !(skb_shinfo(skb)->gso_type & SKB_GSO_DODGY))
+ !(skb_shinfo(skb)->gso_type & SKB_GSO_DODGY) &&
+ !skb->encapsulation && !skb_tnl_header_len(skb) &&
+ (skb->ip_summed != CHECKSUM_PARTIAL ||
+ can_checksum_protocol(features, htons(ETH_P_IPV6))))
return __tcp6_gso_segment_list(skb, features);
skb->ip_summed = CHECKSUM_NONE;
--
2.56.0.567.gdb80ae8cd5-goog
next reply other threads:[~2026-11-11 22:12 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 22:10 Willem de Bruijn [this message]
2026-11-12 19:16 ` [PATCH net] net: gso: fall back to skb_segment when fraglist cannot satisfy checksum Jakub Kicinski
2026-11-12 20:27 ` Willem de Bruijn
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=20261111221129.829218-1-willemdebruijn.kernel@gmail.com \
--to=willemdebruijn.kernel@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=hassansultan@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@vger.kernel.org \
--cc=steffen.klassert@secunet.com \
--cc=willemb@google.com \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.