* [PATCH net v2] net: gro: avoid truncating transport header offsets
@ 2026-11-13 13:00 Hyunwoo Kim
0 siblings, 0 replies; only message in thread
From: Hyunwoo Kim @ 2026-11-13 13:00 UTC (permalink / raw)
To: David Ahern, Ido Schimmel, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Richard Gobert,
Willem de Bruijn
Cc: Kuniyuki Iwashima, netdev, imv4bel
skb->transport_header is a 16-bit offset from skb->head, and U16_MAX is
reserved as the unset transport header value. A long IPv6 extension
header chain can make the final offset exceed the field's range.
skb_set_transport_header() then truncates it.
In the UDP reproducer, UDP_GRO is enabled on the receiving socket and
the first packet is held on the GRO list. The truncated offset makes
udp_hdr(p) point to the source MAC address. The first four bytes of the
source MAC address match the UDP source and destination ports, allowing
the second packet in the same flow to pass the port comparison.
gro_receive_network_flush() then derives the first packet's IPv6 header
address from the truncated transport header offset. ipv6_gro_flush()
consequently reads from an address 65,536 bytes before the first packet's
actual IPv6 header. Both UDP and TCP GRO call
gro_receive_network_flush().
KASAN reports:
BUG: KASAN: use-after-free in udp_gro_receive+0x1f37/0x2680
Read of size 4 at addr ffff8881065f005e by task swapper/1/0
...
Call Trace:
<IRQ>
...
udp_gro_receive+0x1f37/0x2680
udp6_gro_receive+0x23e/0x11d0
ipv6_gro_receive+0x1200/0x2750
...
dev_gro_receive+0x1a7c/0x2600
gro_receive_skb+0x271/0x970
virtnet_receive_done+0x3b5/0x12c0
receive_buf+0x33a/0x4b40
...
virtnet_poll+0xc69/0x1980
...
__napi_poll+0xa0/0x530
net_rx_action+0x8ff/0x1280
...
</IRQ>
The out-of-bounds address was in a free page, so KASAN reported a
use-after-free. This classification reflects the state of that page,
not the lifetime of the skb. The invalid read was caused by truncation
of the 16-bit transport header offset.
The IP6_MAX_EXT_HDRS_CNT limit in ipv6_gro_pull_exthdrs() prevents this
reproducer. However, nested encapsulations can each add an extension
header chain, so the accumulated offset can still exceed the range of
skb->transport_header. inet_gro_receive() can also be reached with such
an offset.
Use skb_set_transport_header_careful() in ipv6_gro_receive() and
inet_gro_receive() to validate the final offset before assigning it. If
the offset cannot be represented, the skb is not held as a new GRO head
and is instead delivered through the normal receive path.
This issue was found through AI-based auditing.
Fixes: 4b0ebbca3e16 ("net: gro: move L3 flush checks to tcp_gro_receive and udp_gro_receive_segment")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Hyunwoo Kim <imv4bel@gmail.com>
---
Changes in v2:
- Base v2 on a tree with Eric's [1] patch applied. (Eric)
- Handle unrepresentable transport header offsets in inet_gro_receive(). (Eric)
- Explain why nested encapsulations still require the checked assignments. (Eric)
- State that the issue was found through AI-based auditing. (Eric)
- Change the subject prefix from ipv6: gro: to net: gro:.
- v1: https://lore.kernel.org/all/avTTQNyvwPP6LtGx@v4bel/
- [1]: https://lore.kernel.org/all/20261113124625.4010206-2-edumazet@kernel.org/
---
net/ipv4/af_inet.c | 5 ++++-
net/ipv6/ip6_offload.c | 3 ++-
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 8822bae439f88d..7db9b52662f6f5 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1530,7 +1530,10 @@ struct sk_buff *inet_gro_receive(struct list_head *head, struct sk_buff *skb)
* as we already checked checksum over ipv4 header was 0
*/
skb_gro_pull(skb, sizeof(*iph));
- skb_set_transport_header(skb, skb_gro_offset(skb));
+ if (!skb_set_transport_header_careful(skb, skb_gro_offset(skb))) {
+ flush = 1;
+ goto out;
+ }
pp = indirect_call_gro_receive(tcp4_gro_receive, udp4_gro_receive,
ops->callbacks.gro_receive, head, skb);
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 01642900c0fa33..fe38dc15faa67a 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -249,7 +249,8 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff *ipv6_gro_receive(struct list_head *head,
skb_gro_pull(skb, sizeof(*iph));
}
- skb_set_transport_header(skb, skb_gro_offset(skb));
+ if (!skb_set_transport_header_careful(skb, skb_gro_offset(skb)))
+ goto out;
NAPI_GRO_CB(skb)->proto = proto;
--
2.43.0
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-11-13 13:00 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-13 13:00 [PATCH net v2] net: gro: avoid truncating transport header offsets Hyunwoo Kim
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox