From: sashiko-bot@kernel.org
To: "Haishuang Yan" <yanhaishuang@cmss.chinamobile.com>
Cc: virtualization@lists.linux.dev, bpf@vger.kernel.org,
"Michael S. Tsirkin" <mst@redhat.com>,
"Eugenio Perez" <eperezma@redhat.com>
Subject: Re: [PATCH net 2/2] virtio_net: Keep AF_XDP zero-copy pools bound across suspend
Date: Sun, 08 Nov 2026 04:58:28 +0000 [thread overview]
Message-ID: <sashiko-outbox-203566@kernel.org> (raw)
In-Reply-To: <20261107125723.33547-3-yanhaishuang@cmss.chinamobile.com>
Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] virtio_net: stale DMA addresses in free_list after unaligned
XSK pool rebind
- [High] virtio_net: uninitialized xsk_hdr_dma_addr used in
virtnet_xsk_pool_enable()
- [High] virtio_net: race between RX NAPI and xsk_pool_dma_unmap() in
virtnet_freeze()
- [Medium] virtio_net: virtnet_xsk_pool_disable() fails for qid >=
curr_queue_pairs
--
commit 0f52eff49a0a7d3c2cb4265b671ce30bc13fd5e3
Author: Haishuang Yan <yanhaishuang@cmss.chinamobile.com>
virtio_net: Keep AF_XDP zero-copy pools bound across suspend
This commit unbinds the driver side of active AF_XDP zero-copy buffer pools
before freezing virtio_net queues and rebinds them after restore.
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 4f5d8812f75b0..5995ad730a91a 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -6052,6 +6055,42 @@ static int virtnet_xsk_pool_disable(struct net_device *dev, u16 qid)
> return err;
> }
>
> +/*
> + * Freeze frees the receive and send queues while the AF_XDP sockets stay
> + * bound to the device, so release the driver side of the zero-copy pools
> + * before and bind them again after restore.
> + */
> +static void __maybe_unused virtnet_xsk_pools_unbind(struct virtnet_info *vi)
> +{
> + int i;
> +
> + ASSERT_RTNL();
> +
> + for (i = 0; i < vi->max_queue_pairs; i++)
> + if (vi->rq[i].xsk_pool)
> + virtnet_xsk_pool_disable(vi->dev, i);
> +}
[Severity: Medium]
Does virtnet_xsk_pool_disable() fail without unbinding the pool when
i >= vi->curr_queue_pairs?
Attaching an XDP program in virtnet_xdp_set() expands vi->curr_queue_pairs
and dev->real_num_rx_queues by xdp_qp, allowing an AF_XDP zero-copy pool to
be bound to a queue in that upper range, and detaching the XDP program
shrinks vi->curr_queue_pairs back down without unbinding the pool. When
virtnet_xsk_pools_unbind() iterates up to vi->max_queue_pairs,
virtnet_xsk_pool_disable() rejects any qid >= vi->curr_queue_pairs:
drivers/net/virtio_net.c:virtnet_xsk_pool_disable() {
if (qid >= vi->curr_queue_pairs)
return -EINVAL;
}
As a result, remove_vq_common() frees vi->rq and vi->sq while leaving
rq->xsk_rxq_info registered, the pool DMA mapping active, and rq->xsk_buffs
and sq->xsk_hdr leaked.
> +
> +static void __maybe_unused virtnet_xsk_pools_rebind(struct virtnet_info *vi)
> +{
> + struct xsk_buff_pool *pool;
> + int i, err;
> +
> + ASSERT_RTNL();
> +
> + for (i = 0; i < vi->curr_queue_pairs; i++) {
> + pool = xsk_get_pool_from_qid(vi->dev, i);
> + if (!pool || !pool->umem->zc)
> + continue;
> +
> + err = virtnet_xsk_pool_enable(vi->dev, pool, i);
[Severity: High]
When pool->unaligned is true, does rebinding the pool after freeze leave
stale pre-suspend DMA addresses in pool->free_list?
During virtnet_freeze(), virtnet_xsk_pool_disable() resets rq->vq and passes
each posted RX buffer to xsk_buff_free(), which places the xdp_buff_xsk
entries onto pool->free_list before xsk_pool_dma_unmap() unmaps and frees
the old DMA mapping. On restore, virtnet_xsk_pool_enable() calls
xsk_pool_dma_map() -> xp_dma_map() -> xp_init_dma_info(), which skips
reinitializing xskb->dma when pool->unaligned is set:
net/xdp/xsk_buff_pool.c:xp_init_dma_info() {
if (!pool->unaligned) {
u32 i;
for (i = 0; i < pool->heads_cnt; i++) {
struct xdp_buff_xsk *xskb = &pool->heads[i];
u64 orig_addr;
orig_addr = xskb->xdp.data_hard_start - pool->addrs - pool->headroom;
xp_init_xskb_dma(xskb, pool, dma_map->dma_pages, orig_addr);
}
}
}
When virtnet_rq_bind_xsk_pool() refills rq->vq via virtnet_rx_resume() ->
try_fill_recv() -> virtnet_add_recvbuf_xsk(), xsk_buff_alloc_batch() pops
those recycled entries from pool->free_list via xp_alloc_reused() without
updating xskb->dma, programming rq->vq with the unmapped pre-suspend DMA
addresses and causing IOMMU faults or DMA memory corruption.
> + if (err)
> + netdev_warn(vi->dev,
> + "failed to rebind AF_XDP pool to queue %d: %d\n",
> + i, err);
> + }
> +}
[Severity: High]
Can virtnet_xsk_pool_enable() transmit from sq->xsk_pool during restore
before sq->xsk_hdr_dma_addr is initialized?
On restore, init_vqs() allocates fresh zeroed send queues (so
sq->xsk_hdr_dma_addr is 0), while bound AF_XDP sockets are already on
pool->xsk_tx_list and may have pending descriptors in their TX rings.
virtnet_xsk_pool_enable() calls virtnet_sq_bind_xsk_pool() before setting
sq->xsk_hdr_dma_addr:
drivers/net/virtio_net.c:virtnet_xsk_pool_enable() {
...
err = virtnet_sq_bind_xsk_pool(vi, sq, pool);
if (err)
goto err_sq;
/* Now, we do not support tx offload(such as tx csum), so all the tx
* virtnet hdr is zero. So all the tx packets can share a single hdr.
*/
sq->xsk_hdr_dma_addr = hdr_dma;
...
}
When the device is running, virtnet_sq_bind_xsk_pool() sets sq->xsk_pool =
pool and calls virtnet_tx_resume() -> virtnet_napi_tx_enable() ->
virtnet_napi_do_enable(), which schedules sq->napi and runs virtnet_poll_tx()
synchronously on local_bh_enable(). virtnet_poll_tx() -> virtnet_xsk_xmit()
-> virtnet_xsk_xmit_one() then submits sq->xsk_hdr_dma_addr (0) as the
virtio header DMA address to the virtqueue.
[ ... ]
> @@ -7246,6 +7285,10 @@ static __maybe_unused int virtnet_freeze(struct virtio_device *vdev)
> {
> struct virtnet_info *vi = vdev->priv;
>
> + rtnl_lock();
> + virtnet_xsk_pools_unbind(vi);
> + rtnl_unlock();
> +
> virtnet_cpu_notif_remove(vi);
> virtnet_freeze_down(vdev);
[Severity: High]
Can unbinding the pools before virtnet_freeze_down() race with RX NAPI
redirecting to the still-bound AF_XDP socket while xsk_pool_dma_unmap()
frees pool->dma_pages?
Because virtnet_xsk_pools_unbind() runs while netif_running(vi->dev) is
true and before virtnet_freeze_down() closes the device,
virtnet_rq_bind_xsk_pool(vi, rq, NULL) clears rq->xsk_pool and calls
virtnet_rx_resume(vi, rq, true), which refills rq->vq with page_pool
buffers and re-enables and schedules rq->napi before xsk_pool_dma_unmap()
runs. Unlike normal socket teardown, the AF_XDP socket is still bound in
xsks_map, sorq->xdp_prog can redirect received page_pool packets into
xsk_rcv(), which falls back to copy mode (__xsk_rcv() -> xsk_buff_alloc())
while xsk_pool_dma_unmap() concurrently tears down the pool's DMA state:
CPU0 CPU1 (<softirq>)
virtnet_freeze()
virtnet_xsk_pools_unbind()
virtnet_xsk_pool_disable()
virtnet_rq_bind_xsk_pool(..., NULL)
rq->xsk_pool = NULL
virtnet_rx_resume()
virtnet_napi_enable(rq)
virtnet_poll()
...
xsk_rcv()
__xsk_rcv()
xsk_buff_alloc(xs->pool)
xp_alloc()
xsk_pool_dma_unmap(pool, 0)
xp_dma_unmap()
kvfree(pool->dma_pages)
xp_get_xskb()
xp_init_xskb_dma(...,
pool->dma_pages, ...)
<-- use-after-free
pool->dma_pages = NULL
pool->dev = NULL
In addition to reading freed pool->dma_pages in xp_check_unaligned() and
xp_init_xskb_dma(), xp_alloc() can call xp_dma_sync_for_cpu() as pool->dev
and the DMA mappings are being cleared, or leave uninitialized xskb->dma
entries on pool->free_list when the socket consumes the copied frame.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261107125723.33547-1-yanhaishuang@cmss.chinamobile.com?part=2
next prev parent reply other threads:[~2026-11-08 4:58 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-07 12:57 [PATCH net 0/2] virtio_net: AF_XDP zero-copy fixes for arm64 and suspend Haishuang Yan
2026-11-07 5:00 ` netdev-bot+sinfo
2026-11-07 12:57 ` [PATCH net 1/2] virtio_net: Don't DMA map the kernel image for the AF_XDP TX header Haishuang Yan
2026-11-08 4:58 ` sashiko-bot
2026-11-08 5:41 ` netdev-bot+sashiko
2026-11-08 17:23 ` Haishuang Yan
2026-11-07 12:57 ` [PATCH net 2/2] virtio_net: Keep AF_XDP zero-copy pools bound across suspend Haishuang Yan
2026-11-08 4:58 ` sashiko-bot [this message]
2026-11-08 5:41 ` netdev-bot+sashiko
2026-11-08 17:23 ` Haishuang Yan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-203566@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=eperezma@redhat.com \
--cc=mst@redhat.com \
--cc=sashiko-reviews@lists.linux.dev \
--cc=virtualization@lists.linux.dev \
--cc=yanhaishuang@cmss.chinamobile.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.