Re: [PATCH net v2] net: iptunnel: fix stale transport header after GRE/TEB decap

From: Eric Dumazet

Date: Sun Apr 19 2026 - 05:26:10 EST


On Sun, Apr 19, 2026 at 2:08 AM Jiayuan Chen <jiayuan.chen@xxxxxxxxx> wrote:
>
> syzbot reported a BUG.
>
> I found that after GRE decapsulation in gretap/ip6gretap paths, the
> transport_header becomes stale with a negative offset. The sequence is:
>
> 1. Before decap, transport_header points to the outer L4 (GRE) header.
> 2. __iptunnel_pull_header() calls skb_pull_rcsum() to advance skb->data
> past the GRE header, but does not update transport_header.
> 3. For TEB (gretap/ip6gretap), eth_type_trans() in ip_tunnel_rcv() /
> __ip6_tnl_rcv() further pulls ETH_HLEN (14 bytes) from skb->data.
>
> After these two pulls, skb->data has moved forward while transport_header
> still points to the old (now behind skb->data) position, resulting in a
> negative skb_transport_offset(): typically -4 after GRE pull alone, or
> -18 after GRE + inner Ethernet pull.
>
> In the normal case where the inner frame is a recognizable protocol
> (e.g., IPv4/TCP), the transport_header is subsequently overwritten by
> ip_rcv_core() (or inet_gro_receive() on the GRO path) via
> skb_set_transport_header(), and the stale value never reaches downstream
> consumers. However, if the inner frame cannot be parsed (e.g.,
> eth_type_trans() classifies it as ETH_P_802_2 due to a zero/invalid
> inner Ethernet header), neither rescue runs, and the stale offset
> persists into __netif_receive_skb_core().
>
> When this stale offset is combined with contradictory GSO metadata (e.g.,
> SKB_GSO_TCPV4 injected via virtio_net_hdr from a tun device),
> qdisc_pkt_len_segs_init() trusts the negative offset: the unsigned
> wraparound makes pskb_may_pull() effectively a no-op, and __tcp_hdrlen()
> then reads from an invalid memory location, causing a use-after-free.
>
> The UAF only triggers on the GSO path, where qdisc_pkt_len_segs_init()
> dereferences the transport header to compute per-segment length. Fix
> this by introducing iptunnel_rebuild_transport_header(), which is a
> no-op for non-GSO packets and otherwise re-probes the transport header
> via the flow dissector. If re-probing fails, the contradictory GSO
> metadata is cleared via skb_gso_reset() so downstream consumers cannot
> trust stale offsets. Restricting the rebuild to GSO packets keeps the
> flow-dissector cost off the common rx fast path.
>
> reproducer: https://gist.github.com/mrpre/5ba943fd86367af748b70de99263da4b
>
> Link: https://syzkaller.appspot.com/bug?extid=83181a31faf9455499c5
> Fixes: c54419321455 ("GRE: Refactor GRE tunneling code.")
> Fixes: 0d3c703a9d17 ("ipv6: Cleanup IPv6 tunnel receive path")
> Reported-by: syzbot+83181a31faf9455499c5@xxxxxxxxxxxxxxxxxxxxxxxxx
> Closes: https://lore.kernel.org/all/69de2bee.a00a0220.475f0.0041.GAE@xxxxxxxxxx/T/
> Signed-off-by: Jiayuan Chen <jiayuan.chen@xxxxxxxxx>
> ---
>
> As a follow-up for production reliability, I am wondering whether we
> can extend the existing safety net in __netif_receive_skb_core() to
> also handle set-but-negative transport_header:
>
> if (!skb_transport_header_was_set(skb) ||
> skb_transport_offset(skb) < 0)
> skb_reset_transport_header(skb);
> ---
> include/net/ip_tunnels.h | 12 ++++++++++++
> net/ipv4/ip_tunnel.c | 2 ++
> net/ipv6/ip6_tunnel.c | 2 ++
> 3 files changed, 16 insertions(+)
>
> diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h
> index d708b66e55cd..9b4e662833a1 100644
> --- a/include/net/ip_tunnels.h
> +++ b/include/net/ip_tunnels.h
> @@ -662,6 +662,18 @@ static inline int iptunnel_pull_offloads(struct sk_buff *skb)
> return 0;
> }
>
> +static inline void iptunnel_rebuild_transport_header(struct sk_buff *skb)
> +{
> + if (!skb_is_gso(skb))
> + return;
> +
> + skb->transport_header = (typeof(skb->transport_header))~0U;
> + skb_probe_transport_header(skb);
> +
> + if (!skb_transport_header_was_set(skb))
> + skb_gso_reset(skb);

I do not think this makes sense.
What is a valid case for this packet being processed further?
The buggy packet must be dropped, instead of being mangled like this.