Netdev List
 help / color / mirror / Atom feed
From: Felix Hoffmann <f3lix.dev@gmx.de>
To: netdev@vger.kernel.org, "David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>
Cc: Simon Horman <horms@kernel.org>,
	Stanislav Fomichev <sdf@fomichev.me>,
	Mina Almasry <almasrymina@google.com>,
	Kaiyuan Zhang <kaiyuanz@google.com>,
	linux-kernel@vger.kernel.org
Subject: [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister
Date: Thu, 10 Sep 2026 16:36:52 +0200	[thread overview]
Message-ID: <20260910143652.166683-1-f3lix.dev@gmx.de> (raw)

RX dma-buf bindings are invalidated by their memory provider when a
netdevice is unregistered. TX bindings have no bound RX queues and no
equivalent uninstall callback, so their physical and virtual netdevice
pointers remain live after the devices are freed.

Closing the owning netlink socket after device removal then makes
netdev_nl_sock_priv_destroy() dereference the freed physical netdevice to
hold and lock it. KASAN reports a slab-use-after-free and the kernel can
panic.

The binding can also outlive the device used for its dma-buf attachment.
Since dma_buf_attach() does not hold a reference to that device, deferred
binding cleanup can pass a freed device to dma_buf_unmap_attachment().

Invalidate TX bindings that refer to either the physical or virtual
netdevice during unregister. Keep a reference on the exact DMA device
until the attachment is unmapped. The netlink socket destructor then uses
the existing device-gone path, while delayed dma-buf cleanup retains a
valid DMA device.

NETDEV_CMD_BIND_TX does not require GENL_ADMIN_PERM. The failure was
reproduced with the binding owned by UID 65534 across module removal.

Fixes: bd61848900bf ("net: devmem: Implement TX path")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5
Signed-off-by: Felix Hoffmann <f3lix.dev@gmx.de>
---
The reproducer is available privately on request.

Testing:
- Full x86-64 kernel build with generic KASAN enabled
- Unpatched kernel: UID 65534 bind, netdevice removal, and socket close
  produced the reported KASAN use-after-free and panic
- Patched kernel: the identical sequence completed without a KASAN report
- Patched kernel: 10 additional bind, removal, and close iterations passed

 net/core/dev.c    |  2 ++
 net/core/devmem.c | 47 +++++++++++++++++++++++++++++++++++++++++++++--
 net/core/devmem.h |  7 +++++++
 3 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/net/core/dev.c b/net/core/dev.c
index ecfbd72d5d1a..c325fa7e0d6f 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -12401,6 +12401,8 @@ static void dev_memory_provider_uninstall(struct net_device *dev)
 
 		__netif_mp_uninstall_rxq(rxq, &rxq->mp_params);
 	}
+
+	net_devmem_uninstall_tx_bindings(dev);
 }
 
 /* devices must be UP and netdev_lock()'d */
diff --git a/net/core/devmem.c b/net/core/devmem.c
index f4d60654ce7f..a21a8fe92f58 100644
--- a/net/core/devmem.c
+++ b/net/core/devmem.c
@@ -77,6 +77,7 @@ void __net_devmem_dmabuf_binding_free(struct work_struct *wq)
 	dma_buf_unmap_attachment_unlocked(binding->attachment, binding->sgt,
 					  binding->direction);
 	dma_buf_detach(binding->dmabuf, binding->attachment);
+	put_device(binding->dma_dev);
 	dma_buf_put(binding->dmabuf);
 	xa_destroy(&binding->bound_rxqs);
 	percpu_ref_exit(&binding->ref);
@@ -153,6 +154,46 @@ void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
 	percpu_ref_kill(&binding->ref);
 }
 
+void net_devmem_uninstall_tx_bindings(struct net_device *dev)
+{
+	struct net_devmem_dmabuf_binding *binding;
+	struct net_devmem_dmabuf_binding *found;
+	unsigned long xa_idx;
+
+	/* Unlike RX bindings, TX bindings have no memory provider whose
+	 * uninstall callback can invalidate their net_device pointers.
+	 */
+again:
+	found = NULL;
+	rcu_read_lock();
+	xa_for_each(&net_devmem_dmabuf_bindings, xa_idx, binding) {
+		if (binding->direction != DMA_TO_DEVICE ||
+		    (READ_ONCE(binding->dev) != dev &&
+		     READ_ONCE(binding->vdev) != dev))
+			continue;
+
+		if (!net_devmem_dmabuf_binding_get(binding))
+			continue;
+		found = binding;
+		break;
+	}
+	rcu_read_unlock();
+
+	if (!found)
+		return;
+
+	binding = found;
+	mutex_lock(&binding->lock);
+	if (binding->direction == DMA_TO_DEVICE &&
+	    (binding->dev == dev || binding->vdev == dev)) {
+		WRITE_ONCE(binding->dev, NULL);
+		WRITE_ONCE(binding->vdev, NULL);
+	}
+	mutex_unlock(&binding->lock);
+	net_devmem_dmabuf_binding_put(binding);
+	goto again;
+}
+
 int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
 				    struct net_devmem_dmabuf_binding *binding,
 				    struct netlink_ext_ack *extack)
@@ -233,12 +274,13 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
 
 	binding->dmabuf = dmabuf;
 	binding->direction = direction;
+	binding->dma_dev = get_device(dma_dev);
 
 	binding->attachment = dma_buf_attach(binding->dmabuf, dma_dev);
 	if (IS_ERR(binding->attachment)) {
 		err = PTR_ERR(binding->attachment);
 		NL_SET_ERR_MSG(extack, "Failed to bind dmabuf to device");
-		goto err_exit_ref;
+		goto err_put_dma_dev;
 	}
 
 	binding->sgt = dma_buf_map_attachment_unlocked(binding->attachment,
@@ -347,7 +389,8 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
 					  direction);
 err_detach:
 	dma_buf_detach(dmabuf, binding->attachment);
-err_exit_ref:
+err_put_dma_dev:
+	put_device(binding->dma_dev);
 	percpu_ref_exit(&binding->ref);
 err_free_binding:
 	kfree(binding);
diff --git a/net/core/devmem.h b/net/core/devmem.h
index 4a293a7d1149..a40f0a7c221f 100644
--- a/net/core/devmem.h
+++ b/net/core/devmem.h
@@ -19,6 +19,8 @@ struct net_devmem_dmabuf_binding {
 	struct dma_buf *dmabuf;
 	struct dma_buf_attachment *attachment;
 	struct sg_table *sgt;
+	/* Device used to map the dma-buf. Held until the mapping is removed. */
+	struct device *dma_dev;
 	/* Physical NIC that does the actual DMA for this binding. */
 	struct net_device *dev;
 	/* Opaque cookie identifying the virtual device (e.g. netkit) the user
@@ -100,6 +102,7 @@ net_devmem_bind_dmabuf(struct net_device *dev, void *vdev,
 		       struct netlink_ext_ack *extack);
 struct net_devmem_dmabuf_binding *net_devmem_lookup_dmabuf(u32 id);
 void net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding);
+void net_devmem_uninstall_tx_bindings(struct net_device *dev);
 int net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
 				    struct net_devmem_dmabuf_binding *binding,
 				    struct netlink_ext_ack *extack);
@@ -196,6 +199,10 @@ net_devmem_unbind_dmabuf(struct net_devmem_dmabuf_binding *binding)
 {
 }
 
+static inline void net_devmem_uninstall_tx_bindings(struct net_device *dev)
+{
+}
+
 static inline int
 net_devmem_bind_dmabuf_to_queue(struct net_device *dev, u32 rxq_idx,
 				struct net_devmem_dmabuf_binding *binding,
-- 
2.43.0

             reply	other threads:[~2026-09-10 14:37 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-10 14:36 Felix Hoffmann [this message]
2026-09-10 21:29 ` [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister Stanislav Fomichev
2026-09-11 17:59 ` Mina Almasry
2026-09-12 10:35   ` Felix Hoffmann

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=20260910143652.166683-1-f3lix.dev@gmx.de \
    --to=f3lix.dev@gmx.de \
    --cc=almasrymina@google.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=kaiyuanz@google.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sdf@fomichev.me \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox