Re: [PATCH 1/1] net: tap: set skb->dev before parsing virtio net header in tap_get_user_xdp()

"Michael S. Tsirkin" <[email protected]> Mon, 3 Aug 2026 12:38:37 -0400
Newsgroups org.kernel.vger.netdev
Message-ID <[email protected]>
On Sun, Aug 02, 2026 at 03:46:12PM -0700, Dongli Zhang wrote:
> 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: [email protected]
> Assisted-by: Codex:GPT-5.5
> Signed-off-by: Dongli Zhang <[email protected]>

Acked-by: Michael S. Tsirkin <[email protected]>

> ---
>  drivers/net/tap.c | 24 ++++++++++++++----------
>  1 file changed, 14 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/net/tap.c b/drivers/net/tap.c
> index fae115915c8e..5d2d34d24ce8 100644
> --- a/drivers/net/tap.c
> +++ b/drivers/net/tap.c
> @@ -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;
> -- 
> 2.43.5