[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <50b71ac2-b84f-4f09-8795-d1c89a7c56af@arm.com>
Date: Mon, 27 Jan 2025 11:50:41 +0000
From: Robin Murphy <robin.murphy@....com>
To: Dima Stepanov <dstepanov.src@...il.com>, Christoph Hellwig <hch@....de>,
Marek Szyprowski <m.szyprowski@...sung.com>, iommu@...ts.linux.dev,
linux-kernel@...r.kernel.org
Subject: Re: [PATCH] kernel/dma: dma_common_find_pages returns pages for
requested address
On 2025-01-26 9:09 am, Dima Stepanov wrote:
> Recently hit an issue on an attempt to mmap allocated DMA memory to the
> user space. It isn't the case for any device, but only if dma_mmap_attrs
> call will use the iommu_dma_mmap() function as a backend.
> If the kernel address (cpu_addr) passed to the iommu_dma_mmap function
> is the same as returned by allocator (dma_alloc_coherent) then memory
> will be mapped correctly. But if the address passed is inside the
> allocated region, then the mapping in the user space will still refer
> to the start of the region and not to the middle of it.
>
> The reason for it is the dma_common_find_pages() call, which returns the
> very first page of the vm structure regardless of the cpu_addr passed.
>
> The idea for the fix is to return not the first page in the vm
> structure, but the page related to the requested cpu_addr.
No, that's a bug in the caller of dma_mmap_attrs(). As the kerneldoc
says, cpu_addr/dma_addr/size must still represent the whole buffer as
returned by the allocator - any offset for the mapping itself relative
to the start of the buffer is expressed in vma->pgoff.
Thanks,
Robin.
> Signed-off-by: Dima Stepanov <dstepanov.src@...il.com>
> ---
> kernel/dma/remap.c | 5 ++++-
> 1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/dma/remap.c b/kernel/dma/remap.c
> index 9e2afad1c615..238fbf2821a6 100644
> --- a/kernel/dma/remap.c
> +++ b/kernel/dma/remap.c
> @@ -9,12 +9,15 @@
> struct page **dma_common_find_pages(void *cpu_addr)
> {
> struct vm_struct *area = find_vm_area(cpu_addr);
> + int i;
>
> if (!area || !(area->flags & VM_DMA_COHERENT))
> return NULL;
> WARN(area->flags != VM_DMA_COHERENT,
> "unexpected flags in area: %p\n", cpu_addr);
> - return area->pages;
> + i = (PAGE_ALIGN((uintptr_t)cpu_addr) - (uintptr_t)area->addr) >>
> PAGE_SHIFT;
> +
> + return &area->pages[i];
> }
>
> /*
Powered by blists - more mailing lists