From: Matt Vollrath <tactii@gmail.com>
To: Robin Murphy <robin.murphy@arm.com>,
"Joerg Roedel (AMD)" <joro@8bytes.org>,
Will Deacon <will@kernel.org>
Cc: iommu@lists.linux.dev, Matt Vollrath <tactii@gmail.com>
Subject: [PATCH] iommu/dma: Don't free under/oversized IOVA ranges
Date: Thu, 1 Oct 2026 02:59:53 -0400 [thread overview]
Message-ID: <20261001065953.2717823-1-tactii@gmail.com> (raw)
When unmapping DMA from a translating IOMMU, the range unmapped by the
IOMMU page table may not match the size specified in the request. An
unmapped length of 0 suggests that either the request was rejected by
the IOMMU or the IOVA did not exist in the page table. A size mismatch
suggests that either the unmap was partially successful or the request
was made with an incorrect IOVA and/or size.
Because the return value of iommu_unmap_fast() is already in hand in
__iommu_dma_unmap(), hardening is cheap.
In the case of an unmapped length of 0, skip the IOTLB sync (because
there is nothing to sync) and skip freeing of the unmapped IOVA which
would corrupt the IOVA allocator state.
In the case of size mismatch, flush the IOTLB synchronously and
intentionally leak the IOVA allocation if it existed. A potential leak
of a valid IOVA is more predictable than potentially corrupting the
IOVA allocator state. The existing WARN_ON() is preserved.
The intention is to prevent failure cascades from intermittent driver
bugs. If one bad unmap causes a future mapping error (because the
allocator cache believed that an IOVA was free), then we are at the
mercy of the driver's map error handling.
Signed-off-by: Matt Vollrath <tactii@gmail.com>
Assisted-by: Claude:claude-opus-5-5
---
This was tested with QEMU 8.2 against virtio-iommu and emulation of:
* VT-d
* AMD-Vi
* SMMUv3
* s390 zPCI
---
drivers/iommu/dma-iommu.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd4..029ccc3b55cf 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -836,7 +836,22 @@ static void __iommu_dma_unmap(struct device *dev, dma_addr_t dma_addr,
iotlb_gather.queued = READ_ONCE(cookie->fq_domain);
unmapped = iommu_unmap_fast(domain, dma_addr, size, &iotlb_gather);
- WARN_ON(unmapped != size);
+ if (WARN_ON(unmapped != size)) {
+ if (unmapped) {
+ /*
+ * If we changed the page table, we must sync IOTLB.
+ * Freeing the IOVA could corrupt the allocator,
+ * so leak it instead.
+ */
+ if (iotlb_gather.queued) {
+ iommu_flush_iotlb_all(domain);
+ iommu_put_pages_list(&iotlb_gather.freelist);
+ } else {
+ iommu_iotlb_sync(domain, &iotlb_gather);
+ }
+ }
+ return;
+ }
if (!iotlb_gather.queued)
iommu_iotlb_sync(domain, &iotlb_gather);
base-commit: cec2dc7663e7182c311162163d71b1bbdfdbf09b
--
2.43.0
reply other threads:[~2026-10-01 7:00 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261001065953.2717823-1-tactii@gmail.com \
--to=tactii@gmail.com \
--cc=iommu@lists.linux.dev \
--cc=joro@8bytes.org \
--cc=robin.murphy@arm.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox