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:
the outer protocol check of UDP tunnel GSO packets rejects a valid
frame when the two ethertypes differ, and GSO packets without
VIRTIO_NET_HDR_F_NEEDS_CSUM are dissected with the wrong protocol.

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 <[email protected]>
Closes: 
https://lore.kernel.org/netdev/[email protected]/
Suggested-by: Willem de Bruijn <[email protected]>
Reviewed-by: Willem de Bruijn <[email protected]>
Signed-off-by: Eric Dumazet <[email protected]>
---
 drivers/net/tun.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 3920b3dff1e8..9aca5056f7c5 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)
-- 
2.53.0


Reply via email to