Fix possible overflow in the address expression used as the second argument to iommu_map() and iommu_unmap(). Without an explicit cast, this expression may overflow when 'r->offset' or 'i' are large. Cast the result to unsigned long before shifting to ensure correct IOVA computation and prevent unintended wraparound.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
Cc: stable@vger.kernel.org # v4.4+ Signed-off-by: Alexey Nepomnyashih sdl@nppct.ru --- drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c index 201022ae9214..17a0e1a46211 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c @@ -334,7 +334,7 @@ gk20a_instobj_dtor_iommu(struct nvkm_memory *memory) /* Unmap pages from GPU address space and free them */ for (i = 0; i < node->base.mn->length; i++) { iommu_unmap(imem->domain, - (r->offset + i) << imem->iommu_pgshift, PAGE_SIZE); + ((unsigned long)r->offset + i) << imem->iommu_pgshift, PAGE_SIZE); dma_unmap_page(dev, node->dma_addrs[i], PAGE_SIZE, DMA_BIDIRECTIONAL); __free_page(node->pages[i]); @@ -472,7 +472,7 @@ gk20a_instobj_ctor_iommu(struct gk20a_instmem *imem, u32 npages, u32 align,
/* Map into GPU address space */ for (i = 0; i < npages; i++) { - u32 offset = (r->offset + i) << imem->iommu_pgshift; + unsigned long offset = ((unsigned long)r->offset + i) << imem->iommu_pgshift;
ret = iommu_map(imem->domain, offset, node->dma_addrs[i], PAGE_SIZE, IOMMU_READ | IOMMU_WRITE,
The unmap logic assumes a fixed step size of PAGE_SIZE, but the actual IOVA step depends on iommu_pgshift, not PAGE_SHIFT. If iommu_pgshift > PAGE_SHIFT, this results in mismatched offsets and causes iommu_unmap() to target incorrect addresses, potentially leaving mappings intact or corrupting IOMMU state.
Fix this by recomputing the offset per index using the same logic as in the map loop, ensuring symmetry and correctness.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
Cc: stable@vger.kernel.org # v4.3+ Fixes: a7f6da6e758c ("drm/nouveau/instmem/gk20a: add IOMMU support") Signed-off-by: Alexey Nepomnyashih sdl@nppct.ru --- drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c index 17a0e1a46211..f58e0d4fb2b1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c @@ -481,8 +481,9 @@ gk20a_instobj_ctor_iommu(struct gk20a_instmem *imem, u32 npages, u32 align, nvkm_error(subdev, "IOMMU mapping failure: %d\n", ret);
while (i-- > 0) { - offset -= PAGE_SIZE; - iommu_unmap(imem->domain, offset, PAGE_SIZE); + iommu_unmap(imem->domain, + ((unsigned long)r->offset + i) << imem->iommu_pgshift, + PAGE_SIZE); } goto release_area; }
linux-stable-mirror@lists.linaro.org