* [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister
@ 2026-09-10 14:36 Felix Hoffmann
2026-09-10 21:29 ` Stanislav Fomichev
2026-09-11 17:59 ` Mina Almasry
0 siblings, 2 replies; 4+ messages in thread
From: Felix Hoffmann @ 2026-09-10 14:36 UTC (permalink / raw)
To: netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Simon Horman, Stanislav Fomichev, Mina Almasry, Kaiyuan Zhang,
linux-kernel
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
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister
2026-09-10 14:36 [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister Felix Hoffmann
@ 2026-09-10 21:29 ` Stanislav Fomichev
2026-09-11 17:59 ` Mina Almasry
1 sibling, 0 replies; 4+ messages in thread
From: Stanislav Fomichev @ 2026-09-10 21:29 UTC (permalink / raw)
To: Felix Hoffmann
Cc: netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Stanislav Fomichev, Mina Almasry,
Kaiyuan Zhang, linux-kernel
On 09/10, Felix Hoffmann wrote:
> 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.
As I mentioned on https://lore.kernel.org/netdev/aqC4XSt7JrrTv4sG@devvm7509.cco0.facebook.com/
I'd like us to have an in-tree selftest for that.
And Dragos also had a suggestion to use a new helper for checking if
there are any outstanding tx dmabufs attached...
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister
2026-09-10 14:36 [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister Felix Hoffmann
2026-09-10 21:29 ` Stanislav Fomichev
@ 2026-09-11 17:59 ` Mina Almasry
2026-09-12 10:35 ` Felix Hoffmann
1 sibling, 1 reply; 4+ messages in thread
From: Mina Almasry @ 2026-09-11 17:59 UTC (permalink / raw)
To: Felix Hoffmann
Cc: netdev, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Stanislav Fomichev, Kaiyuan Zhang,
linux-kernel
On Thu, Sep 10, 2026 at 7:37 AM Felix Hoffmann <f3lix.dev@gmx.de> wrote:
>
> 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.
> + */
Please remove these LLM generated comments that make no sense. The
comment is correct but no one reading this code is wondering if the TX
binding have a memory provider.
> +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;
> +
Can you do a deeper investigation on what to do here or explain why
this is correct. I'm not sure we should do anything on the vdev
unregestiring? hmm...
FWIW I think probably an in-tree test would not go to net, but would
be a separate patch that goes to net-next? Maybe? IDK. Up to stan.
--
Thanks,
Mina
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister
2026-09-11 17:59 ` Mina Almasry
@ 2026-09-12 10:35 ` Felix Hoffmann
0 siblings, 0 replies; 4+ messages in thread
From: Felix Hoffmann @ 2026-09-12 10:35 UTC (permalink / raw)
To: Mina Almasry, Stanislav Fomichev
Cc: netdev, davem, edumazet, kuba, pabeni, horms, sdf, kaiyuanz,
bobbyeshleman, dtatulea, uqbarz, linux-kernel
Thanks for the review, Mina, and for the pointers, Stan.
I removed the RX-versus-TX comment and the added DMA-device field comment.
You were right to question invalidating the entire binding when the
virtual device disappears. The socket destructor uses binding->dev;
binding->vdev is only a cookie for the route comparison in
net_devmem_get_binding(). The revision clears only vdev on virtual-device
unregister and leaves the physical binding intact. Clearing the cookie
still matters because keeping its old address could match a newly
allocated netdevice. Physical-device unregister clears both pointers.
I factored the TX-binding scan into a reference-taking lookup helper. It
holds RCU while finding the binding and taking its reference; invalidation
then takes binding->lock, which also protects the socket destructor's
netdevice reference acquisition. The temporary binding reference covers
the interval outside RCU and is released after invalidation.
I read Dragos's proposed netdev_has_dmabuf_binding() helper. Its boolean
physical-device/DMA-device query is useful for data-direct, but teardown
also needs a binding reference and virtual-cookie matching. The factored
lookup handles those requirements; I have not added an unused exported
boolean API. Dragos, does this separation work for your planned helper?
I added a separate KUnit test patch covering real netdevice unregister
with test-owned binding metadata: physical removal, virtual removal, both
orders, multiple matches, unrelated/RX bindings, and reference balancing.
All five cases pass under KASAN and lockdep. These are kernel unit tests;
they do not replace the hardware DMA/netlink selftest Stan proposed. I
have not included or claimed to run that hardware test.
The current netdev guidance says that a fix and its related test should
go to the same tree as separate commits. I therefore prepared the fix and
KUnit coverage as a two-patch v2 for net. If you prefer the KUnit patch in
net-next, I can split the routing accordingly.
I also added credit and a link to Hengbin's earlier report, and corrected
the missing Cc recipients identified by the submission checks.
I'll send v2 as a fresh thread with a link to v1 once this discussion is
settled, as the netdev submission guidance requests.
Thanks,
Felix
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-09-12 10:36 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10 14:36 [PATCH net] net: devmem: fix TX binding UAF on netdevice unregister Felix Hoffmann
2026-09-10 21:29 ` Stanislav Fomichev
2026-09-11 17:59 ` Mina Almasry
2026-09-12 10:35 ` Felix Hoffmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox