From 46a93917bf776c35dfdfe7601f8972dacd9259ee Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 26 Feb 2026 22:05:21 +0100 Subject: [PATCH 1/7] iommufd: Constify struct dma_buf_attach_ops 'struct dma_buf_attach_ops' is not modified in this driver. Constifying this structure moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 81096 13899 192 95187 173d3 drivers/iommu/iommufd/pages.o After: ===== text data bss dec hex filename 81160 13835 192 95187 173d3 drivers/iommu/iommufd/pages.o Link: https://patch.msgid.link/r/67e9126bbffa1d5c05124773a8dd4a3493be77ac.1772139886.git.christophe.jaillet@wanadoo.fr Signed-off-by: Christophe JAILLET Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/pages.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c index 9b49f0c5b459..4691456616ea 100644 --- a/drivers/iommu/iommufd/pages.c +++ b/drivers/iommu/iommufd/pages.c @@ -1450,7 +1450,7 @@ static void iopt_revoke_notify(struct dma_buf_attachment *attach) pages->dmabuf.phys.len = 0; } -static struct dma_buf_attach_ops iopt_dmabuf_attach_revoke_ops = { +static const struct dma_buf_attach_ops iopt_dmabuf_attach_revoke_ops = { .allow_peer2peer = true, .move_notify = iopt_revoke_notify, }; From 7147ec874ea08c322d779d8eba28946e294ed1f3 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Fri, 13 Feb 2026 10:36:36 -0800 Subject: [PATCH 2/7] iommufd: vfio compatibility extension check for noiommu mode VFIO_CHECK_EXTENSION should return false for TYPE1_IOMMU variants when in NO-IOMMU mode and IOMMUFD compat container is set. This change makes the behavior match VFIO_CONTAINER in noiommu mode. It also prevents userspace from incorrectly attempting to use TYPE1 IOMMU operations in a no-iommu context. Fixes: d624d6652a65 ("iommufd: vfio container FD ioctl compatibility") Link: https://patch.msgid.link/r/20260213183636.3340-1-jacob.pan@linux.microsoft.com Signed-off-by: Jacob Pan Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/vfio_compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/vfio_compat.c b/drivers/iommu/iommufd/vfio_compat.c index a258ee2f4579..acb48cdd3b00 100644 --- a/drivers/iommu/iommufd/vfio_compat.c +++ b/drivers/iommu/iommufd/vfio_compat.c @@ -283,7 +283,7 @@ static int iommufd_vfio_check_extension(struct iommufd_ctx *ictx, case VFIO_TYPE1_IOMMU: case VFIO_TYPE1v2_IOMMU: case VFIO_UNMAP_ALL: - return 1; + return !ictx->no_iommu_mode; case VFIO_NOIOMMU_IOMMU: return IS_ENABLED(CONFIG_VFIO_NOIOMMU); From 09c091fddb0b93297ea659ab48ee64f54ebeeaa2 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 12 Mar 2026 17:40:42 +0100 Subject: [PATCH 3/7] iommufd/selftest: Fix page leaks in mock_viommu_{init,destroy} mock_viommu_init() allocates two pages using __get_free_pages(..., 1), but its error path and mock_viommu_destroy() only release the first page using free_page(), leaking the second page. Use free_pages() with the matching order instead to avoid any page leaks. Fixes: 80478a2b450e ("iommufd/selftest: Add coverage for the new mmap interface") Link: https://patch.msgid.link/r/20260312164040.457293-3-thorsten.blum@linux.dev Signed-off-by: Thorsten Blum Reviewed-by: Nicolin Chen Reviewed-by: Pranjal Shrivastava Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/selftest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c index 7823142097d4..83e2215e7800 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -636,7 +636,7 @@ static void mock_viommu_destroy(struct iommufd_viommu *viommu) if (mock_viommu->mmap_offset) iommufd_viommu_destroy_mmap(&mock_viommu->core, mock_viommu->mmap_offset); - free_page((unsigned long)mock_viommu->page); + free_pages((unsigned long)mock_viommu->page, 1); mutex_destroy(&mock_viommu->queue_mutex); /* iommufd core frees mock_viommu and viommu */ @@ -870,7 +870,7 @@ static int mock_viommu_init(struct iommufd_viommu *viommu, iommufd_viommu_destroy_mmap(&mock_viommu->core, mock_viommu->mmap_offset); err_free_page: - free_page((unsigned long)mock_viommu->page); + free_pages((unsigned long)mock_viommu->page, 1); return rc; } From 67cb50aee082842077a8404337dc21b7d03829d7 Mon Sep 17 00:00:00 2001 From: Kexin Sun Date: Sat, 21 Mar 2026 18:57:59 +0800 Subject: [PATCH 4/7] iommufd: update outdated comment for renamed iommufd_hw_pagetable_alloc() The function iommufd_hw_pagetable_alloc() was renamed to iommufd_hwpt_paging_alloc() by commit 89db31635c87 ("iommufd: Derive iommufd_hwpt_paging from iommufd_hw_pagetable"). Update the stale reference in iommufd_device_auto_get_domain(). Link: https://patch.msgid.link/r/20260321105759.6832-1-kexinsun@smail.nju.edu.cn Assisted-by: unnamed:deepseek-v3.2 coccinelle Signed-off-by: Kexin Sun Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 344d620cdecc..2ae06372d63c 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -866,7 +866,7 @@ iommufd_device_auto_get_domain(struct iommufd_device *idev, ioasid_t pasid, { /* * iommufd_hw_pagetable_attach() is called by - * iommufd_hw_pagetable_alloc() in immediate attachment mode, same as + * iommufd_hwpt_paging_alloc() in immediate attachment mode, same as * iommufd_device_do_attach(). So if we are in this mode then we prefer * to use the immediate_attach path as it supports drivers that can't * directly allocate a domain. From aaca2aa92785a6ab8e3183e7184bca447a99cd76 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 29 Mar 2026 23:07:55 -0400 Subject: [PATCH 5/7] iommufd: Fix return value of iommufd_fault_fops_write() copy_from_user() may return number of bytes failed to copy, we should not pass over this number to user space to cheat that write() succeed. Instead, -EFAULT should be returned. Link: https://patch.msgid.link/r/20260330030755.12856-1-zhenzhong.duan@intel.com Cc: stable@vger.kernel.org Fixes: 07838f7fd529 ("iommufd: Add iommufd fault object") Signed-off-by: Zhenzhong Duan Reviewed-by: Lu Baolu Reviewed-by: Pranjal Shrivastava Reviewed-by: Shuai Xue Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/eventq.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index f1e686b3a265..710eef0b6004 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -187,9 +187,10 @@ static ssize_t iommufd_fault_fops_write(struct file *filep, const char __user *b mutex_lock(&fault->mutex); while (count > done) { - rc = copy_from_user(&response, buf + done, response_size); - if (rc) + if (copy_from_user(&response, buf + done, response_size)) { + rc = -EFAULT; break; + } static_assert((int)IOMMUFD_PAGE_RESP_SUCCESS == (int)IOMMU_PAGE_RESP_SUCCESS); From 8c4dc1a5025f5c35beef43fbf8ce50bb7e93b762 Mon Sep 17 00:00:00 2001 From: Pranjal Shrivastava Date: Mon, 30 Mar 2026 09:26:09 +0000 Subject: [PATCH 6/7] iommufd/selftest: Remove MOCK_IOMMUPT_AMDV1 format syzbot found that allocating a mock domain with AMDV1 format could cause a WARN_ON because the selftest enabled DYNAMIC_TOP without providing the required driver_ops. The AMDV1 format in the selftest was a placeholder and was not actually used by any of the existing selftests. Instead of adding dummy driver_ops to satisfy the requirements of a format we don't currently test, remove the AMDV1 format option from the selftest. The MOCK_IOMMUPT_DEFAULT and MOCK_IOMMUPT_HUGE formats are unaffected as they use the amdv1_mock variant which does not enable DYNAMIC_TOP. Fixes: dcd6a011a8d5 ("iommupt: Add map_pages op") Link: https://patch.msgid.link/r/20260330092609.2659235-1-praan@google.com Reported-by: syzbot+453eb7add07c3767adab@syzkaller.appspotmail.com Closes: https://lore.kernel.org/all/69c1d50b.a70a0220.3cae05.0001.GAE@google.com/ Signed-off-by: Pranjal Shrivastava Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/iommufd_test.h | 1 - drivers/iommu/iommufd/selftest.c | 31 ---------------------------- 2 files changed, 32 deletions(-) diff --git a/drivers/iommu/iommufd/iommufd_test.h b/drivers/iommu/iommufd/iommufd_test.h index 73e73e1ec158..52b78cbcc920 100644 --- a/drivers/iommu/iommufd/iommufd_test.h +++ b/drivers/iommu/iommufd/iommufd_test.h @@ -36,7 +36,6 @@ enum { enum { MOCK_IOMMUPT_DEFAULT = 0, MOCK_IOMMUPT_HUGE, - MOCK_IOMMUPT_AMDV1, }; /* These values are true for MOCK_IOMMUPT_DEFAULT */ diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c index 83e2215e7800..2a7f037060ca 100644 --- a/drivers/iommu/iommufd/selftest.c +++ b/drivers/iommu/iommufd/selftest.c @@ -421,19 +421,6 @@ static const struct iommu_dirty_ops amdv1_mock_dirty_ops = { .set_dirty_tracking = mock_domain_set_dirty_tracking, }; -static const struct iommu_domain_ops amdv1_ops = { - IOMMU_PT_DOMAIN_OPS(amdv1), - .free = mock_domain_free, - .attach_dev = mock_domain_nop_attach, - .set_dev_pasid = mock_domain_set_dev_pasid_nop, - .iotlb_sync = &mock_iotlb_sync, -}; - -static const struct iommu_dirty_ops amdv1_dirty_ops = { - IOMMU_PT_DIRTY_OPS(amdv1), - .set_dirty_tracking = mock_domain_set_dirty_tracking, -}; - static struct mock_iommu_domain * mock_domain_alloc_pgtable(struct device *dev, const struct iommu_hwpt_selftest *user_cfg, u32 flags) @@ -477,24 +464,6 @@ mock_domain_alloc_pgtable(struct device *dev, mock->domain.dirty_ops = &amdv1_mock_dirty_ops; break; } - - case MOCK_IOMMUPT_AMDV1: { - struct pt_iommu_amdv1_cfg cfg = {}; - - cfg.common.hw_max_vasz_lg2 = 64; - cfg.common.hw_max_oasz_lg2 = 52; - cfg.common.features = BIT(PT_FEAT_DYNAMIC_TOP) | - BIT(PT_FEAT_AMDV1_ENCRYPT_TABLES) | - BIT(PT_FEAT_AMDV1_FORCE_COHERENCE); - cfg.starting_level = 2; - mock->domain.ops = &amdv1_ops; - rc = pt_iommu_amdv1_init(&mock->amdv1, &cfg, GFP_KERNEL); - if (rc) - goto err_free; - if (flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING) - mock->domain.dirty_ops = &amdv1_dirty_ops; - break; - } default: rc = -EOPNOTSUPP; goto err_free; From 8602018b1f17fbdaa5e5d79f4c8603ad20640c12 Mon Sep 17 00:00:00 2001 From: Sina Hassani Date: Fri, 10 Apr 2026 11:32:44 -0700 Subject: [PATCH 7/7] iommufd: Fix a race with concurrent allocation and unmap iopt_unmap_iova_range() releases the lock on iova_rwsem inside the loop body when getting to the more expensive unmap operations. This is fine on its own, except the loop condition is based on the first area that matches the unmap address range. If a concurrent call to map picks an area that was unmapped in previous iterations, the loop mistakenly tries to unmap it. This is reproducible by having one userspace thread map buffers and pass them to another thread that unmaps them. The problem manifests as EBUSY errors with single page mappings. Fix this by advancing the start pointer after unmapping an area. This ensures each iteration only examines the IOVA range that remains mapped, which is guaranteed not to have overlaps. Cc: stable@vger.kernel.org Fixes: 51fe6141f0f6 ("iommufd: Data structure to provide IOVA to PFN mapping") Link: https://patch.msgid.link/r/CAAJpGJSR4r_ds1JOjmkqHtsBPyxu8GntoeW08Sk5RNQPmgi+tg@mail.gmail.com Signed-off-by: Sina Hassani Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/io_pagetable.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c index ee003bb2f647..24d4917105d9 100644 --- a/drivers/iommu/iommufd/io_pagetable.c +++ b/drivers/iommu/iommufd/io_pagetable.c @@ -814,6 +814,16 @@ static int iopt_unmap_iova_range(struct io_pagetable *iopt, unsigned long start, unmapped_bytes += area_last - area_first + 1; down_write(&iopt->iova_rwsem); + + /* + * After releasing the iova_rwsem concurrent allocation could + * place new areas at IOVAs we have already unmapped. Keep + * moving the start of the search forward to ignore the area + * already unmapped. + */ + if (area_last >= last) + break; + start = area_last + 1; } out_unlock_iova: