* [PATCH] kernel/dma: dma_common_find_pages returns pages for requested address
@ 2025-01-26 9:09 Dima Stepanov
2025-01-27 11:50 ` Robin Murphy
0 siblings, 1 reply; 5+ messages in thread
From: Dima Stepanov @ 2025-01-26 9:09 UTC (permalink / raw)
To: Christoph Hellwig, Marek Szyprowski, Robin Murphy, iommu,
linux-kernel
Cc: Dima Stepanov
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.
Signed-off-by: Dima Stepanov <dstepanov.src@gmail.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];
}
/*
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH] kernel/dma: dma_common_find_pages returns pages for requested address
2025-01-26 9:09 [PATCH] kernel/dma: dma_common_find_pages returns pages for requested address Dima Stepanov
@ 2025-01-27 11:50 ` Robin Murphy
2025-01-27 17:47 ` Dima Stepanov
0 siblings, 1 reply; 5+ messages in thread
From: Robin Murphy @ 2025-01-27 11:50 UTC (permalink / raw)
To: Dima Stepanov, Christoph Hellwig, Marek Szyprowski, iommu,
linux-kernel
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@gmail.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];
> }
>
> /*
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH] kernel/dma: dma_common_find_pages returns pages for requested address
2025-01-27 11:50 ` Robin Murphy
@ 2025-01-27 17:47 ` Dima Stepanov
2025-01-28 18:53 ` Robin Murphy
0 siblings, 1 reply; 5+ messages in thread
From: Dima Stepanov @ 2025-01-27 17:47 UTC (permalink / raw)
To: Robin Murphy; +Cc: Christoph Hellwig, Marek Szyprowski, iommu, linux-kernel
On Mon, Jan 27, 2025 at 12:50 PM Robin Murphy <robin.murphy@arm.com> wrote:
>
> 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.
I see, thanks for clarification Robin. I was confused, because depending
on the backend it will work or not work. But i believe that in this case it is
undefined behavior. That is unfortunate to me, since the idea behind was:
- The memory allocated once because of device/firmware
- Different users could request a part of this memory from the kernel and
mmap it
And i didn't want to expose this offset information to the user. Wanted to
rely on the kernel to mmap the proper part of the buffer.
Thanks,
Dima.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] kernel/dma: dma_common_find_pages returns pages for requested address
2025-01-27 17:47 ` Dima Stepanov
@ 2025-01-28 18:53 ` Robin Murphy
2025-01-29 8:13 ` Dima Stepanov
0 siblings, 1 reply; 5+ messages in thread
From: Robin Murphy @ 2025-01-28 18:53 UTC (permalink / raw)
To: Dima Stepanov; +Cc: Christoph Hellwig, Marek Szyprowski, iommu, linux-kernel
On 27/01/2025 5:47 pm, Dima Stepanov wrote:
> On Mon, Jan 27, 2025 at 12:50 PM Robin Murphy <robin.murphy@arm.com> wrote:
>>
>> 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.
>
> I see, thanks for clarification Robin. I was confused, because depending
> on the backend it will work or not work. But i believe that in this case it is
> undefined behavior. That is unfortunate to me, since the idea behind was:
> - The memory allocated once because of device/firmware
> - Different users could request a part of this memory from the kernel and
> mmap it
>
> And i didn't want to expose this offset information to the user. Wanted to
> rely on the kernel to mmap the proper part of the buffer.
In principle I don't see why you shouldn't be able to create multiple
files representing different parts of one large buffer - seems like the
ops for said files would just need to be a bit cleverer, and remain
aware of the whole buffer as well as the range of their own part within
it. Then they can adjust vma->vm_pgoff accordingly before calling
dma_mmap_*() (and possibly perform their own stricter bounds checking as
well.)
Thanks,
Robin.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] kernel/dma: dma_common_find_pages returns pages for requested address
2025-01-28 18:53 ` Robin Murphy
@ 2025-01-29 8:13 ` Dima Stepanov
0 siblings, 0 replies; 5+ messages in thread
From: Dima Stepanov @ 2025-01-29 8:13 UTC (permalink / raw)
To: Robin Murphy; +Cc: Christoph Hellwig, Marek Szyprowski, iommu, linux-kernel
On Tue, Jan 28, 2025 at 7:53 PM Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 27/01/2025 5:47 pm, Dima Stepanov wrote:
> > On Mon, Jan 27, 2025 at 12:50 PM Robin Murphy <robin.murphy@arm.com> wrote:
> >>
> >> 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.
> >
> > I see, thanks for clarification Robin. I was confused, because depending
> > on the backend it will work or not work. But i believe that in this case it is
> > undefined behavior. That is unfortunate to me, since the idea behind was:
> > - The memory allocated once because of device/firmware
> > - Different users could request a part of this memory from the kernel and
> > mmap it
> >
> > And i didn't want to expose this offset information to the user. Wanted to
> > rely on the kernel to mmap the proper part of the buffer.
>
> In principle I don't see why you shouldn't be able to create multiple
> files representing different parts of one large buffer - seems like the
> ops for said files would just need to be a bit cleverer, and remain
> aware of the whole buffer as well as the range of their own part within
> it. Then they can adjust vma->vm_pgoff accordingly before calling
> dma_mmap_*() (and possibly perform their own stricter bounds checking as
> well.)
>
This is a good point, i think you are right. Indeed in this case i could go with
the vm_pgoff approach internally without exposing any information to the user.
Thanks for figuring this out!
Thanks, Dima
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-01-29 8:13 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-26 9:09 [PATCH] kernel/dma: dma_common_find_pages returns pages for requested address Dima Stepanov
2025-01-27 11:50 ` Robin Murphy
2025-01-27 17:47 ` Dima Stepanov
2025-01-28 18:53 ` Robin Murphy
2025-01-29 8:13 ` Dima Stepanov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox