[PATCH] iommufd: Reject opens until module initialization succeeds
From: Kyle Zeng
Date: Fri Oct 30 2026 - 00:28:41 EST
iommufd_init() publishes /dev/iommu before registering the optional
/dev/vfio/vfio compatibility node and running the optional self-test
initialization. Either later operation can fail. Since file operations
can acquire a reference to a COMING module, an already-open file can
survive the failed initialization and refer to freed iommufd module
memory.
Gate the shared open callback until all fallible initialization has
completed. Publish the ready flag with release semantics and read it
with acquire semantics so successful opens see the initialized state.
An early rejected open also needs to drop its module-backed operations
table. misc_open() holds misc_mtx across the callback, so deregistration
waits for it, but the VFS's fops_put() on the failed open happens later.
Restore the inode's core character-device operations before returning
-ENODEV, avoiding that remaining reference to the failed module.
Fixes: 01f70cbb26ea ("iommufd: Allow iommufd to supply /dev/vfio/vfio")
Fixes: 23a1b46f15d5 ("iommufd/selftest: Make the mock iommu driver into a real driver")
Signed-off-by: Kyle Zeng <kylebot@xxxxxxxxxx>
---
I have a reproducer for this issue and can share via DM
drivers/iommu/iommufd/main.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c
index 9a921b153162..781a8d63dda9 100644
--- a/drivers/iommu/iommufd/main.c
+++ b/drivers/iommu/iommufd/main.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/smp.h>
#include <uapi/linux/iommufd.h>
#include "io_pagetable.h"
@@ -30,6 +31,7 @@ struct iommufd_object_ops {
};
static const struct iommufd_object_ops iommufd_object_ops[];
static struct miscdevice vfio_misc_dev;
+static bool iommufd_ready;
struct iommufd_object *_iommufd_object_alloc(struct iommufd_ctx *ictx,
size_t size,
@@ -316,6 +318,17 @@ static int iommufd_fops_open(struct inode *inode, struct file *filp)
{
struct iommufd_ctx *ictx;
+ /* Pairs with the ready publication at the end of iommufd_init(). */
+ if (!smp_load_acquire(&iommufd_ready)) {
+ /*
+ * misc_deregister() waits for this callback, but not the
+ * VFS's fops_put() after a failed open. Restore the core
+ * character-device operations before dropping misc_mtx.
+ */
+ replace_fops(filp, fops_get(inode->i_fop));
+ return -ENODEV;
+ }
+
ictx = kzalloc_obj(*ictx, GFP_KERNEL_ACCOUNT);
if (!ictx)
return -ENOMEM;
@@ -799,6 +812,8 @@ static int __init iommufd_init(void)
ret = iommufd_test_init();
if (ret)
goto err_vfio_misc;
+ /* Pairs with iommufd_fops_open(); no fallible setup may follow. */
+ smp_store_release(&iommufd_ready, true);
return 0;
err_vfio_misc:
base-commit: d89e1d186ec9271bf78e62771779e9fc03bf0fc3
--
2.56.0