* [PATCH v2] Fixes a race in iopt_unmap_iova_range @ 2026-04-06 23:07 Sina Hassani 2026-04-07 1:12 ` Jason Gunthorpe 0 siblings, 1 reply; 8+ messages in thread From: Sina Hassani @ 2026-04-06 23:07 UTC (permalink / raw) To: jgg Cc: kevin.tian, joro, will, robin.murphy, iommu, linux-kernel, Aaron Wisner, stable Bug: 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 the previous iterations, this loop will try to mistakenly unmap them. How to reproduce: I was able to reproduce this by having one userspace thread mapping buffers and passing them to another thread that maps them. The problem easily shows up as ebusy errors if you use single page mappings. The fix: A simple fix that I implemented here is to advance the start pointer after we unmap an area. That way we are only looking at the IOVA range that is mapped and hence guaranteed to not have any overlaps in each iteration. Test: I tested this against the repro mentioned above and it works fine. Cc: stable@vger.kernel.org Signed-off-by: Sina Hassani <sina@openai.com> --- drivers/iommu/iommufd/io_pagetable.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c index ee003bb2f647..c69af341219a 100644 --- a/drivers/iommu/iommufd/io_pagetable.c +++ b/drivers/iommu/iommufd/io_pagetable.c @@ -814,6 +814,15 @@ 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); + + /* Do not reconsider things already unmapped in case of + * concurrent allocation */ + start = area_last + 1; + if (start < area_last) { + /* Overflow. IOVA ranges do not wrap around so we can + * exit here */ + break; + } } out_unlock_iova: -- 2.43.0 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Fixes a race in iopt_unmap_iova_range 2026-04-06 23:07 [PATCH v2] Fixes a race in iopt_unmap_iova_range Sina Hassani @ 2026-04-07 1:12 ` Jason Gunthorpe 2026-04-07 1:17 ` Sina Hassani 0 siblings, 1 reply; 8+ messages in thread From: Jason Gunthorpe @ 2026-04-07 1:12 UTC (permalink / raw) To: Sina Hassani Cc: kevin.tian, joro, will, robin.murphy, iommu, linux-kernel, Aaron Wisner, stable On Mon, Apr 06, 2026 at 04:07:01PM -0700, Sina Hassani wrote: > io_pagetable *iopt, unsigned long start, > unmapped_bytes += area_last - area_first + 1; > > down_write(&iopt->iova_rwsem); > + > + /* Do not reconsider things already unmapped in case of > + * concurrent allocation */ > + start = area_last + 1; area_last can be ULONG_MAX so this literally overflows to 0. It is why I formed the suggestion I gave as I did Jason ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Fixes a race in iopt_unmap_iova_range 2026-04-07 1:12 ` Jason Gunthorpe @ 2026-04-07 1:17 ` Sina Hassani 2026-04-07 1:27 ` Jason Gunthorpe 0 siblings, 1 reply; 8+ messages in thread From: Sina Hassani @ 2026-04-07 1:17 UTC (permalink / raw) To: Jason Gunthorpe Cc: kevin.tian, joro, will, robin.murphy, iommu, linux-kernel, Aaron Wisner, stable On Mon, Apr 6, 2026 at 6:12 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > On Mon, Apr 06, 2026 at 04:07:01PM -0700, Sina Hassani wrote: > > > io_pagetable *iopt, unsigned long start, > > unmapped_bytes += area_last - area_first + 1; > > > > down_write(&iopt->iova_rwsem); > > + > > + /* Do not reconsider things already unmapped in case of > > + * concurrent allocation */ > > + start = area_last + 1; > > area_last can be ULONG_MAX so this literally overflows to 0. It is why > I formed the suggestion I gave as I did > Yes, in which case the if (start < area_last) that follows will catch it. Are you suggesting I compare against ULONG_MAX instead? > Jason ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Fixes a race in iopt_unmap_iova_range 2026-04-07 1:17 ` Sina Hassani @ 2026-04-07 1:27 ` Jason Gunthorpe 2026-04-07 1:40 ` Sina Hassani 0 siblings, 1 reply; 8+ messages in thread From: Jason Gunthorpe @ 2026-04-07 1:27 UTC (permalink / raw) To: Sina Hassani Cc: kevin.tian, joro, will, robin.murphy, iommu, linux-kernel, Aaron Wisner, stable On Mon, Apr 06, 2026 at 06:17:24PM -0700, Sina Hassani wrote: > On Mon, Apr 6, 2026 at 6:12 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > > > On Mon, Apr 06, 2026 at 04:07:01PM -0700, Sina Hassani wrote: > > > > > io_pagetable *iopt, unsigned long start, > > > unmapped_bytes += area_last - area_first + 1; > > > > > > down_write(&iopt->iova_rwsem); > > > + > > > + /* Do not reconsider things already unmapped in case of > > > + * concurrent allocation */ > > > + start = area_last + 1; > > > > area_last can be ULONG_MAX so this literally overflows to 0. It is why > > I formed the suggestion I gave as I did > > > Yes, in which case the if (start < area_last) that follows will catch > it. Are you suggesting I compare against ULONG_MAX instead? iommufd does not have any overflows to 0 and rely on it tricks like this. You should just compare to the existing iteration last Jason ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Fixes a race in iopt_unmap_iova_range 2026-04-07 1:27 ` Jason Gunthorpe @ 2026-04-07 1:40 ` Sina Hassani 2026-04-07 22:03 ` Sina Hassani 2026-04-09 15:15 ` Jason Gunthorpe 0 siblings, 2 replies; 8+ messages in thread From: Sina Hassani @ 2026-04-07 1:40 UTC (permalink / raw) To: Jason Gunthorpe Cc: kevin.tian, joro, will, robin.murphy, iommu, linux-kernel, Aaron Wisner, stable On Mon, Apr 6, 2026 at 6:27 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > On Mon, Apr 06, 2026 at 06:17:24PM -0700, Sina Hassani wrote: > > On Mon, Apr 6, 2026 at 6:12 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > > > > > On Mon, Apr 06, 2026 at 04:07:01PM -0700, Sina Hassani wrote: > > > > > > > io_pagetable *iopt, unsigned long start, > > > > unmapped_bytes += area_last - area_first + 1; > > > > > > > > down_write(&iopt->iova_rwsem); > > > > + > > > > + /* Do not reconsider things already unmapped in case of > > > > + * concurrent allocation */ > > > > + start = area_last + 1; > > > > > > area_last can be ULONG_MAX so this literally overflows to 0. It is why > > > I formed the suggestion I gave as I did > > > > > Yes, in which case the if (start < area_last) that follows will catch > > it. Are you suggesting I compare against ULONG_MAX instead? > > iommufd does not have any overflows to 0 and rely on it tricks like > this. You should just compare to the existing iteration last > Just to confirm that I understand correctly, like this? + if (area_last >= last) { + break; +. } else { +. start = area_last + 1; + } > Jason ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Fixes a race in iopt_unmap_iova_range 2026-04-07 1:40 ` Sina Hassani @ 2026-04-07 22:03 ` Sina Hassani 2026-04-09 15:15 ` Jason Gunthorpe 1 sibling, 0 replies; 8+ messages in thread From: Sina Hassani @ 2026-04-07 22:03 UTC (permalink / raw) To: Jason Gunthorpe Cc: kevin.tian, joro, will, robin.murphy, iommu, linux-kernel, Aaron Wisner, stable Friendly ping On Mon, Apr 6, 2026 at 6:40 PM Sina Hassani <sina@openai.com> wrote: > > On Mon, Apr 6, 2026 at 6:27 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > > > On Mon, Apr 06, 2026 at 06:17:24PM -0700, Sina Hassani wrote: > > > On Mon, Apr 6, 2026 at 6:12 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > > > > > > > On Mon, Apr 06, 2026 at 04:07:01PM -0700, Sina Hassani wrote: > > > > > > > > > io_pagetable *iopt, unsigned long start, > > > > > unmapped_bytes += area_last - area_first + 1; > > > > > > > > > > down_write(&iopt->iova_rwsem); > > > > > + > > > > > + /* Do not reconsider things already unmapped in case of > > > > > + * concurrent allocation */ > > > > > + start = area_last + 1; > > > > > > > > area_last can be ULONG_MAX so this literally overflows to 0. It is why > > > > I formed the suggestion I gave as I did > > > > > > > Yes, in which case the if (start < area_last) that follows will catch > > > it. Are you suggesting I compare against ULONG_MAX instead? > > > > iommufd does not have any overflows to 0 and rely on it tricks like > > this. You should just compare to the existing iteration last > > > Just to confirm that I understand correctly, like this? > > + if (area_last >= last) { > + break; > +. } else { > +. start = area_last + 1; > + } > > > Jason ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Fixes a race in iopt_unmap_iova_range 2026-04-07 1:40 ` Sina Hassani 2026-04-07 22:03 ` Sina Hassani @ 2026-04-09 15:15 ` Jason Gunthorpe 2026-04-09 22:10 ` Sina Hassani 1 sibling, 1 reply; 8+ messages in thread From: Jason Gunthorpe @ 2026-04-09 15:15 UTC (permalink / raw) To: Sina Hassani Cc: kevin.tian, joro, will, robin.murphy, iommu, linux-kernel, Aaron Wisner, stable On Mon, Apr 06, 2026 at 06:40:05PM -0700, Sina Hassani wrote: > On Mon, Apr 6, 2026 at 6:27 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > > > On Mon, Apr 06, 2026 at 06:17:24PM -0700, Sina Hassani wrote: > > > On Mon, Apr 6, 2026 at 6:12 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > > > > > > > On Mon, Apr 06, 2026 at 04:07:01PM -0700, Sina Hassani wrote: > > > > > > > > > io_pagetable *iopt, unsigned long start, > > > > > unmapped_bytes += area_last - area_first + 1; > > > > > > > > > > down_write(&iopt->iova_rwsem); > > > > > + > > > > > + /* Do not reconsider things already unmapped in case of > > > > > + * concurrent allocation */ > > > > > + start = area_last + 1; > > > > > > > > area_last can be ULONG_MAX so this literally overflows to 0. It is why > > > > I formed the suggestion I gave as I did > > > > > > > Yes, in which case the if (start < area_last) that follows will catch > > > it. Are you suggesting I compare against ULONG_MAX instead? > > > > iommufd does not have any overflows to 0 and rely on it tricks like > > this. You should just compare to the existing iteration last > > > Just to confirm that I understand correctly, like this? > > + if (area_last >= last) { > + break; > +. } else { > +. start = area_last + 1; > + } Yeah that looks Ok Jason ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] Fixes a race in iopt_unmap_iova_range 2026-04-09 15:15 ` Jason Gunthorpe @ 2026-04-09 22:10 ` Sina Hassani 0 siblings, 0 replies; 8+ messages in thread From: Sina Hassani @ 2026-04-09 22:10 UTC (permalink / raw) To: Jason Gunthorpe Cc: kevin.tian, joro, will, robin.murphy, iommu, linux-kernel, Aaron Wisner, stable On Thu, Apr 9, 2026 at 8:16 AM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > On Mon, Apr 06, 2026 at 06:40:05PM -0700, Sina Hassani wrote: > > On Mon, Apr 6, 2026 at 6:27 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > > > > > On Mon, Apr 06, 2026 at 06:17:24PM -0700, Sina Hassani wrote: > > > > On Mon, Apr 6, 2026 at 6:12 PM Jason Gunthorpe <jgg@ziepe.ca> wrote: > > > > > > > > > > On Mon, Apr 06, 2026 at 04:07:01PM -0700, Sina Hassani wrote: > > > > > > > > > > > io_pagetable *iopt, unsigned long start, > > > > > > unmapped_bytes += area_last - area_first + 1; > > > > > > > > > > > > down_write(&iopt->iova_rwsem); > > > > > > + > > > > > > + /* Do not reconsider things already unmapped in case of > > > > > > + * concurrent allocation */ > > > > > > + start = area_last + 1; > > > > > > > > > > area_last can be ULONG_MAX so this literally overflows to 0. It is why > > > > > I formed the suggestion I gave as I did > > > > > > > > > Yes, in which case the if (start < area_last) that follows will catch > > > > it. Are you suggesting I compare against ULONG_MAX instead? > > > > > > iommufd does not have any overflows to 0 and rely on it tricks like > > > this. You should just compare to the existing iteration last > > > > > Just to confirm that I understand correctly, like this? > > > > + if (area_last >= last) { > > + break; > > +. } else { > > +. start = area_last + 1; > > + } > > Yeah that looks Ok Sounds good, sent you the v3 > > Jason ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-04-09 22:10 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-04-06 23:07 [PATCH v2] Fixes a race in iopt_unmap_iova_range Sina Hassani 2026-04-07 1:12 ` Jason Gunthorpe 2026-04-07 1:17 ` Sina Hassani 2026-04-07 1:27 ` Jason Gunthorpe 2026-04-07 1:40 ` Sina Hassani 2026-04-07 22:03 ` Sina Hassani 2026-04-09 15:15 ` Jason Gunthorpe 2026-04-09 22:10 ` Sina Hassani
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox