patch 'net/af_xdp: fix shared UMEM refcount corruption' has been queued to stable release 24.11.9

luca.boccassi at gmail.com luca.boccassi at gmail.com
Tue Oct 27 12:48:41 CET 2026


Hi,

FYI, your patch has been queued to stable release 24.11.9

Note it hasn't been pushed to http://dpdk.org/browse/dpdk-stable yet.
It will be pushed if I get no objections before 10/29/26. So please
shout if anyone has objections.

Also note that after the patch there's a diff of the upstream commit vs the
patch applied to the branch. This will indicate if there was any rebasing
needed to apply to the stable branch. If there were code changes for rebasing
(ie: not only metadata diffs), please double check that the rebase was
correctly done.

Queued patches are on a temporary branch at:
https://github.com/bluca/dpdk-stable

This queued commit can be viewed at:
https://github.com/bluca/dpdk-stable/commit/41e54bfd5f3b23e61b85ea9ad908fc39847569d7

Thanks.

Luca Boccassi

---
>From 41e54bfd5f3b23e61b85ea9ad908fc39847569d7 Mon Sep 17 00:00:00 2001
From: Sandeep Penigalapati <sandeep.penigalapati at intel.com>
Date: Thu, 20 Aug 2026 22:16:07 -0400
Subject: [PATCH] net/af_xdp: fix shared UMEM refcount corruption

[ upstream commit 53cdbf06bca6c02cf1e4cc31f42f4691734059fc ]

Shared UMEM is meant to be shared by a limited number of sockets,
governed by the mempool size (max_xsks). When the UMEM was already at
capacity (refcnt >= max_xsks), xdp_umem_configure() returned the UMEM
without incrementing its refcount, so the extra socket used it
unaccounted for.

This missing reference has two consequences. During queue setup the
fill-queue reservation is chosen from the refcount, so the sharing
socket reserves into its own uninitialised fill queue and crashes. At
close, the under-counted refcount reaches zero while the UMEM is still
in use, freeing it early and causing a use-after-free.

Reject sharing once the UMEM is at capacity by returning NULL. The
error is propagated from xsk_configure(), so Rx queue setup fails
cleanly with -ENOMEM. This applies the per-mempool socket limit that
shared UMEM was always intended to respect.

Harden the failure path this makes reachable:
- clear rxq->umem and its paired txq->umem when xsk_configure() fails,
  and skip queues whose UMEM is not yet set in get_shared_umem(), so a
  later scan over the same mempool cannot dereference a NULL or
  dangling UMEM;
- free the fill-queue mbufs that were allocated but not yet handed to
  the fill queue when a sharing socket fails to bind, so it no longer
  leaks a burst of mbufs back out of the shared mempool;
- propagate the map-insert failures in xsk_configure() instead of
  returning success, so a failed xsks_map update no longer leaves the
  caller using a deleted socket;
- continue past, rather than stop at, a failed queue in eth_dev_close()
  so later successful queues and their UMEM references are still freed;
- clamp max_xsks to UINT8_MAX so the cap stays within the uint8_t
  refcount.

Also correct the UMEM refcount memory ordering: release on the shared
increment and acquire-release on the final decrement, so the thread that
drops the last reference observes all prior users' writes before it
frees the UMEM.

Document the shared mempool sizing requirement (4096 mbufs per socket).

Note: on stable branches this is a behaviour change. Shared-UMEM setups
that previously appeared to start, until the fill-queue crash or the
use-after-free at close, now fail cleanly at Rx queue setup with
-ENOMEM.

Fixes: 74b46340e2d4 ("net/af_xdp: support shared UMEM")

Signed-off-by: Sandeep Penigalapati <sandeep.penigalapati at intel.com>
---
 doc/guides/nics/af_xdp.rst          |  5 +++
 drivers/net/af_xdp/rte_eth_af_xdp.c | 64 +++++++++++++++++++++++------
 2 files changed, 57 insertions(+), 12 deletions(-)

