[PATCH] RDMA/core: Fix use-after-free when netns exit races compat dev removal

Serhat Kumral <[email protected]>
Newsgroups org.kernel.vger.linux-rdma,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <[email protected]>
---
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.