]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: tap: set skb->dev before parsing virtio net header in tap_get_user_xdp()
authorDongli Zhang <dongli.zhang@oracle.com>
Sun, 2 Aug 2026 22:46:12 +0000 (15:46 -0700)
committerJakub Kicinski <kuba@kernel.org>
Thu, 6 Aug 2026 00:32:55 +0000 (17:32 -0700)
The commit 4f61f133f354 ("net: tap: NULL pointer derefence in
dev_parse_header_protocol when skb->dev is null") fixed a crash in
tap_get_user() by assigning skb->dev before calling tun_vnet_hdr_to_skb().
This is required because virtio_net_hdr_to_skb() may invoke
dev_parse_header_protocol(), which dereferences skb->dev. Without the
assignment, a NULL pointer dereference can occur.

However, tap_get_user_xdp() still parses the virtio-net header before
assigning skb->dev. When the vhost TX path passes an XDP buffer containing
a GSO virtio-net header but the protocol is set to zero on purpose,
tun_vnet_hdr_to_skb() can reach dev_parse_header_protocol() while skb->dev
is still NULL, resulting in a crash.

Fix this by looking up the tap device and assigning skb->dev before calling
tun_vnet_hdr_to_skb(), matching the ordering already used in
tap_get_user(). Preserve the existing RCU read-side critical section across
dev_queue_xmit().

Fixes: 924a9bc362a5 ("net: check if protocol extracted by virtio_net_hdr_set_proto is correct")
Cc: stable@vger.kernel.org
Signed-off-by: Dongli Zhang <dongli.zhang@oracle.com>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Link: https://patch.msgid.link/20260802224612.264563-1-dongli.zhang@oracle.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/tap.c

index fae115915c8eff0b1068ab3f8c8e99a5ad9d1e86..5d2d34d24ce818477d281c26ba4aa2be9a19b80f 100644 (file)
@@ -1074,10 +1074,21 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
        skb_reset_mac_header(skb);
        skb->protocol = eth_hdr(skb)->h_proto;
 
+       rcu_read_lock();
+       tap = rcu_dereference(q->tap);
+       if (!tap) {
+               kfree_skb(skb);
+               rcu_read_unlock();
+               return 0;
+       }
+       skb->dev = tap->dev;
+
        if (vnet_hdr_len) {
                err = tun_vnet_hdr_to_skb(q->flags, skb, gso);
-               if (err)
+               if (err) {
+                       rcu_read_unlock();
                        goto err_kfree;
+               }
        }
 
        /* Move network header to the right position for VLAN tagged packets */
@@ -1085,15 +1096,8 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
            vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0)
                skb_set_network_header(skb, depth);
 
-       rcu_read_lock();
-       tap = rcu_dereference(q->tap);
-       if (tap) {
-               skb->dev = tap->dev;
-               skb_probe_transport_header(skb);
-               dev_queue_xmit(skb);
-       } else {
-               kfree_skb(skb);
-       }
+       skb_probe_transport_header(skb);
+       dev_queue_xmit(skb);
        rcu_read_unlock();
 
        return 0;