diff --git a/doc/guides/nics/af_xdp.rst b/doc/guides/nics/af_xdp.rst
index 57dc3b69186..a9e257af233 100644
--- a/doc/guides/nics/af_xdp.rst
+++ b/doc/guides/nics/af_xdp.rst
@@ -99,6 +99,11 @@ configured like so:
     --vdev net_af_xdp0,iface=ens786f1,shared_umem=1 \
     --vdev net_af_xdp1,iface=ens786f2,shared_umem=1
 
+The shared mempool must be large enough for every AF_XDP socket sharing the UMEM.
+Each socket needs 4096 mbufs for its fill queue,
+so ``N`` sockets need at least ``4096 * N`` mbufs.
+Rx queue setup fails if the mempool is too small to add another socket to the UMEM.
+
 xdp_prog
 ~~~~~~~~
 
diff --git a/drivers/net/af_xdp/rte_eth_af_xdp.c b/drivers/net/af_xdp/rte_eth_af_xdp.c
index cf3b70fbef6..15689e677f0 100644
--- a/drivers/net/af_xdp/rte_eth_af_xdp.c
+++ b/drivers/net/af_xdp/rte_eth_af_xdp.c
@@ -1020,12 +1020,13 @@ eth_dev_close(struct rte_eth_dev *dev)
 
 	for (i = 0; i < internals->queue_cnt; i++) {
 		rxq = &internals->rx_queues[i];
+		/* Skip queues where eth_rx_queue_setup() was never called or failed. */
 		if (rxq->umem == NULL)
-			break;
+			continue;
 		xsk_socket__delete(rxq->xsk);
 
 		if (rte_atomic_fetch_sub_explicit(&rxq->umem->refcnt, 1,
-				rte_memory_order_acquire) - 1 == 0)
+				rte_memory_order_acq_rel) - 1 == 0)
 			xdp_umem_destroy(rxq->umem);
 	}
 	/* Free Tx and Rx queue arrays */
@@ -1112,6 +1113,9 @@ get_shared_umem(struct pkt_rx_queue *rxq, const char *ifname,
 					ret = -1;
 					goto out;
 				}
