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