mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2] xen/gntdev: Fix the abuse of underlying struct page in DMA-buf import
@ 2024-01-07 10:34 Oleksandr Tyshchenko
  2024-01-08 12:05 ` Daniel Vetter
  0 siblings, 1 reply; 3+ messages in thread
From: Oleksandr Tyshchenko @ 2024-01-07 10:34 UTC (permalink / raw)
  To: xen-devel, linux-kernel
  Cc: Oleksandr Tyshchenko, Christian König, Daniel Vetter,
	Juergen Gross, Stefano Stabellini

From: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>

DO NOT access the underlying struct page of an sg table exported
by DMA-buf in dmabuf_imp_to_refs(), this is not allowed.
Please see drivers/dma-buf/dma-buf.c:mangle_sg_table() for details.

Fortunately, here (for special Xen device) we can avoid using
pages and calculate gfns directly from dma addresses provided by
the sg table.

Suggested-by: Daniel Vetter <daniel@ffwll.ch>
Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
Acked-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
---
Please note, I didn't manage to test the patch against the latest master branch
on real HW (patch was only build tested there). Patch was tested on Arm64
guests using Linux v5.10.41 from vendor's BSP, this is the environment where
running this use-case is possible and to which I have an access (Xen PV display
with zero-copy and backend domain as a buffer provider - be-alloc=1, so dma-buf
import part was involved). A little bit old, but the dma-buf import code
in gntdev-dmabuf.c hasn't been changed much since that time, all context
remains allmost the same according to my code inspection.

  v2:
   - add R-b and A-b
   - fix build warning noticed by kernel test robot by initializing
     "ret" in case of error
     https://lore.kernel.org/oe-kbuild-all/202401062122.it6zvLG0-lkp@intel.com/
---
---
 drivers/xen/gntdev-dmabuf.c | 44 ++++++++++++++++---------------------
 1 file changed, 19 insertions(+), 25 deletions(-)

diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c
index 4440e626b797..272c0ab01ef5 100644
--- a/drivers/xen/gntdev-dmabuf.c
+++ b/drivers/xen/gntdev-dmabuf.c
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/dma-buf.h>
+#include <linux/dma-direct.h>
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
@@ -50,7 +51,7 @@ struct gntdev_dmabuf {
 
 	/* Number of pages this buffer has. */
 	int nr_pages;
-	/* Pages of this buffer. */
+	/* Pages of this buffer (only for dma-buf export). */
 	struct page **pages;
 };
 
@@ -484,7 +485,7 @@ static int dmabuf_exp_from_refs(struct gntdev_priv *priv, int flags,
 /* DMA buffer import support. */
 
 static int
-dmabuf_imp_grant_foreign_access(struct page **pages, u32 *refs,
+dmabuf_imp_grant_foreign_access(unsigned long *gfns, u32 *refs,
 				int count, int domid)
 {
 	grant_ref_t priv_gref_head;
@@ -507,7 +508,7 @@ dmabuf_imp_grant_foreign_access(struct page **pages, u32 *refs,
 		}
 
 		gnttab_grant_foreign_access_ref(cur_ref, domid,
-						xen_page_to_gfn(pages[i]), 0);
+						gfns[i], 0);
 		refs[i] = cur_ref;
 	}
 
@@ -529,7 +530,6 @@ static void dmabuf_imp_end_foreign_access(u32 *refs, int count)
 
 static void dmabuf_imp_free_storage(struct gntdev_dmabuf *gntdev_dmabuf)
 {
-	kfree(gntdev_dmabuf->pages);
 	kfree(gntdev_dmabuf->u.imp.refs);
 	kfree(gntdev_dmabuf);
 }
@@ -549,12 +549,6 @@ static struct gntdev_dmabuf *dmabuf_imp_alloc_storage(int count)
 	if (!gntdev_dmabuf->u.imp.refs)
 		goto fail;
 
-	gntdev_dmabuf->pages = kcalloc(count,
-				       sizeof(gntdev_dmabuf->pages[0]),
-				       GFP_KERNEL);
-	if (!gntdev_dmabuf->pages)
-		goto fail;
-
 	gntdev_dmabuf->nr_pages = count;
 
 	for (i = 0; i < count; i++)
@@ -576,7 +570,8 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev,
 	struct dma_buf *dma_buf;
 	struct dma_buf_attachment *attach;
 	struct sg_table *sgt;
-	struct sg_page_iter sg_iter;
+	struct sg_dma_page_iter sg_iter;
+	unsigned long *gfns;
 	int i;
 
 	dma_buf = dma_buf_get(fd);
@@ -624,26 +619,25 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev,
 
 	gntdev_dmabuf->u.imp.sgt = sgt;
 
-	/* Now convert sgt to array of pages and check for page validity. */
+	gfns = kcalloc(count, sizeof(*gfns), GFP_KERNEL);
+	if (!gfns) {
+		ret = ERR_PTR(-ENOMEM);
+		goto fail_unmap;
+	}
+
+	/* Now convert sgt to array of gfns without accessing underlying pages. */
 	i = 0;
-	for_each_sgtable_page(sgt, &sg_iter, 0) {
-		struct page *page = sg_page_iter_page(&sg_iter);
-		/*
-		 * Check if page is valid: this can happen if we are given
-		 * a page from VRAM or other resources which are not backed
-		 * by a struct page.
-		 */
-		if (!pfn_valid(page_to_pfn(page))) {
-			ret = ERR_PTR(-EINVAL);
-			goto fail_unmap;
-		}
+	for_each_sgtable_dma_page(sgt, &sg_iter, 0) {
+		dma_addr_t addr = sg_page_iter_dma_address(&sg_iter);
+		unsigned long pfn = bfn_to_pfn(XEN_PFN_DOWN(dma_to_phys(dev, addr)));
 
-		gntdev_dmabuf->pages[i++] = page;
+		gfns[i++] = pfn_to_gfn(pfn);
 	}
 
-	ret = ERR_PTR(dmabuf_imp_grant_foreign_access(gntdev_dmabuf->pages,
+	ret = ERR_PTR(dmabuf_imp_grant_foreign_access(gfns,
 						      gntdev_dmabuf->u.imp.refs,
 						      count, domid));
+	kfree(gfns);
 	if (IS_ERR(ret))
 		goto fail_end_access;
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v2] xen/gntdev: Fix the abuse of underlying struct page in DMA-buf import
  2024-01-07 10:34 [PATCH v2] xen/gntdev: Fix the abuse of underlying struct page in DMA-buf import Oleksandr Tyshchenko
@ 2024-01-08 12:05 ` Daniel Vetter
  2024-01-09 17:03   ` Oleksandr Tyshchenko
  0 siblings, 1 reply; 3+ messages in thread
From: Daniel Vetter @ 2024-01-08 12:05 UTC (permalink / raw)
  To: Oleksandr Tyshchenko
  Cc: xen-devel, linux-kernel, Oleksandr Tyshchenko,
	Christian König, Juergen Gross, Stefano Stabellini

On Sun, 7 Jan 2024 at 11:35, Oleksandr Tyshchenko <olekstysh@gmail.com> wrote:
>
> From: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
>
> DO NOT access the underlying struct page of an sg table exported
> by DMA-buf in dmabuf_imp_to_refs(), this is not allowed.
> Please see drivers/dma-buf/dma-buf.c:mangle_sg_table() for details.
>
> Fortunately, here (for special Xen device) we can avoid using
> pages and calculate gfns directly from dma addresses provided by
> the sg table.
>
> Suggested-by: Daniel Vetter <daniel@ffwll.ch>
> Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
> Acked-by: Christian König <christian.koenig@amd.com>
> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
> ---
> Please note, I didn't manage to test the patch against the latest master branch
> on real HW (patch was only build tested there). Patch was tested on Arm64
> guests using Linux v5.10.41 from vendor's BSP, this is the environment where
> running this use-case is possible and to which I have an access (Xen PV display
> with zero-copy and backend domain as a buffer provider - be-alloc=1, so dma-buf
> import part was involved). A little bit old, but the dma-buf import code
> in gntdev-dmabuf.c hasn't been changed much since that time, all context
> remains allmost the same according to my code inspection.
>
>   v2:
>    - add R-b and A-b
>    - fix build warning noticed by kernel test robot by initializing
>      "ret" in case of error
>      https://lore.kernel.org/oe-kbuild-all/202401062122.it6zvLG0-lkp@intel.com/
> ---
> ---
>  drivers/xen/gntdev-dmabuf.c | 44 ++++++++++++++++---------------------
>  1 file changed, 19 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c
> index 4440e626b797..272c0ab01ef5 100644
> --- a/drivers/xen/gntdev-dmabuf.c
> +++ b/drivers/xen/gntdev-dmabuf.c
> @@ -11,6 +11,7 @@
>  #include <linux/kernel.h>
>  #include <linux/errno.h>
>  #include <linux/dma-buf.h>
> +#include <linux/dma-direct.h>
>  #include <linux/slab.h>
>  #include <linux/types.h>
>  #include <linux/uaccess.h>
> @@ -50,7 +51,7 @@ struct gntdev_dmabuf {
>
>         /* Number of pages this buffer has. */
>         int nr_pages;
> -       /* Pages of this buffer. */
> +       /* Pages of this buffer (only for dma-buf export). */
>         struct page **pages;
>  };
>
> @@ -484,7 +485,7 @@ static int dmabuf_exp_from_refs(struct gntdev_priv *priv, int flags,
>  /* DMA buffer import support. */
>
>  static int
> -dmabuf_imp_grant_foreign_access(struct page **pages, u32 *refs,
> +dmabuf_imp_grant_foreign_access(unsigned long *gfns, u32 *refs,
>                                 int count, int domid)
>  {
>         grant_ref_t priv_gref_head;
> @@ -507,7 +508,7 @@ dmabuf_imp_grant_foreign_access(struct page **pages, u32 *refs,
>                 }
>
>                 gnttab_grant_foreign_access_ref(cur_ref, domid,
> -                                               xen_page_to_gfn(pages[i]), 0);
> +                                               gfns[i], 0);
>                 refs[i] = cur_ref;
>         }
>
> @@ -529,7 +530,6 @@ static void dmabuf_imp_end_foreign_access(u32 *refs, int count)
>
>  static void dmabuf_imp_free_storage(struct gntdev_dmabuf *gntdev_dmabuf)
>  {
> -       kfree(gntdev_dmabuf->pages);
>         kfree(gntdev_dmabuf->u.imp.refs);
>         kfree(gntdev_dmabuf);
>  }
> @@ -549,12 +549,6 @@ static struct gntdev_dmabuf *dmabuf_imp_alloc_storage(int count)
>         if (!gntdev_dmabuf->u.imp.refs)
>                 goto fail;
>
> -       gntdev_dmabuf->pages = kcalloc(count,
> -                                      sizeof(gntdev_dmabuf->pages[0]),
> -                                      GFP_KERNEL);
> -       if (!gntdev_dmabuf->pages)
> -               goto fail;
> -
>         gntdev_dmabuf->nr_pages = count;
>
>         for (i = 0; i < count; i++)
> @@ -576,7 +570,8 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev,
>         struct dma_buf *dma_buf;
>         struct dma_buf_attachment *attach;
>         struct sg_table *sgt;
> -       struct sg_page_iter sg_iter;
> +       struct sg_dma_page_iter sg_iter;
> +       unsigned long *gfns;
>         int i;
>
>         dma_buf = dma_buf_get(fd);
> @@ -624,26 +619,25 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev,
>
>         gntdev_dmabuf->u.imp.sgt = sgt;
>
> -       /* Now convert sgt to array of pages and check for page validity. */
> +       gfns = kcalloc(count, sizeof(*gfns), GFP_KERNEL);
> +       if (!gfns) {
> +               ret = ERR_PTR(-ENOMEM);
> +               goto fail_unmap;
> +       }
> +
> +       /* Now convert sgt to array of gfns without accessing underlying pages. */
>         i = 0;
> -       for_each_sgtable_page(sgt, &sg_iter, 0) {
> -               struct page *page = sg_page_iter_page(&sg_iter);
> -               /*
> -                * Check if page is valid: this can happen if we are given
> -                * a page from VRAM or other resources which are not backed
> -                * by a struct page.
> -                */
> -               if (!pfn_valid(page_to_pfn(page))) {
> -                       ret = ERR_PTR(-EINVAL);
> -                       goto fail_unmap;
> -               }
> +       for_each_sgtable_dma_page(sgt, &sg_iter, 0) {

Maybe add a comment here to explain why this is done and why it's ok?
Either way:

Acked-by: Daniel Vetter <daniel@ffwll.ch>


> +               dma_addr_t addr = sg_page_iter_dma_address(&sg_iter);
> +               unsigned long pfn = bfn_to_pfn(XEN_PFN_DOWN(dma_to_phys(dev, addr)));
>
> -               gntdev_dmabuf->pages[i++] = page;
> +               gfns[i++] = pfn_to_gfn(pfn);
>         }
>
> -       ret = ERR_PTR(dmabuf_imp_grant_foreign_access(gntdev_dmabuf->pages,
> +       ret = ERR_PTR(dmabuf_imp_grant_foreign_access(gfns,
>                                                       gntdev_dmabuf->u.imp.refs,
>                                                       count, domid));
> +       kfree(gfns);
>         if (IS_ERR(ret))
>                 goto fail_end_access;
>
> --
> 2.34.1
>


-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v2] xen/gntdev: Fix the abuse of underlying struct page in DMA-buf import
  2024-01-08 12:05 ` Daniel Vetter
@ 2024-01-09 17:03   ` Oleksandr Tyshchenko
  0 siblings, 0 replies; 3+ messages in thread
From: Oleksandr Tyshchenko @ 2024-01-09 17:03 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: xen-devel, Oleksandr Tyshchenko, linux-kernel,
	Christian König, Juergen Gross, Stefano Stabellini



On 08.01.24 14:05, Daniel Vetter wrote:

Hello Daniel


> On Sun, 7 Jan 2024 at 11:35, Oleksandr Tyshchenko <olekstysh@gmail.com> wrote:
>>
>> From: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
>>
>> DO NOT access the underlying struct page of an sg table exported
>> by DMA-buf in dmabuf_imp_to_refs(), this is not allowed.
>> Please see drivers/dma-buf/dma-buf.c:mangle_sg_table() for details.
>>
>> Fortunately, here (for special Xen device) we can avoid using
>> pages and calculate gfns directly from dma addresses provided by
>> the sg table.
>>
>> Suggested-by: Daniel Vetter <daniel@ffwll.ch>
>> Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
>> Acked-by: Christian König <christian.koenig@amd.com>
>> Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
>> ---
>> Please note, I didn't manage to test the patch against the latest master branch
>> on real HW (patch was only build tested there). Patch was tested on Arm64
>> guests using Linux v5.10.41 from vendor's BSP, this is the environment where
>> running this use-case is possible and to which I have an access (Xen PV display
>> with zero-copy and backend domain as a buffer provider - be-alloc=1, so dma-buf
>> import part was involved). A little bit old, but the dma-buf import code
>> in gntdev-dmabuf.c hasn't been changed much since that time, all context
>> remains allmost the same according to my code inspection.
>>
>>    v2:
>>     - add R-b and A-b
>>     - fix build warning noticed by kernel test robot by initializing
>>       "ret" in case of error
>>       https://urldefense.com/v3/__https://lore.kernel.org/oe-kbuild-all/202401062122.it6zvLG0-lkp@intel.com/__;!!GF_29dbcQIUBPA!38-mwT9HCtOeZC3m4I-m9n0hragYMHfmWcHKgDxEpGs9mg35M0bpPWWORK8aichxHtO36GZ_JnCWTLdJXdZYBmCv$ [lore[.]kernel[.]org]
>> ---
>> ---
>>   drivers/xen/gntdev-dmabuf.c | 44 ++++++++++++++++---------------------
>>   1 file changed, 19 insertions(+), 25 deletions(-)
>>
>> diff --git a/drivers/xen/gntdev-dmabuf.c b/drivers/xen/gntdev-dmabuf.c
>> index 4440e626b797..272c0ab01ef5 100644
>> --- a/drivers/xen/gntdev-dmabuf.c
>> +++ b/drivers/xen/gntdev-dmabuf.c
>> @@ -11,6 +11,7 @@
>>   #include <linux/kernel.h>
>>   #include <linux/errno.h>
>>   #include <linux/dma-buf.h>
>> +#include <linux/dma-direct.h>
>>   #include <linux/slab.h>
>>   #include <linux/types.h>
>>   #include <linux/uaccess.h>
>> @@ -50,7 +51,7 @@ struct gntdev_dmabuf {
>>
>>          /* Number of pages this buffer has. */
>>          int nr_pages;
>> -       /* Pages of this buffer. */
>> +       /* Pages of this buffer (only for dma-buf export). */
>>          struct page **pages;
>>   };
>>
>> @@ -484,7 +485,7 @@ static int dmabuf_exp_from_refs(struct gntdev_priv *priv, int flags,
>>   /* DMA buffer import support. */
>>
>>   static int
>> -dmabuf_imp_grant_foreign_access(struct page **pages, u32 *refs,
>> +dmabuf_imp_grant_foreign_access(unsigned long *gfns, u32 *refs,
>>                                  int count, int domid)
>>   {
>>          grant_ref_t priv_gref_head;
>> @@ -507,7 +508,7 @@ dmabuf_imp_grant_foreign_access(struct page **pages, u32 *refs,
>>                  }
>>
>>                  gnttab_grant_foreign_access_ref(cur_ref, domid,
>> -                                               xen_page_to_gfn(pages[i]), 0);
>> +                                               gfns[i], 0);
>>                  refs[i] = cur_ref;
>>          }
>>
>> @@ -529,7 +530,6 @@ static void dmabuf_imp_end_foreign_access(u32 *refs, int count)
>>
>>   static void dmabuf_imp_free_storage(struct gntdev_dmabuf *gntdev_dmabuf)
>>   {
>> -       kfree(gntdev_dmabuf->pages);
>>          kfree(gntdev_dmabuf->u.imp.refs);
>>          kfree(gntdev_dmabuf);
>>   }
>> @@ -549,12 +549,6 @@ static struct gntdev_dmabuf *dmabuf_imp_alloc_storage(int count)
>>          if (!gntdev_dmabuf->u.imp.refs)
>>                  goto fail;
>>
>> -       gntdev_dmabuf->pages = kcalloc(count,
>> -                                      sizeof(gntdev_dmabuf->pages[0]),
>> -                                      GFP_KERNEL);
>> -       if (!gntdev_dmabuf->pages)
>> -               goto fail;
>> -
>>          gntdev_dmabuf->nr_pages = count;
>>
>>          for (i = 0; i < count; i++)
>> @@ -576,7 +570,8 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev,
>>          struct dma_buf *dma_buf;
>>          struct dma_buf_attachment *attach;
>>          struct sg_table *sgt;
>> -       struct sg_page_iter sg_iter;
>> +       struct sg_dma_page_iter sg_iter;
>> +       unsigned long *gfns;
>>          int i;
>>
>>          dma_buf = dma_buf_get(fd);
>> @@ -624,26 +619,25 @@ dmabuf_imp_to_refs(struct gntdev_dmabuf_priv *priv, struct device *dev,
>>
>>          gntdev_dmabuf->u.imp.sgt = sgt;
>>
>> -       /* Now convert sgt to array of pages and check for page validity. */
>> +       gfns = kcalloc(count, sizeof(*gfns), GFP_KERNEL);
>> +       if (!gfns) {
>> +               ret = ERR_PTR(-ENOMEM);
>> +               goto fail_unmap;
>> +       }
>> +
>> +       /* Now convert sgt to array of gfns without accessing underlying pages. */
>>          i = 0;
>> -       for_each_sgtable_page(sgt, &sg_iter, 0) {
>> -               struct page *page = sg_page_iter_page(&sg_iter);
>> -               /*
>> -                * Check if page is valid: this can happen if we are given
>> -                * a page from VRAM or other resources which are not backed
>> -                * by a struct page.
>> -                */
>> -               if (!pfn_valid(page_to_pfn(page))) {
>> -                       ret = ERR_PTR(-EINVAL);
>> -                       goto fail_unmap;
>> -               }
>> +       for_each_sgtable_dma_page(sgt, &sg_iter, 0) {
> 
> Maybe add a comment here to explain why this is done and why it's ok?


Makes sense, will do for v3.


> Either way:
> 
> Acked-by: Daniel Vetter <daniel@ffwll.ch>


Thanks!


[snip]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2024-01-09 18:23 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-07 10:34 [PATCH v2] xen/gntdev: Fix the abuse of underlying struct page in DMA-buf import Oleksandr Tyshchenko
2024-01-08 12:05 ` Daniel Vetter
2024-01-09 17:03   ` Oleksandr Tyshchenko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®