* [PATCH net 0/2] net: gro: fix frag0 and remcsum handling
@ 2026-10-28 14:13 Eric Dumazet
2026-10-28 14:13 ` [PATCH net 1/2] net: gro: do not undo remote checksum offload on merged packets Eric Dumazet
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Eric Dumazet @ 2026-10-28 14:13 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Willem de Bruijn, Steffen Klassert,
AutonomousCodeSecurity, netdev, edumazet
Two GRO fixes for paths using remote checksum offload (VXLAN, GUE)
and for NAPI_GRO_CB(skb)->frag0 after a pull.
Patch 1 skips skb_gro_remcsum_cleanup() once the skb was merged,
as skb->data was moved past the headers.
Patch 2 invalidates frag0 after a pull, which commit bd56a29c7a4e
("net: gro: change skb_gro_network_header()") stopped doing.
Eric Dumazet (2):
net: gro: do not undo remote checksum offload on merged packets
net: gro: invalidate frag0 after a pull
include/net/gro.h | 15 ++++++++++++++-
net/core/gro.c | 1 +
net/ipv4/esp4_offload.c | 1 +
net/ipv4/udp_offload.c | 1 +
net/ipv6/esp6_offload.c | 1 +
5 files changed, 18 insertions(+), 1 deletion(-)
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH net 1/2] net: gro: do not undo remote checksum offload on merged packets
2026-10-28 14:13 [PATCH net 0/2] net: gro: fix frag0 and remcsum handling Eric Dumazet
@ 2026-10-28 14:13 ` Eric Dumazet
2026-10-28 14:13 ` [PATCH net 2/2] net: gro: invalidate frag0 after a pull Eric Dumazet
2026-10-28 14:15 ` [PATCH net 0/2] net: gro: fix frag0 and remcsum handling netdev-bot+sinfo
2 siblings, 0 replies; 4+ messages in thread
From: Eric Dumazet @ 2026-10-28 14:13 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Willem de Bruijn, Steffen Klassert,
AutonomousCodeSecurity, netdev, edumazet, Cen Zhang
skb_gro_flush_final_remcsum() calls skb_gro_remcsum_cleanup() to undo
the checksum adjustment done by skb_gro_remcsum_process() when the
packet was not consumed by GRO.
When the inner handler merged the packet (NAPI_GRO_CB(skb)->same_flow
is set), skb->data might have been moved past the headers:
- skb_gro_receive() uses __skb_pull(skb, offset) before adding the skb
to the frag_list of the held packet.
- skb_gro_receive_list() uses skb_pull(skb, skb_gro_offset(skb)).
grc->offset is relative to the original skb->data, so the 2-byte
read-modify-write done by skb_gro_remcsum_cleanup() can land in
the payload of a packet which now belongs to the GRO packet.
pskb_may_pull() can also be called on this frag_list member.
With fraglist GRO, this can also be a use-after-free:
pskb_may_pull(skb, skb_gro_offset(skb)) in skb_gro_receive_list()
or udp_gro_receive_segment() releases the first page fragment if it
only contains headers. NAPI_GRO_CB(skb)->frag0 and frag0_len are not
updated, so skb_gro_remcsum_cleanup() takes the fast path and writes
through frag0 into a page the skb no longer holds a reference on.
The non merged case, where frag0 is used after this pull, is handled
in the following patch.
Headers of a merged skb are not used anymore: the skb is either freed,
or its headers are rebuilt from the head skb in skb_segment() or
skb_segment_list(). Skip the cleanup in this case.
This affects VXLAN and GUE with remote checksum offload enabled.
Fixes: 26c4f7da3e41 ("net: Fix remcsum in GRO path to not change packet")
Tested-by: Cen Zhang <cenzhang@linux.microsoft.com>
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
---
include/net/gro.h | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/include/net/gro.h b/include/net/gro.h
index 2300b6da05b2728ec40f42228f8fa9c195d8479c..b560e866683b03314b3e80a887ac4467d1a5acb9 100644
--- a/include/net/gro.h
+++ b/include/net/gro.h
@@ -351,7 +351,10 @@ static inline void skb_gro_remcsum_cleanup(struct sk_buff *skb,
void *ptr;
size_t plen = grc->offset + sizeof(u16);
- if (!grc->delta)
+ /* Once merged, skb->data was moved past the headers, and
+ * the headers of this skb will not be used anymore.
+ */
+ if (!grc->delta || NAPI_GRO_CB(skb)->same_flow)
return;
ptr = skb_gro_header(skb, plen, grc->offset);
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH net 2/2] net: gro: invalidate frag0 after a pull
2026-10-28 14:13 [PATCH net 0/2] net: gro: fix frag0 and remcsum handling Eric Dumazet
2026-10-28 14:13 ` [PATCH net 1/2] net: gro: do not undo remote checksum offload on merged packets Eric Dumazet
@ 2026-10-28 14:13 ` Eric Dumazet
2026-10-28 14:15 ` [PATCH net 0/2] net: gro: fix frag0 and remcsum handling netdev-bot+sinfo
2 siblings, 0 replies; 4+ messages in thread
From: Eric Dumazet @ 2026-10-28 14:13 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Willem de Bruijn, Steffen Klassert,
AutonomousCodeSecurity, netdev, edumazet, Taesoo Kim, Cen Zhang
Since commit bd56a29c7a4e ("net: gro: change skb_gro_network_header()"),
skb_gro_header_slow() no longer invalidates NAPI_GRO_CB(skb)->frag0
after pskb_may_pull(). skb_gro_may_pull() keeps comparing against the
frag0_len value set in skb_gro_reset_offset(), assuming that after a
slow pull, all later accesses are beyond frag0_len.
This is not true:
- udp4_gro_receive() and udp6_gro_receive() read the network header
with skb_gro_network_header() after udp_gro_udphdr() pulled the
UDP header.
- vxlan_gro_receive() calls skb_gro_remcsum_cleanup() at a lower
offset after the inner handlers pulled their headers.
- skb_gro_remcsum_process() can pull beyond the inner Ethernet header
before eth_gro_receive() reads it.
- napi_frags_skb() calls __skb_pull(skb, ETH_HLEN) after a slow pull,
so frag0 still points to the Ethernet header.
If the pull consumed the first page fragment, __skb_pull_tail() released
it. If the head had to grow, pskb_expand_head() freed the old one.
These reads, and the 2-byte write in skb_gro_remcsum_cleanup(), then
access memory the skb no longer holds a reference on.
esp4_gro_receive(), esp6_gro_receive(), skb_gro_receive_list() and
udp_gro_receive_segment() also pull directly and never invalidated
frag0.
Add skb_gro_frag0_invalidate(), which only clears frag0_len, and call
it after each of these pulls. This is a single store in the slow path,
and frag0 stays non NULL, so skb_gro_network_header() does not need
a NULL test.
Fixes: bd56a29c7a4e ("net: gro: change skb_gro_network_header()")
Reported-by: Taesoo Kim <tsgatesv@gmail.com>
Reported-by: Cen Zhang <cenzhang@linux.microsoft.com>
Tested-by: Cen Zhang <cenzhang@linux.microsoft.com>
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
---
include/net/gro.h | 10 ++++++++++
net/core/gro.c | 1 +
net/ipv4/esp4_offload.c | 1 +
net/ipv4/udp_offload.c | 1 +
net/ipv6/esp6_offload.c | 1 +
5 files changed, 14 insertions(+)
diff --git a/include/net/gro.h b/include/net/gro.h
index b560e866683b03314b3e80a887ac4467d1a5acb9..78373e9caf68b6acefcd976ed7192ec5e44db2b9 100644
--- a/include/net/gro.h
+++ b/include/net/gro.h
@@ -160,12 +160,22 @@ static inline bool skb_gro_may_pull(const struct sk_buff *skb,
return likely(hlen <= NAPI_GRO_CB(skb)->frag0_len);
}
+/* A pull can release the page fragment frag0 points to, or reallocate
+ * skb->head. Make sure later skb_gro_header() and skb_gro_network_header()
+ * calls no longer use frag0.
+ */
+static inline void skb_gro_frag0_invalidate(struct sk_buff *skb)
+{
+ NAPI_GRO_CB(skb)->frag0_len = 0;
+}
+
static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen,
unsigned int offset)
{
if (!pskb_may_pull(skb, hlen))
return NULL;
+ skb_gro_frag0_invalidate(skb);
return skb->data + offset;
}
diff --git a/net/core/gro.c b/net/core/gro.c
index ff828a3dce3ca649e149a840bd6e3dfd9ddaf0c4..2fe1bbe49a082873a700d36f7a10570f0954526e 100644
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -242,6 +242,7 @@ int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
NAPI_GRO_CB(skb)->flush = 1;
return -ENOMEM;
}
+ skb_gro_frag0_invalidate(skb);
if (NAPI_GRO_CB(p)->last == p)
skb_shinfo(p)->frag_list = skb;
diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c
index abd77162f5e75ec01d5e66693f20a80a78af9e50..dea34411e77f6cf1302315e665df003e4d1130a6 100644
--- a/net/ipv4/esp4_offload.c
+++ b/net/ipv4/esp4_offload.c
@@ -39,6 +39,7 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,
if (!pskb_pull(skb, offset))
return NULL;
+ skb_gro_frag0_invalidate(skb);
if (xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq) != 0)
goto out;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index cf07c3c6611a22c29b57a72420f2f940609f5e9a..62c44c450d78c9c2b9ea38d953376026fd2aa4e7 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -747,6 +747,7 @@ static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
NAPI_GRO_CB(skb)->flush = 1;
return NULL;
}
+ skb_gro_frag0_invalidate(skb);
if ((skb->ip_summed != p->ip_summed) ||
(skb->csum_level != p->csum_level)) {
NAPI_GRO_CB(skb)->flush = 1;
diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c
index 22895521a57d09acfe091927f20188764f33f91b..674e66541d9f914bf788a148bceea51c1067f541 100644
--- a/net/ipv6/esp6_offload.c
+++ b/net/ipv6/esp6_offload.c
@@ -66,6 +66,7 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
if (!pskb_pull(skb, offset))
return NULL;
+ skb_gro_frag0_invalidate(skb);
if (xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq) != 0)
goto out;
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net 0/2] net: gro: fix frag0 and remcsum handling
2026-10-28 14:13 [PATCH net 0/2] net: gro: fix frag0 and remcsum handling Eric Dumazet
2026-10-28 14:13 ` [PATCH net 1/2] net: gro: do not undo remote checksum offload on merged packets Eric Dumazet
2026-10-28 14:13 ` [PATCH net 2/2] net: gro: invalidate frag0 after a pull Eric Dumazet
@ 2026-10-28 14:15 ` netdev-bot+sinfo
2 siblings, 0 replies; 4+ messages in thread
From: netdev-bot+sinfo @ 2026-10-28 14:15 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
Willem de Bruijn, Steffen Klassert, AutonomousCodeSecurity,
netdev
Hi!
This is an automated message. This series looks like a fix, but its
commit messages seem to be missing some information:
- How the issue was discovered, e.g. hit in production, hit during
development, syzbot report, manual code inspection, LLM or static
analysis tool scan.
- Whether the issue was actually triggered, or is only theoretical
(e.g. found by code inspection). If it was triggered please include
the symptoms, like the stack trace or error messages.
Please do not repost the series just to address the above. Instead,
reply to this email with the missing information, so that reviewers
can take it into account. If the series needs another revision for
other reasons, please include the information in the commit messages
then.
The evaluation is done by an LLM so it may be wrong, if you think
that is the case please reply and explain.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-28 14:15 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 14:13 [PATCH net 0/2] net: gro: fix frag0 and remcsum handling Eric Dumazet
2026-10-28 14:13 ` [PATCH net 1/2] net: gro: do not undo remote checksum offload on merged packets Eric Dumazet
2026-10-28 14:13 ` [PATCH net 2/2] net: gro: invalidate frag0 after a pull Eric Dumazet
2026-10-28 14:15 ` [PATCH net 0/2] net: gro: fix frag0 and remcsum handling netdev-bot+sinfo
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox