Skip to content

Commit d93b3ac

Browse files
Antonios Motakisawilliam
authored andcommitted
VFIO: vfio_iommu_type1: fix bug caused by break in nested loop
In vfio_iommu_type1.c there is a bug in vfio_dma_do_map, when checking that pages are not already mapped. Since the check is being done in a for loop nested within the main loop, breaking out of it does not create the intended behavior. If the underlying IOMMU driver returns a non-NULL value, this will be ignored and mapping the DMA range will be attempted anyway, leading to unpredictable behavior. This interracts badly with the ARM SMMU driver issue fixed in the patch that was submitted with the title: "[PATCH 2/2] ARM: SMMU: return NULL on error in arm_smmu_iova_to_phys" Both fixes are required in order to use the vfio_iommu_type1 driver with an ARM SMMU. This patch refactors the function slightly, in order to also make this kind of bug less likely. Signed-off-by: Antonios Motakis <a.motakis@virtualopensystems.com> Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
1 parent d0e639c commit d93b3ac

File tree

1 file changed

+21
-19
lines changed

1 file changed

+21
-19
lines changed

drivers/vfio/vfio_iommu_type1.c

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
545545
long npage;
546546
int ret = 0, prot = 0;
547547
uint64_t mask;
548+
struct vfio_dma *dma = NULL;
549+
unsigned long pfn;
548550

549551
end = map->iova + map->size;
550552

@@ -587,8 +589,6 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
587589
}
588590

589591
for (iova = map->iova; iova < end; iova += size, vaddr += size) {
590-
struct vfio_dma *dma = NULL;
591-
unsigned long pfn;
592592
long i;
593593

594594
/* Pin a contiguous chunk of memory */
@@ -597,16 +597,15 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
597597
if (npage <= 0) {
598598
WARN_ON(!npage);
599599
ret = (int)npage;
600-
break;
600+
goto out;
601601
}
602602

603603
/* Verify pages are not already mapped */
604604
for (i = 0; i < npage; i++) {
605605
if (iommu_iova_to_phys(iommu->domain,
606606
iova + (i << PAGE_SHIFT))) {
607-
vfio_unpin_pages(pfn, npage, prot, true);
608607
ret = -EBUSY;
609-
break;
608+
goto out_unpin;
610609
}
611610
}
612611

@@ -616,8 +615,7 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
616615
if (ret) {
617616
if (ret != -EBUSY ||
618617
map_try_harder(iommu, iova, pfn, npage, prot)) {
619-
vfio_unpin_pages(pfn, npage, prot, true);
620-
break;
618+
goto out_unpin;
621619
}
622620
}
623621

@@ -672,9 +670,8 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
672670
dma = kzalloc(sizeof(*dma), GFP_KERNEL);
673671
if (!dma) {
674672
iommu_unmap(iommu->domain, iova, size);
675-
vfio_unpin_pages(pfn, npage, prot, true);
676673
ret = -ENOMEM;
677-
break;
674+
goto out_unpin;
678675
}
679676

680677
dma->size = size;
@@ -685,16 +682,21 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu,
685682
}
686683
}
687684

688-
if (ret) {
689-
struct vfio_dma *tmp;
690-
iova = map->iova;
691-
size = map->size;
692-
while ((tmp = vfio_find_dma(iommu, iova, size))) {
693-
int r = vfio_remove_dma_overlap(iommu, iova,
694-
&size, tmp);
695-
if (WARN_ON(r || !size))
696-
break;
697-
}
685+
WARN_ON(ret);
686+
mutex_unlock(&iommu->lock);
687+
return ret;
688+
689+
out_unpin:
690+
vfio_unpin_pages(pfn, npage, prot, true);
691+
692+
out:
693+
iova = map->iova;
694+
size = map->size;
695+
while ((dma = vfio_find_dma(iommu, iova, size))) {
696+
int r = vfio_remove_dma_overlap(iommu, iova,
697+
&size, dma);
698+
if (WARN_ON(r || !size))
699+
break;
698700
}
699701

700702
mutex_unlock(&iommu->lock);

0 commit comments

Comments
 (0)