From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from out-181.mta1.migadu.com (out-181.mta1.migadu.com [95.215.58.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2C194298CAB for ; Thu, 16 Apr 2026 03:46:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.181 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776311205; cv=none; b=lHximMUyzO3DCXupSqaJDjL9oh/AqYFjIQtcr+7zaHgSOn/qWRQ9CByz9NN2KBMt50dbVtwlF4c0Fc/trSWSJgo1GIpgq3sEzbTUB94+IkauglJnCJgxaYxeTiBHRH1+LUxMiRRKLq2QmtWWzc+AerZvpkXIZXV7W6pno8FSwfU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776311205; c=relaxed/simple; bh=ATB3acJJRcv7+vkNRw2cemvMEX8EAkhws6dD/5PhPYU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=vAIpkdPMNO1cSFRukMDvDvbZWHnkVO9WT/ph3bSh12I1zlcPll9eLsYthAwfU72ZC8eNN2CAvYFIOr76TMkeWZlj1XCdW4k3dDxzogPSIaTqft1jBbv/JOKaMCi2fUUZs8dlYYYMheHbU7V9RFlYTkwiKgs9PMCjdTqBJqn5O2Q= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=CqOiWfFh; arc=none smtp.client-ip=95.215.58.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="CqOiWfFh" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1776311192; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=STy5fptl4kDA0hxRdge2uXtZILjqvQ9NCE82yGpau2k=; b=CqOiWfFhUGltgYgb/bc9YecNLk0MSWip6Rq+DGMCFgwsNMXym8r2n7UT8vWQl2erCtgGgG /qbPpLroimBemnX59n8k0s5/NHDgAtEQdX+g3XWDBMwlzs3WnChgDZ+F1CXKGxe2mHA/H4 MST7Rpui1CGRKxl0cN6CAoKeb93zOLE= From: Jiayuan Chen To: netdev@vger.kernel.org Cc: Jiayuan Chen , syzbot+83181a31faf9455499c5@syzkaller.appspotmail.com, "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , David Ahern , Pravin B Shelar , Tom Herbert , linux-kernel@vger.kernel.org Subject: [PATCH net v1 1/2] net: tunnel: fix stale transport header after GRE/TEB decap Date: Thu, 16 Apr 2026 11:46:06 +0800 Message-ID: <20260416034610.8873-1-jiayuan.chen@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Migadu-Flow: FLOW_OUT 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), inet_gro_receive() in net/ipv4/af_inet.c corrects the transport_header via skb_set_transport_header() during GRO processing. 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), no GRO callback resets the transport_header, 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. Fix this by introducing iptunnel_rebuild_transport_header() which resets and re-probes the transport header after tunnel decapsulation. If the transport header cannot be rebuilt and the skb carries GSO metadata, the inconsistent GSO fields are cleared to prevent downstream consumers from trusting stale offsets. 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@syzkaller.appspotmail.com Signed-off-by: Jiayuan Chen --- include/net/ip_tunnels.h | 14 ++++++++++++++ net/ipv4/ip_tunnel.c | 2 ++ net/ipv6/ip6_tunnel.c | 2 ++ 3 files changed, 18 insertions(+) diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index d708b66e55cd..f160d82e6196 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -662,6 +662,20 @@ static inline int iptunnel_pull_offloads(struct sk_buff *skb) return 0; } +static inline void iptunnel_rebuild_transport_header(struct sk_buff *skb) +{ + skb->transport_header = (typeof(skb->transport_header))~0U; + skb_probe_transport_header(skb); + + if (!skb_transport_header_was_set(skb) && skb_is_gso(skb)) { + struct skb_shared_info *shinfo = skb_shinfo(skb); + + shinfo->gso_type = 0; + shinfo->gso_size = 0; + shinfo->gso_segs = 0; + } +} + static inline void iptunnel_xmit_stats(struct net_device *dev, int pkt_len) { if (pkt_len > 0) { diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index 50d0f5fe4e4c..c46be68cfafa 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); + iptunnel_rebuild_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 46bc06506470..f95348cf3c77 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -879,6 +879,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); + iptunnel_rebuild_transport_header(skb); + gro_cells_receive(&tunnel->gro_cells, skb); return 0; -- 2.43.0