Thread (2 messages) flat view 2 messages, 1 author, 1d ago
DORMANTno replies

[PATCH net v2 1/2] virtio_net: fix AF_XDP pool disable after queue shrink

From: Xiong Weimin <hidden>
Date: 2026-08-07 01:32:32
Also in: lkml, virtualization
Subsystem: networking drivers, the rest, virtio net driver · Maintainers: Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds, "Michael S. Tsirkin", Jason Wang

xp_clear_dev() calls the driver to unbind an AF_XDP pool, then frees
the pool.  virtnet_xsk_pool_disable() currently rejects qid >=
curr_queue_pairs, so if XDP detach (or another path) has already shrunk
curr_queue_pairs below a still-bound qid, disable returns -EINVAL.
The core only WARNs, clears the netdev pool pointer, and destroys the
pool, which leaks the driver's DMA mappings and xsk_buffs and leaves
rq/sq->xsk_pool dangling for a use-after-free if those queues are
brought back later.

Use max_queue_pairs for the bounds check so cleanup can still run,
null the freed pointers, and avoid refill on inactive queues when
unbinding.  Also unmap the shared TX header with sq->vq on the enable
error path to match the mapping side (no functional change while
rq/sq share a DMA device).

Fixes: 09d2b3182c8e ("virtio_net: xsk: bind/unbind xsk for rx")
Signed-off-by: Xiong Weimin <redacted>
---
v2:
- rewrite as a real fix for disable-after-shrink (leak / UAF)
- drop the previous "check queue index before use" reorder patch
- fold sq->vq unmap symmetry here as NFC (no separate Fixes for that)

 drivers/net/virtio_net.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 3e2a587..d07ccef 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -5848,7 +5848,7 @@ static int virtnet_rq_bind_xsk_pool(struct virtnet_info *vi, struct receive_queu
 
 	rq->xsk_pool = pool;
 
-	virtnet_rx_resume(vi, rq, true);
+	virtnet_rx_resume(vi, rq, qindex < vi->curr_queue_pairs);
 
 	if (pool)
 		return 0;
@@ -5959,10 +5959,11 @@ err_sq:
 err_rq:
 	xsk_pool_dma_unmap(pool, 0);
 err_xsk_map:
-	virtqueue_unmap_single_attrs(rq->vq, hdr_dma, vi->hdr_len,
+	virtqueue_unmap_single_attrs(sq->vq, hdr_dma, vi->hdr_len,
 				     DMA_TO_DEVICE, 0);
 err_free_buffs:
 	kvfree(rq->xsk_buffs);
+	rq->xsk_buffs = NULL;
 	return err;
 }
 
@@ -5974,7 +5975,12 @@ static int virtnet_xsk_pool_disable(struct net_device *dev, u16 qid)
 	struct send_queue *sq;
 	int err;
 
-	if (qid >= vi->curr_queue_pairs)
+	/* rq/sq are sized by max_queue_pairs.  Allow cleanup even if
+	 * curr_queue_pairs has shrunk below qid (e.g. after XDP detach),
+	 * otherwise disable fails, leaks mappings/xsk_buffs, and leaves
+	 * dangling rq/sq->xsk_pool pointers to a soon-to-be-freed pool.
+	 */
+	if (qid >= vi->max_queue_pairs)
 		return -EINVAL;
 
 	sq = &vi->sq[qid];
@@ -5985,11 +5991,17 @@ static int virtnet_xsk_pool_disable(struct net_device *dev, u16 qid)
 	err = virtnet_rq_bind_xsk_pool(vi, rq, NULL);
 	err |= virtnet_sq_bind_xsk_pool(vi, sq, NULL);
 
-	xsk_pool_dma_unmap(pool, 0);
+	if (pool)
+		xsk_pool_dma_unmap(pool, 0);
+
+	if (sq->xsk_hdr_dma_addr) {
+		virtqueue_unmap_single_attrs(sq->vq, sq->xsk_hdr_dma_addr,
+					     vi->hdr_len, DMA_TO_DEVICE, 0);
+		sq->xsk_hdr_dma_addr = 0;
+	}
 
-	virtqueue_unmap_single_attrs(sq->vq, sq->xsk_hdr_dma_addr,
-				     vi->hdr_len, DMA_TO_DEVICE, 0);
 	kvfree(rq->xsk_buffs);
+	rq->xsk_buffs = NULL;
 
 	return err;
 }
-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help