summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSina Hassani <sina@openai.com>2026-04-10 21:32:44 +0300
committerJason Gunthorpe <jgg@nvidia.com>2026-04-11 15:57:02 +0300
commit8602018b1f17fbdaa5e5d79f4c8603ad20640c12 (patch)
treecdb481dac349bb47bd0afd1fc2dd0809ef6fffac
parent8c4dc1a5025f5c35beef43fbf8ce50bb7e93b762 (diff)
downloadlinux-8602018b1f17fbdaa5e5d79f4c8603ad20640c12.tar.xz
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 <sina@openai.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
-rw-r--r--drivers/iommu/iommufd/io_pagetable.c10
1 files changed, 10 insertions, 0 deletions
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 @@ again:
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: