From: Stephen Hemminger <stephen@networkplumber.org>
To: sandeep.penigalapati@intel.com
Cc: dev@dpdk.org, Ciara Loftus <ciara.loftus@intel.com>,
Maryam Tahhan <mtahhan@redhat.com>,
stable@dpdk.org
Subject: Re: [PATCH v3] net/af_xdp: fix shared UMEM refcount corruption
Date: Tue, 18 Aug 2026 07:07:15 -0700 [thread overview]
Message-ID: <20260818070715.055cbc61@phoenix.local> (raw)
In-Reply-To: <20260817161301.54049-1-sandeep.penigalapati@intel.com>
On Mon, 17 Aug 2026 12:13:01 -0400
sandeep.penigalapati@intel.com wrote:
> From: Sandeep Penigalapati <sandeep.penigalapati@intel.com>
>
> 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, so 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.
>
> Also 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")
> Cc: stable@dpdk.org
>
> Signed-off-by: Sandeep Penigalapati <sandeep.penigalapati@intel.com>
> ---
AI still spots errors on this patch. It can be wrong, but it
does seem to track error paths well.
Review of [PATCH v3] net/af_xdp: fix shared UMEM refcount corruption
The core fix is right: returning NULL once refcnt >= max_xsks removes
both the unaccounted reference and the bogus reserve_before decision
that followed from it. Fixes: tag resolves to 74b46340e2d4
("net/af_xdp: support shared UMEM"), so Cc: stable is appropriate.
Findings below are against the tree with the patch applied.
Error
-----
1. drivers/net/af_xdp/rte_eth_af_xdp.c, xsk_configure()
The fq_bufs allocated before socket creation are leaked on the
error paths this patch is hardening.
In the shared case reserve_before is false, so the 2048 mbufs
obtained by rte_pktmbuf_alloc_bulk() are not handed to
reserve_fill_queue() until after the socket exists:
ret = rte_pktmbuf_alloc_bulk(rxq->umem->mb_pool, fq_bufs,
reserve_size);
...
if (reserve_before) { ... } /* skipped when sharing */
...
ret = load_custom_xdp_prog(...);
if (ret)
goto out_umem; /* fq_bufs leaked */
...
ret = create_shared_socket(...);
if (ret)
goto out_umem; /* fq_bufs leaked */
if (!reserve_before)
ret = reserve_fill_queue(...);
reserve_fill_queue_zc() frees the array itself when it fails, and
the reserve_before path therefore cleans up, but out_umem does not.
Every sharing socket that fails to bind leaks a full burst of
mbufs back out of the shared mempool, which is the same mempool
whose size now decides max_xsks.
Suggest freeing them at out_umem, guarded so the buffers are not
freed twice:
out_xsk:
xsk_socket__delete(rxq->xsk);
out_umem:
if (!reserve_before)
rte_pktmbuf_free_bulk(fq_bufs, reserve_size);
(or a bool tracking whether reserve_fill_queue() has consumed
them, if the out_xsk path is folded in later).
This predates the patch, but it is on the exact failure path the
commit message says it is hardening, and the new -ENOMEM rejection
makes it easier to reach.
Warning
-------
2. drivers/net/af_xdp/rte_eth_af_xdp.c, eth_dev_close()
The patch makes "mb_pool set, umem NULL" a deliberate marker for a
queue whose setup failed, and get_shared_umem() correctly skips
such queues with continue. eth_dev_close() still treats the same
state as end-of-list:
for (i = 0; i < internals->queue_cnt; i++) {
rxq = &internals->rx_queues[i];
if (rxq->umem == NULL)
break;
xsk_socket__delete(rxq->xsk);
...
}
If a middle queue fails setup and a later queue succeeds (easy with
per-queue mempools: queue 0 on pool A, queue 1 on pool A rejected
at capacity, queue 2 on pool B), close stops at queue 1 and leaks
queue 2's xsk socket and its UMEM reference, so that UMEM is never
destroyed.
break should be continue. Skipping is safe: a failed queue now has
umem == NULL and its socket was already deleted or never created.
3. Commit message errno does not match the code.
The message states twice that queue setup "fails cleanly with
-ENOMEM". xsk_configure() does return -ENOMEM, but
eth_rx_queue_setup() discards it:
if (xsk_configure(internals, rxq, nb_rx_desc)) {
AF_XDP_LOG_LINE(ERR, "Failed to configure xdp socket");
ret = -EINVAL;
goto err;
}
The application sees -EINVAL. Either propagate the return value
from xsk_configure() or reword the commit message; the behaviour
note for the stable branches should say what the application will
actually observe.
Info
----
4. xsk_configure(), early return leaves txq->umem stale.
rxq->umem = xdp_umem_configure(internals, rxq);
if (rxq->umem == NULL)
return -ENOMEM;
txq->umem = rxq->umem;
The out_umem path now clears both rxq->umem and txq->umem, but this
return clears only rxq->umem. Harmless on a first setup because the
queue arrays are rte_zmalloc'd, but stale after a re-setup of a
queue that previously succeeded. Clearing txq->umem here too would
make the two exits consistent.
5. The capacity check and the increment are not atomic.
get_shared_umem() drops internal_list_lock before returning, so the
load of refcnt and the fetch_add that follows are separate steps.
Two threads configuring queues on the same mempool can both observe
refcnt < max_xsks and both increment. Control-path setup is
normally single threaded so this is not urgent, but a
compare-exchange loop would actually enforce the limit the patch is
adding.
6. Log text when max_xsks is zero.
max_xsks is only assigned when the creating port has shared_umem
set, and it is populated_size / 4096, so it is 0 for a pool smaller
than 4096 mbufs or for a UMEM created by a non-shared port. In
those cases the new message reads "already at max 0 sockets", which
points away from the real cause. Worth special-casing, e.g. report
the mempool as too small for shared UMEM when max_xsks == 0.
7. get_shared_umem(), the new check can use the existing alias.
if (internals->rx_queues[i].umem == NULL)
continue;
list_rxq already points at &internals->rx_queues[i] a few lines
above; list_rxq->umem would be shorter and match the ctx_exists()
call just above it.
Notes
-----
Documentation change is accurate: max_xsks is populated_size divided
by ETH_AF_XDP_NUM_BUFFERS (4096), so 4096 * N is the correct figure
for N sockets.
Not verified: build. libxdp/libbpf were not available here, so the
af_xdp PMD was not compiled.
prev parent reply other threads:[~2026-08-18 14:07 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-12 22:08 [PATCH v1] net/af_xdp: fix shared UMEM refcount corruption sandeep.penigalapati
2026-08-12 20:56 ` Stephen Hemminger
2026-08-14 21:51 ` [PATCH v2] " sandeep.penigalapati
2026-08-14 15:46 ` Stephen Hemminger
2026-08-17 16:13 ` [PATCH v3] " sandeep.penigalapati
2026-08-18 14:07 ` Stephen Hemminger [this message]
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=20260818070715.055cbc61@phoenix.local \
--to=stephen@networkplumber.org \
--cc=ciara.loftus@intel.com \
--cc=dev@dpdk.org \
--cc=mtahhan@redhat.com \
--cc=sandeep.penigalapati@intel.com \
--cc=stable@dpdk.org \
/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.