* [PATCH net] net: iptunnel: fix stale transport header during tunnel decapsulation
@ 2026-08-13 3:38 Dong Chenchen
2026-08-13 4:39 ` Eric Dumazet
0 siblings, 1 reply; 2+ messages in thread
From: Dong Chenchen @ 2026-08-13 3:38 UTC (permalink / raw)
To: davem, edumazet, pabeni, kuba, horms, herbert, kuniyu
Cc: idosch, andrew+netdev, ap420073, steffen.klassert, laforge,
jiayuan.chen, jhs, zhangchangzhong, netdev, Dong Chenchen,
syzbot+83181a31faf9455499c5
Syzbot reported a crash in qdisc_pkt_len_segs_init() caused by a stale
transport_header offset after tunnel decapsulation.
BUG: unable to handle page fault for address: ffffed102091a42e
Oops: Oops: 0000 [#1] SMP KASAN NOPTI
CPU: 0 UID: 0 PID: 340 Comm: qdisc_uaf_repro Not tainted 7.2.0-rc4-00061-g248951ddc14d #256 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:__asan_load2
<IRQ>
qdisc_pkt_len_segs_init (net/core/dev.c:4145)
__dev_queue_xmit (net/core/dev.c:4787)
br_dev_queue_push_xmit (net/bridge/br_forward.c:53)
br_handle_frame_finish (net/bridge/br_input.c:229)
br_handle_frame (net/bridge/br_input.c:315)
__netif_receive_skb_core.constprop.0 (net/core/dev.c:6099)
__netif_receive_skb_list_core (net/core/dev.c:6287)
netif_receive_skb_list_internal (net/core/dev.c:6445)
napi_complete_done (net/core/dev.c:6813)
gro_cell_poll (net/core/gro_cells.c:74)
__napi_poll (net/core/dev.c:7735)
net_rx_action (net/core/dev.c:7798 net/core/dev.c:7955)
handle_softirqs (kernel/softirq.c:622)
do_softirq (kernel/softirq.c:523 kernel/softirq.c:510 )
__local_bh_enable_ip (kernel/softirq.c:450)
tun_get_user (drivers/net/tun.c:1986 (discriminator 1))
tun_chr_write_iter (drivers/net/tun.c:2032)
The crash requires four conditions to line up:
1. The incoming packet is encapsulated and carries GSO metadata. The outer
transport header offset is stored in skb->transport_header while the
packet is still in the outer tunnel context.
2. The tunnel receiver strips the outer headers. skb->data is advanced to
the inner frame, but skb->transport_header is left pointing to the
now-removed outer L4 header, so it becomes a negative offset relative to
the new data.
3. The inner frame is not delivered to the local IP stack. Instead, it
is forwarded at L2 by a bridge or HSR, so ip_rcv_core() never runs and
the transport header is not reset to the inner L4 offset.
4. The forwarding path calls __dev_queue_xmit(), which enters
qdisc_pkt_len_segs_init(). That function computes the GSO header length
from skb_transport_offset(skb). Because the offset is negative, the
unsigned cast overflows and pskb_may_pull(skb, hdr_len +
sizeof(struct tcphdr)) reads past the end of the skb, triggering a
KASAN fault or page fault.
Fix this by clearing skb->transport_header to the ~0U sentinel at the
tunnel decapsulation boundary, after each tunnel receive function has
finished all processing that needs the outer L4 header and before the skb
is handed to GRO or the stack. The IP/GRO receive paths then set the
transport header correctly when they parse the inner packet.
Fixes: 7fb4c1967011 ("net: pull headers in qdisc_pkt_len_segs_init()")
Reported-by: syzbot+83181a31faf9455499c5@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/69de2bee.a00a0220.475f0.0041.GAE@google.com/T/
Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com>
---
drivers/net/amt.c | 1 +
drivers/net/bareudp.c | 2 ++
drivers/net/geneve.c | 6 ++++--
drivers/net/gtp.c | 1 +
drivers/net/pfcp.c | 1 +
drivers/net/vxlan/vxlan_core.c | 1 +
include/linux/skbuff.h | 5 +++++
net/ipv4/ip_tunnel.c | 2 ++
net/ipv6/ip6_tunnel.c | 2 ++
net/ipv6/sit.c | 1 +
net/xfrm/xfrm_input.c | 1 +
11 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/net/amt.c b/drivers/net/amt.c
index 182a41d59a75..5968a08fcd5c 100644
--- a/drivers/net/amt.c
+++ b/drivers/net/amt.c
@@ -2355,6 +2355,7 @@ static bool amt_multicast_data_handler(struct amt_dev *amt, struct sk_buff *skb)
skb->pkt_type = PACKET_MULTICAST;
skb->ip_summed = CHECKSUM_NONE;
+ skb_unset_transport_header(skb);
len = skb->len;
err = gro_cells_receive(&amt->gro_cells, skb);
if (likely(err == NET_RX_SUCCESS))
diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c
index 5ef841c85526..b92652ca91ec 100644
--- a/drivers/net/bareudp.c
+++ b/drivers/net/bareudp.c
@@ -191,6 +191,8 @@ static int bareudp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
}
}
+ skb_unset_transport_header(skb);
+
len = skb->len;
err = gro_cells_receive(&bareudp->gro_cells, skb);
if (likely(err == NET_RX_SUCCESS))
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 72023ebd0e1b..b632239c5e43 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -372,10 +372,12 @@ static void geneve_rx(struct geneve_dev *geneve, struct geneve_sock *gs,
/* Skip the additional GRO stage when hints are in use. */
len = skb->len;
- if (skb->encapsulation)
+ if (skb->encapsulation) {
err = netif_rx(skb);
- else
+ } else {
+ skb_unset_transport_header(skb);
err = gro_cells_receive(&geneve->gro_cells, skb);
+ }
if (likely(err == NET_RX_SUCCESS))
dev_dstats_rx_add(geneve->dev, len);
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
index 9a12cc53da00..1e3013d49713 100644
--- a/drivers/net/gtp.c
+++ b/drivers/net/gtp.c
@@ -337,6 +337,7 @@ static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,
dev_sw_netstats_rx_add(pctx->dev, skb->len);
+ skb_unset_transport_header(skb);
__netif_rx(skb);
return 0;
diff --git a/drivers/net/pfcp.c b/drivers/net/pfcp.c
index d8e4d60f5834..4b67906646ff 100644
--- a/drivers/net/pfcp.c
+++ b/drivers/net/pfcp.c
@@ -94,6 +94,7 @@ static int pfcp_encap_recv(struct sock *sk, struct sk_buff *skb)
skb_reset_mac_header(skb);
skb->dev = pfcp->dev;
+ skb_unset_transport_header(skb);
gro_cells_receive(&pfcp->gro_cells, skb);
return 0;
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 1ded27768a97..9366895856f6 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -1799,6 +1799,7 @@ static int vxlan_rcv(struct sock *sk, struct sk_buff *skb)
dev_dstats_rx_add(vxlan->dev, skb->len);
vxlan_vnifilter_count(vxlan, vni, vninode, VXLAN_VNI_STATS_RX, skb->len);
+ skb_unset_transport_header(skb);
gro_cells_receive(&vxlan->gro_cells, skb);
rcu_read_unlock();
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 22eda1d54a0e..626bbb9bae1a 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3082,6 +3082,11 @@ static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
return skb->transport_header != (typeof(skb->transport_header))~0U;
}
+static inline void skb_unset_transport_header(struct sk_buff *skb)
+{
+ skb->transport_header = (typeof(skb->transport_header))~0U;
+}
+
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
{
DEBUG_NET_WARN_ON_ONCE(!skb_transport_header_was_set(skb));
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 9d114bd575f9..5e677c86f0e3 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -445,6 +445,8 @@ int ip_tunnel_rcv(struct ip_tunnel *tunnel, struct sk_buff *skb,
if (tun_dst)
skb_dst_set(skb, (struct dst_entry *)tun_dst);
+ skb_unset_transport_header(skb);
+
gro_cells_receive(&tunnel->gro_cells, skb);
return 0;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index ebf83f090376..e7c8283a0e39 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -892,6 +892,8 @@ static int __ip6_tnl_rcv(struct ip6_tnl *tunnel, struct sk_buff *skb,
if (tun_dst)
skb_dst_set(skb, (struct dst_entry *)tun_dst);
+ skb_unset_transport_header(skb);
+
gro_cells_receive(&tunnel->gro_cells, skb);
return 0;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index a38b24fb8384..ad5adc5abe6e 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -726,6 +726,7 @@ static int ipip6_rcv(struct sk_buff *skb)
dev_sw_netstats_rx_add(tunnel->dev, skb->len);
+ skb_unset_transport_header(skb);
netif_rx(skb);
return 0;
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index eecab337bd0a..2767021c138a 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -744,6 +744,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
skb_dst_drop(skb);
if (async)
dev_put(dev);
+ skb_unset_transport_header(skb);
gro_cells_receive(&gro_cells, skb);
rcu_read_unlock();
return 0;
--
2.25.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH net] net: iptunnel: fix stale transport header during tunnel decapsulation
2026-08-13 3:38 [PATCH net] net: iptunnel: fix stale transport header during tunnel decapsulation Dong Chenchen
@ 2026-08-13 4:39 ` Eric Dumazet
0 siblings, 0 replies; 2+ messages in thread
From: Eric Dumazet @ 2026-08-13 4:39 UTC (permalink / raw)
To: Dong Chenchen
Cc: davem, pabeni, kuba, horms, herbert, kuniyu, idosch,
andrew+netdev, ap420073, steffen.klassert, laforge, jiayuan.chen,
jhs, zhangchangzhong, netdev, syzbot+83181a31faf9455499c5
On Thu, Aug 13, 2026 at 5:30 AM Dong Chenchen <dongchenchen2@huawei.com> wrote:
>
> Syzbot reported a crash in qdisc_pkt_len_segs_init() caused by a stale
> transport_header offset after tunnel decapsulation.
>
> BUG: unable to handle page fault for address: ffffed102091a42e
> Oops: Oops: 0000 [#1] SMP KASAN NOPTI
> CPU: 0 UID: 0 PID: 340 Comm: qdisc_uaf_repro Not tainted 7.2.0-rc4-00061-g248951ddc14d #256 PREEMPT(full)
> Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> RIP: 0010:__asan_load2
> <IRQ>
> qdisc_pkt_len_segs_init (net/core/dev.c:4145)
> __dev_queue_xmit (net/core/dev.c:4787)
> br_dev_queue_push_xmit (net/bridge/br_forward.c:53)
> br_handle_frame_finish (net/bridge/br_input.c:229)
> br_handle_frame (net/bridge/br_input.c:315)
> __netif_receive_skb_core.constprop.0 (net/core/dev.c:6099)
> __netif_receive_skb_list_core (net/core/dev.c:6287)
> netif_receive_skb_list_internal (net/core/dev.c:6445)
> napi_complete_done (net/core/dev.c:6813)
> gro_cell_poll (net/core/gro_cells.c:74)
> __napi_poll (net/core/dev.c:7735)
> net_rx_action (net/core/dev.c:7798 net/core/dev.c:7955)
> handle_softirqs (kernel/softirq.c:622)
> do_softirq (kernel/softirq.c:523 kernel/softirq.c:510 )
> __local_bh_enable_ip (kernel/softirq.c:450)
> tun_get_user (drivers/net/tun.c:1986 (discriminator 1))
> tun_chr_write_iter (drivers/net/tun.c:2032)
>
> The crash requires four conditions to line up:
>
> 1. The incoming packet is encapsulated and carries GSO metadata. The outer
> transport header offset is stored in skb->transport_header while the
> packet is still in the outer tunnel context.
> 2. The tunnel receiver strips the outer headers. skb->data is advanced to
> the inner frame, but skb->transport_header is left pointing to the
> now-removed outer L4 header, so it becomes a negative offset relative to
> the new data.
> 3. The inner frame is not delivered to the local IP stack. Instead, it
> is forwarded at L2 by a bridge or HSR, so ip_rcv_core() never runs and
> the transport header is not reset to the inner L4 offset.
> 4. The forwarding path calls __dev_queue_xmit(), which enters
> qdisc_pkt_len_segs_init(). That function computes the GSO header length
> from skb_transport_offset(skb). Because the offset is negative, the
> unsigned cast overflows and pskb_may_pull(skb, hdr_len +
> sizeof(struct tcphdr)) reads past the end of the skb, triggering a
> KASAN fault or page fault.
>
> Fix this by clearing skb->transport_header to the ~0U sentinel at the
> tunnel decapsulation boundary, after each tunnel receive function has
> finished all processing that needs the outer L4 header and before the skb
> is handed to GRO or the stack. The IP/GRO receive paths then set the
> transport header correctly when they parse the inner packet.
>
> Fixes: 7fb4c1967011 ("net: pull headers in qdisc_pkt_len_segs_init()")
This Fixes: tag seems pretty random to me. Issue was present long
before that commit.
Next time, please be precise with the `Fixes:` tag; it's not decoration.
Also, calling skb_unset_transport_header(skb) directly from
gro_cells_receive() would make more sense and
your patch would be less intrusive.
pw-bot: cr
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-13 4:39 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 3:38 [PATCH net] net: iptunnel: fix stale transport header during tunnel decapsulation Dong Chenchen
2026-08-13 4:39 ` Eric Dumazet
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox