Commit 3287cd714a85 for kernel
commit 3287cd714a853d9443c4a743eca010aedcfbbcdd
Author: Eric Dumazet <edumazet@kernel.org>
Date: Tue Nov 10 22:36:27 2026 +0100
tun: clear stale skb->protocol of NAPI frags skbs
With IFF_NAPI_FRAGS, tun_napi_alloc_frags() gets its skb from
napi_get_frags(). If GRO merged the previous frame with
GRO_MERGED_FREE, that skb was recycled by napi_reuse_skb(), which does
not reset skb->protocol set by napi_frags_skb() for the previous frame.
tun_get_user() calls tun_vnet_hdr_tnl_to_skb() before eth_type_trans(),
so the virtio header checks see the protocol of the previous frame:
GSO packets without VIRTIO_NET_HDR_F_NEEDS_CSUM are dissected with the
wrong protocol. A following change checks the outer protocol of UDP
tunnel GSO packets, which would then reject a valid frame when the two
ethertypes differ.
This needs GRO to hold packets across writes, and IFF_NAPI_FRAGS needs
CAP_NET_ADMIN.
Clear skb->protocol in tun_napi_alloc_frags(), like skbs allocated
by tun_alloc_skb() or build_skb(). eth_type_trans() sets it later.
Fixes: 90e33d459407 ("tun: enable napi_gro_frags() for TUN/TAP driver")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://lore.kernel.org/netdev/179386753589.488856.11455181721004759454@kernel.org/
Suggested-by: Willem de Bruijn <willemb@google.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Signed-off-by: Eric Dumazet <edumazet@kernel.org>
Link: https://patch.msgid.link/20261110213628.3476051-3-edumazet@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index ab1b91a9a99e..4ab7a4f7fc4e 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1500,6 +1500,12 @@ static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile,
if (!skb)
return ERR_PTR(-ENOMEM);
+ /* An skb recycled by napi_reuse_skb() keeps the protocol of a
+ * previous frame. eth_type_trans() sets it after the virtio
+ * header checks, which must not see a stale one.
+ */
+ skb->protocol = 0;
+
linear = iov_iter_single_seg_count(it);
err = __skb_grow(skb, linear);
if (err)