From: Stanislav Fomichev <sdf.kernel@gmail.com>
To: Mina Almasry <almasrymina@google.com>
Cc: "Jakub Kicinski" <kuba@kernel.org>,
"Pavel Begunkov" <asml.silence@gmail.com>,
"David Wei" <dw@davidwei.uk>,
"Stanislav Fomichev" <sdf@fomichev.me>,
"Taehee Yoo" <ap420073@gmail.com>,
"Paolo Abeni" <pabeni@redhat.com>,
"Kaiyuan Zhang" <kaiyuanz@google.com>,
"Bobby Eshleman" <bobbyeshleman@meta.com>,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-kselftest@vger.kernel.org, linux-media@vger.kernel.org,
dri-devel@lists.freedesktop.org,
"Andrew Lunn" <andrew+netdev@lunn.ch>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@kernel.org>,
"Jesper Dangaard Brouer" <hawk@kernel.org>,
"Ilias Apalodimas" <ilias.apalodimas@linaro.org>,
"Simon Horman" <horms@kernel.org>,
"Shuah Khan" <shuah@kernel.org>,
"Sumit Semwal" <sumit.semwal@linaro.org>,
"Christian König" <christian.koenig@amd.com>,
"Dragos Tatulea" <dtatulea@nvidia.com>,
"Nikolay Aleksandrov" <razor@blackwall.org>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Tariq Toukan" <tariqt@nvidia.com>,
"Kaifeng Wang" <kaifengw@google.com>
Subject: Re: [PATCH net v4 1/4] net: devmem: unmap dma_buf synchronously on queue uninstall
Date: Thu, 15 Oct 2026 14:34:12 -0700 [thread overview]
Message-ID: <atFGd4U3qpq6ZZKY@devvm7509.cco0.facebook.com> (raw)
In-Reply-To: <20261015163915.1166043-2-almasrymina@google.com>
On 10/15, Mina Almasry wrote:
> When a bound net_device is unregistered while userspace still holds a
> netlink or TCP socket reference on a devmem binding,
> mp_dmabuf_devmem_uninstall() clears binding->dev to NULL so
> netdev_nl_sock_priv_destroy() will not touch the unregistered
> net_device, but leaves the dma_buf attachment mapped until
> __net_devmem_dmabuf_binding_free() runs later on socket close.
>
> Because dma_buf_attach() does not take a reference on dma_dev, device
> removal frees dma_dev while the attachment is still mapped. Closing the
> netlink socket afterwards triggers a slab use-after-free in
> dma_unmap_sg_attrs():
>
> BUG: KASAN: slab-use-after-free in dma_unmap_sg_attrs+0x76/0x2b0
> Read of size 8 at addr ffff88810fae64b0 by task kworker/3:1/53
> Workqueue: events __net_devmem_dmabuf_binding_free
> Call Trace:
> dma_unmap_sg_attrs+0x76/0x2b0
> unmap_udmabuf+0x8e/0x100
> dma_buf_unmap_attachment+0x145/0x330
> dma_buf_unmap_attachment_unlocked+0x96/0x130
> __net_devmem_dmabuf_binding_free+0x156/0x2e0
> Freed by task 9:
> kfree+0x1b8/0x550
> nsim_bus_dev_release+0xe/0x60
> device_release+0xcf/0x250
>
> Add net_devmem_dmabuf_binding_unmap() serialized under binding->lock
> and null out binding->sgt and binding->attachment after unmapping so
> unmapping is idempotent across queue uninstall and deferred binding
> destruction. Call it synchronously when the last bound RX queue is
> uninstalled in mp_dmabuf_devmem_uninstall(), except when a leased
> queue is unleased: the physical device is still registered and, if
> restarting its queue failed, may still be using the binding.
>
> The same use-after-free happens when the netlink socket is closed
> before the device is removed but RX tokens keep the binding alive. Once
> its queues are closed, nothing DMAs into an RX binding anymore and the
> outstanding tokens only need the net_iovs, so unmap it in
> net_devmem_unbind_dmabuf(). Make netif_mp_close_rxq() return an error
> when a queue failed to restart and may still be using the binding. Such
> a binding stays mapped and keeps binding->dev. Record a queue in
> bound_rxqs before opening it, so that a failed bind never leaves a
> queue using the binding without unbind knowing about it.
>
> Keep bindings in net_devmem_dmabuf_bindings until they are freed and
> hide unbound ones from TX lookups with an xarray mark, so that on
> NETDEV_UNREGISTER the remaining unbound RX bindings of the device can be
> found, detached and unmapped, unless the device is a virtual device
> leasing queues, for the same reason as above. The walk runs under the
> device's instance lock and is skipped when the device only moves to
> another netns. Like netdev_nl_sock_priv_destroy(),
> __net_devmem_dmabuf_binding_free() takes the instance lock of
> binding->dev before unmapping and erasing the binding, so a binding the
> walk finds under the xarray lock stays alive while it is detached.
>
> Fixes: f8350a4358fc ("net: page_pool: add a mp hook to unregister_netdevice*")
> Fixes: 0afc44d8cdf6 ("net: devmem: fix kernel panic when netlink socket close after module unload")
> Suggested-by: Stanislav Fomichev <sdf@fomichev.me>
> Cc: Taehee Yoo <ap420073@gmail.com>
> Cc: Tariq Toukan <tariqt@nvidia.com>
> Cc: Kaifeng Wang <kaifengw@google.com>
> Assisted-by: LLM
> Signed-off-by: Mina Almasry <almasrymina@google.com>
> Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
> ---
> v4:
> - Unmap RX bindings in net_devmem_unbind_dmabuf() once their queues are
> closed, and make netif_mp_close_rxq() report queue restart failures.
> The NETDEV_UNREGISTER walk only handles bindings whose queue failed
> to restart (Stanislav).
> - Drop net_devmem_lock. Walk the bindings under the xarray lock, and
> take the instance lock of binding->dev in the final free (Stanislav).
> - Record a queue in bound_rxqs before opening it, so unbind knows about
> every queue using the binding (Sashiko).
>
> v3:
> - Run the NETDEV_UNREGISTER walk under the netdev instance lock so it
> cannot race with net_devmem_unbind_dmabuf() (Sashiko).
> - Skip the walk when the device only moves to another netns (Sashiko).
>
> v2:
> - Don't unmap when a leased queue is unleased (Sashiko).
> - Unmap unbound RX bindings on NETDEV_UNREGISTER; keep bindings in
> net_devmem_dmabuf_bindings until freed, hidden from TX lookups by
> NET_DEVMEM_BINDING_LIVE (Sashiko).
> - Update the binding->lock and binding->ref comments (Sashiko).
> ---
> include/net/page_pool/memory_provider.h | 4 +-
> net/core/devmem.c | 137 +++++++++++++++++++-----
> net/core/devmem.h | 10 +-
> net/core/netdev-genl.c | 1 +
> net/core/netdev_rx_queue.c | 26 +++--
> 5 files changed, 140 insertions(+), 38 deletions(-)
>
> diff --git a/include/net/page_pool/memory_provider.h b/include/net/page_pool/memory_provider.h
> index 255ce4cfd9755..a999a9b28bc70 100644
> --- a/include/net/page_pool/memory_provider.h
> +++ b/include/net/page_pool/memory_provider.h
> @@ -26,8 +26,8 @@ void net_mp_niov_clear_page_pool(struct net_iov *niov);
> int netif_mp_open_rxq(struct net_device *dev, unsigned int rxq_idx,
> const struct pp_memory_provider_params *p,
> struct netlink_ext_ack *extack);
> -void netif_mp_close_rxq(struct net_device *dev, unsigned int rxq_idx,
> - const struct pp_memory_provider_params *old_p);
> +int netif_mp_close_rxq(struct net_device *dev, unsigned int rxq_idx,
> + const struct pp_memory_provider_params *old_p);
>
> /**
> * net_mp_netmem_place_in_cache() - give a netmem to a page pool
> diff --git a/net/core/devmem.c b/net/core/devmem.c
> index f4d60654ce7fd..20d95b24d5951 100644
> --- a/net/core/devmem.c
> +++ b/net/core/devmem.c
> @@ -12,6 +12,7 @@
> #include <linux/mm.h>
> #include <linux/netdevice.h>
> #include <linux/types.h>
> +#include <net/netdev_lock.h>
> #include <net/netdev_queues.h>
> #include <net/netdev_rx_queue.h>
> #include <net/page_pool/helpers.h>
> @@ -20,6 +21,7 @@
> #include <net/tcp.h>
> #include <trace/events/page_pool.h>
>
> +#include "dev.h"
> #include "devmem.h"
> #include "mp_dmabuf_devmem.h"
> #include "page_pool_priv.h"
> @@ -27,6 +29,7 @@
> /* Device memory support */
>
> static DEFINE_XARRAY_FLAGS(net_devmem_dmabuf_bindings, XA_FLAGS_ALLOC1);
> +#define NET_DEVMEM_BINDING_LIVE XA_MARK_1
>
> static const struct memory_provider_ops dmabuf_devmem_ops;
>
> @@ -58,9 +61,26 @@ static void net_devmem_dmabuf_binding_release(struct percpu_ref *ref)
> schedule_work(&binding->unbind_w);
> }
>
> +static void
> +net_devmem_dmabuf_binding_unmap(struct net_devmem_dmabuf_binding *binding)
> +{
> + lockdep_assert_held(&binding->lock);
> +
> + if (!binding->sgt)
> + return;
> +
> + dma_buf_unmap_attachment_unlocked(binding->attachment, binding->sgt,
> + binding->direction);
> + dma_buf_detach(binding->dmabuf, binding->attachment);
> + binding->sgt = NULL;
> + binding->attachment = NULL;
> +}
> +
> void __net_devmem_dmabuf_binding_free(struct work_struct *wq)
> {
> struct net_devmem_dmabuf_binding *binding = container_of(wq, typeof(*binding), unbind_w);
> + netdevice_tracker dev_tracker;
> + struct net_device *dev;
>
> size_t size, avail;
>
> @@ -74,9 +94,23 @@ void __net_devmem_dmabuf_binding_free(struct work_struct *wq)
> size, avail))
> gen_pool_destroy(binding->chunk_pool);
>
> - dma_buf_unmap_attachment_unlocked(binding->attachment, binding->sgt,
> - binding->direction);
> - dma_buf_detach(binding->dmabuf, binding->attachment);
> + mutex_lock(&binding->lock);
> + dev = binding->dev;
> + if (dev)
> + netdev_hold(dev, &dev_tracker, GFP_KERNEL);
> + mutex_unlock(&binding->lock);
> +
> + if (dev)
> + netdev_lock(dev);
> + mutex_lock(&binding->lock);
> + net_devmem_dmabuf_binding_unmap(binding);
> + mutex_unlock(&binding->lock);
> + xa_erase(&net_devmem_dmabuf_bindings, binding->id);
> + if (dev) {
> + netdev_unlock(dev);
> + netdev_put(dev, &dev_tracker);
> + }
> +
> dma_buf_put(binding->dmabuf);
> xa_destroy(&binding->bound_rxqs);
> percpu_ref_exit(&binding->ref);
> @@ -128,11 +162,13 @@ void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
> struct netdev_rx_queue *rxq;
> unsigned long xa_idx;
> unsigned int rxq_idx;
> + bool busy = false;
>
> - xa_erase(&net_devmem_dmabuf_bindings, binding->id);
> + xa_clear_mark(&net_devmem_dmabuf_bindings, binding->id,
> + NET_DEVMEM_BINDING_LIVE);
>
> /* Ensure no tx net_devmem_lookup_dmabuf() are in flight after the
> - * erase.
> + * mark is cleared.
> */
> synchronize_net();
>
> @@ -147,12 +183,62 @@ void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
>
> rxq_idx = get_netdev_rx_queue_index(rxq);
>
> - netif_mp_close_rxq(binding->dev, rxq_idx, &mp_params);
> + if (netif_mp_close_rxq(binding->dev, rxq_idx, &mp_params))
> + busy = true;
> + }
> +
> + if (binding->direction == DMA_FROM_DEVICE && binding->dev && !busy) {
> + mutex_lock(&binding->lock);
> + WRITE_ONCE(binding->dev, NULL);
> + net_devmem_dmabuf_binding_unmap(binding);
> + mutex_unlock(&binding->lock);
> }
>
> percpu_ref_kill(&binding->ref);
> }
>
[..]
> +static bool
> +net_devmem_dev_unregister_rx(struct net_devmem_dmabuf_binding *binding,
> + unsigned long xa_idx, struct net_device *dev)
> +{
> + return binding->direction == DMA_FROM_DEVICE &&
> + READ_ONCE(binding->dev) == dev &&
> + !xa_get_mark(&net_devmem_dmabuf_bindings, xa_idx,
> + NET_DEVMEM_BINDING_LIVE);
> +}
super nit: if you happen to respin, I'd just open code this part in
net_devmem_dev_unregister, it reads funny to me idk, but maybe it's just me.
But even as is LGTM, thanks!
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
next prev parent reply other threads:[~2026-10-15 21:34 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 16:39 [PATCH net v4 0/4] net: devmem: fix RX and TX binding teardown on device unregistration Mina Almasry
2026-10-15 16:39 ` [PATCH net v4 1/4] net: devmem: unmap dma_buf synchronously on queue uninstall Mina Almasry
2026-10-15 21:34 ` Stanislav Fomichev [this message]
2026-10-15 16:39 ` [PATCH net v4 2/4] net: devmem: detach TX bindings on NETDEV_UNREGISTER Mina Almasry
2026-10-15 21:34 ` Stanislav Fomichev
2026-10-15 16:39 ` [PATCH net v4 3/4] netdevsim: support devmem RX and TX bindings Mina Almasry
2026-10-15 21:34 ` Stanislav Fomichev
2026-10-15 16:39 ` [PATCH net v4 4/4] selftests: net: add devmem RX and TX netdev unregister tests Mina Almasry
2026-10-15 21:34 ` Stanislav Fomichev
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=atFGd4U3qpq6ZZKY@devvm7509.cco0.facebook.com \
--to=sdf.kernel@gmail.com \
--cc=almasrymina@google.com \
--cc=andrew+netdev@lunn.ch \
--cc=ap420073@gmail.com \
--cc=asml.silence@gmail.com \
--cc=bobbyeshleman@meta.com \
--cc=christian.koenig@amd.com \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=dri-devel@lists.freedesktop.org \
--cc=dtatulea@nvidia.com \
--cc=dw@davidwei.uk \
--cc=edumazet@kernel.org \
--cc=hawk@kernel.org \
--cc=horms@kernel.org \
--cc=ilias.apalodimas@linaro.org \
--cc=kaifengw@google.com \
--cc=kaiyuanz@google.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=razor@blackwall.org \
--cc=sdf@fomichev.me \
--cc=shuah@kernel.org \
--cc=sumit.semwal@linaro.org \
--cc=tariqt@nvidia.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.