+				/* A failed setup leaves mb_pool set with no umem. */
+				if (list_rxq->umem == NULL)
+					continue;
 				if (rte_atomic_load_explicit(&internals->rx_queues[i].umem->refcnt,
 						    rte_memory_order_acquire)) {
 					*umem = internals->rx_queues[i].umem;
@@ -1146,12 +1150,29 @@ xsk_umem_info *xdp_umem_configure(struct pmd_internals *internals,
 		if (get_shared_umem(rxq, internals->if_name, &umem) < 0)
 			return NULL;
 
-		if (umem != NULL &&
-			rte_atomic_load_explicit(&umem->refcnt, rte_memory_order_acquire) <
-					umem->max_xsks) {
+		if (umem != NULL) {
+			/* Reject sharing once the UMEM is at capacity. */
+			if (rte_atomic_load_explicit(&umem->refcnt,
+					rte_memory_order_acquire) >= umem->max_xsks) {
+				if (umem->max_xsks == 0)
+					AF_XDP_LOG_LINE(ERR,
+						"%s,qid%i: mempool %s has %u mbufs, "
+						"need at least %u to share UMEM",
+						internals->if_name, rxq->xsk_queue_idx,
+						umem->mb_pool->name,
+						umem->mb_pool->populated_size,
+						ETH_AF_XDP_NUM_BUFFERS);
+				else
+					AF_XDP_LOG_LINE(ERR,
+						"%s,qid%i: UMEM %s already at max %u sockets",
+						internals->if_name, rxq->xsk_queue_idx,
+						umem->mb_pool->name, umem->max_xsks);
+				return NULL;
+			}
+
 			AF_XDP_LOG_LINE(INFO, "%s,qid%i sharing UMEM",
 					internals->if_name, rxq->xsk_queue_idx);
-			rte_atomic_fetch_add_explicit(&umem->refcnt, 1, rte_memory_order_acquire);
+			rte_atomic_fetch_add_explicit(&umem->refcnt, 1, rte_memory_order_release);
 		}
 	}
 
@@ -1197,8 +1218,10 @@ xsk_umem_info *xdp_umem_configure(struct pmd_internals *internals,
 		umem->buffer = aligned_addr;
 
 		if (internals->shared_umem) {
-			umem->max_xsks = mb_pool->populated_size /
-						ETH_AF_XDP_NUM_BUFFERS;
+			/* refcnt is uint8_t, so the cap cannot exceed UINT8_MAX. */
+			umem->max_xsks = RTE_MIN(mb_pool->populated_size /
+						ETH_AF_XDP_NUM_BUFFERS,
+						(uint32_t)UINT8_MAX);
 			AF_XDP_LOG_LINE(INFO, "Max xsks for UMEM %s: %u",
 						mb_pool->name, umem->max_xsks);
 		}
@@ -1642,10 +1665,13 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
 	int reserve_size = ETH_AF_XDP_DFLT_NUM_DESCS;
 	struct rte_mbuf *fq_bufs[reserve_size];
 	bool reserve_before;
+	bool free_fq_bufs = false;
 
 	rxq->umem = xdp_umem_configure(internals, rxq);
-	if (rxq->umem == NULL)
+	if (rxq->umem == NULL) {
+		txq->umem = NULL;
 		return -ENOMEM;
+	}
 	txq->umem = rxq->umem;
 	reserve_before = rte_atomic_load_explicit(&rxq->umem->refcnt,
 			rte_memory_order_acquire) <= 1;
@@ -1656,11 +1682,13 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
 		AF_XDP_LOG_LINE(DEBUG, "Failed to get enough buffers for fq.");
 		goto out_umem;
 	}
+	free_fq_bufs = true;
 #endif
 
 	/* reserve fill queue of queues not (yet) sharing UMEM */
 	if (reserve_before) {
 		ret = reserve_fill_queue(rxq->umem, reserve_size, fq_bufs, &rxq->fq);
+		free_fq_bufs = false;
 		if (ret) {
 			AF_XDP_LOG_LINE(ERR, "Failed to reserve fill queue.");
 			goto out_umem;
@@ -1717,6 +1745,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
 	if (!reserve_before) {
 		/* reserve fill queue of queues sharing UMEM */
 		ret = reserve_fill_queue(rxq->umem, reserve_size, fq_bufs, &rxq->fq);
+		free_fq_bufs = false;
 		if (ret) {
 			AF_XDP_LOG_LINE(ERR, "Failed to reserve fill queue.");
 			goto out_xsk;
@@ -1732,6 +1761,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
 					  &rxq->xsk_queue_idx, &fd, 0);
 		if (err) {
 			AF_XDP_LOG_LINE(ERR, "Failed to insert xsk in map.");
+			ret = -EINVAL;
 			goto out_xsk;
 		}
 	}
@@ -1744,6 +1774,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
 			map_fd = uds_get_xskmap_fd(internals->if_name, internals->dp_path);
 			if (map_fd < 0) {
 				AF_XDP_LOG_LINE(ERR, "Failed to receive xskmap fd from AF_XDP Device Plugin");
+				ret = -EINVAL;
 				goto out_xsk;
 			}
 		} else {
@@ -1751,6 +1782,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
 			err = get_pinned_map(internals->dp_path, &map_fd);
 			if (err < 0 || map_fd < 0) {
 				AF_XDP_LOG_LINE(ERR, "Failed to retrieve pinned map fd");
+				ret = -EINVAL;
 				goto out_xsk;
 			}
 		}
@@ -1758,6 +1790,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
 		err = update_xskmap(rxq->xsk, map_fd, rxq->xsk_queue_idx);
 		if (err) {
 			AF_XDP_LOG_LINE(ERR, "Failed to insert xsk in map.");
+			ret = -EINVAL;
 			goto out_xsk;
 		}
 
@@ -1774,8 +1807,15 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
 out_xsk:
 	xsk_socket__delete(rxq->xsk);
 out_umem:
-	if (rte_atomic_fetch_sub_explicit(&rxq->umem->refcnt, 1, rte_memory_order_acquire) - 1 == 0)
+	/* Free fq_bufs that were allocated but never handed to the fill queue. */
+	if (free_fq_bufs)
+		rte_pktmbuf_free_bulk(fq_bufs, reserve_size);
+	if (rte_atomic_fetch_sub_explicit(&rxq->umem->refcnt, 1,
+			rte_memory_order_acq_rel) - 1 == 0)
 		xdp_umem_destroy(rxq->umem);
+	/* Drop dangling pointers so a later shared-UMEM scan skips this queue. */
+	rxq->umem = NULL;
+	txq->umem = NULL;
 
 	return ret;
 }
@@ -1816,9 +1856,9 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
 
 	rxq->mb_pool = mb_pool;
 
-	if (xsk_configure(internals, rxq, nb_rx_desc)) {
+	ret = xsk_configure(internals, rxq, nb_rx_desc);
+	if (ret) {
 		AF_XDP_LOG_LINE(ERR, "Failed to configure xdp socket");
-		ret = -EINVAL;
 		goto err;
 	}
 
-- 
2.47.3

---
  Diff of the applied patch vs upstream commit (please double-check if non-empty:
---
--- -	2026-10-27 11:43:54.801799208 +0000
+++ 0027-net-af_xdp-fix-shared-UMEM-refcount-corruption.patch	2026-10-27 11:43:53.522059697 +0000
@@ -1 +1 @@
-From 53cdbf06bca6c02cf1e4cc31f42f4691734059fc Mon Sep 17 00:00:00 2001
+From 41e54bfd5f3b23e61b85ea9ad908fc39847569d7 Mon Sep 17 00:00:00 2001
@@ -5,0 +6,2 @@
+[ upstream commit 53cdbf06bca6c02cf1e4cc31f42f4691734059fc ]
+
@@ -52 +53,0 @@
-Cc: stable at dpdk.org
@@ -61 +62 @@
-index 3fb366dfdd..4d44746239 100644
+index 57dc3b69186..a9e257af233 100644
@@ -64 +65 @@
-@@ -102,6 +102,11 @@ configured like so:
+@@ -99,6 +99,11 @@ configured like so:
@@ -77 +78 @@
-index 52f7fc2cd5..9c99dcec20 100644
+index cf3b70fbef6..15689e677f0 100644
@@ -80 +81 @@
-@@ -1250,12 +1250,13 @@ eth_dev_close(struct rte_eth_dev *dev)
+@@ -1020,12 +1020,13 @@ eth_dev_close(struct rte_eth_dev *dev)
@@ -96 +97 @@
-@@ -1350,6 +1351,9 @@ get_shared_umem(struct pkt_rx_queue *rxq, const char *ifname,
+@@ -1112,6 +1113,9 @@ get_shared_umem(struct pkt_rx_queue *rxq, const char *ifname,
@@ -106 +107 @@
-@@ -1384,12 +1388,29 @@ xsk_umem_info *xdp_umem_configure(struct pmd_internals *internals,
+@@ -1146,12 +1150,29 @@ xsk_umem_info *xdp_umem_configure(struct pmd_internals *internals,
@@ -140 +141 @@
-@@ -1435,8 +1456,10 @@ xsk_umem_info *xdp_umem_configure(struct pmd_internals *internals,
+@@ -1197,8 +1218,10 @@ xsk_umem_info *xdp_umem_configure(struct pmd_internals *internals,
@@ -153 +154 @@
-@@ -1880,10 +1903,13 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
+@@ -1642,10 +1665,13 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
@@ -168 +169 @@
-@@ -1894,11 +1920,13 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
+@@ -1656,11 +1682,13 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
@@ -182 +183 @@
-@@ -1955,6 +1983,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
+@@ -1717,6 +1745,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
@@ -190 +191 @@
-@@ -1970,6 +1999,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
+@@ -1732,6 +1761,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
@@ -198 +199 @@
-@@ -1982,6 +2012,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
+@@ -1744,6 +1774,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
@@ -206 +207 @@
-@@ -1989,6 +2020,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
+@@ -1751,6 +1782,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
@@ -214 +215 @@
-@@ -1996,6 +2028,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
+@@ -1758,6 +1790,7 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
@@ -222 +223 @@
-@@ -2012,8 +2045,15 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
+@@ -1774,8 +1807,15 @@ xsk_configure(struct pmd_internals *internals, struct pkt_rx_queue *rxq,
@@ -239 +240 @@
-@@ -2054,9 +2094,9 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,
+@@ -1816,9 +1856,9 @@ eth_rx_queue_setup(struct rte_eth_dev *dev,


More information about the stable mailing list