public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [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