[PATCH] RDMA/core: Fix use-after-free when netns exit races compat dev removal
From: Serhat Kumral
Date: Tue Aug 18 2026 - 04:12:59 EST
A compat device is removed from two places: disable_device() during the
ib device unregistration, and rdma_dev_exit_net() when the netns it
belongs to dies. remove_one_compat_dev() lets the xa_erase() decide
which of the two performs the removal, but drops compat_devs_mutex
before device_del(), so the caller that finds nothing to erase returns
without waiting for the removal the other one is running.
When that caller is rdma_dev_exit_net(), cleanup_net() carries on while
the compat device is still alive. Its kobject is tagged with the dying
net, so the device_del() still in progress dereferences net->uevent_sock
after uevent_net_exit() has freed it:
CPU0 (ib-unreg-wq) CPU1 (netns wq)
------------------ ---------------
__ib_unregister_device()
disable_device()
remove_compat_devs()
remove_one_compat_dev()
xa_erase()
device_del(cdev)
...
cleanup_net()
rdma_dev_exit_net()
remove_one_compat_dev()
xa_erase()
// returns without waiting
uevent_net_exit()
kfree(net->uevent_sock)
kobject_uevent_env()
net->uevent_sock->sk // UAF
BUG: KASAN: slab-use-after-free in kobject_uevent_env+0xb6f/0xc80
Read of size 8 at addr ffff888103806490 by task kworker/u16:1/41
Workqueue: ib-unreg-wq ib_unregister_work
Call Trace:
kobject_uevent_env+0xb6f/0xc80
device_del+0x737/0xc10
disable_device+0x1ad/0x230
__ib_unregister_device+0x229/0x3f0
Freed by task 12:
kfree+0x1b3/0x550
ops_undo_list+0x273/0x8c0
cleanup_net+0x3b3/0x720
Fix by holding compat_devs_mutex across the whole removal, so that once
remove_one_compat_dev() returns the compat device is gone no matter
which caller removed it.
Fixes: 4e0f7b907072 ("RDMA/core: Implement compat device/sysfs tree in net namespace")
Assisted-by: Claude:claude-opus-5
Signed-off-by: Serhat Kumral <serhatkumral1@xxxxxxxxx>
---
With this patch applied the report no longer shows up.
drivers/infiniband/core/device.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index d954eda63134..a80ac69ef986 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -1005,14 +1005,20 @@ static void remove_one_compat_dev(struct ib_device *device, u32 id)
{
struct ib_core_device *cdev;
+ /*
+ * Hold the lock across device_del(): the other remover may have won
+ * the xa_erase() and still be inside device_del(), and the netns exit
+ * path has to wait for it instead of letting cleanup_net() free the
+ * netns state the compat device is still tagged with.
+ */
mutex_lock(&device->compat_devs_mutex);
cdev = xa_erase(&device->compat_devs, id);
- mutex_unlock(&device->compat_devs_mutex);
if (cdev) {
ib_free_port_attrs(cdev);
device_del(&cdev->dev);
put_device(&cdev->dev);
}
+ mutex_unlock(&device->compat_devs_mutex);
}
static void remove_compat_devs(struct ib_device *device)
--
2.53.0