> Subject: [PATCH] udmabuf: fix DMA direction mismatch in
> release_udmabuf()
> 
> begin_cpu_udmabuf() maps the sg_table with the caller-provided
> direction
> (e.g., DMA_TO_DEVICE for a write-only sync), and caches it in ubuf->sg
> for reuse.  However, release_udmabuf() always unmaps this sg_table with
> a hardcoded DMA_BIDIRECTIONAL, regardless of the direction that was
> originally used for the mapping.
> 
> With CONFIG_DMA_API_DEBUG=y this produces:
> 
>   DMA-API: misc udmabuf: device driver frees DMA memory with different
>   direction [device address=0x000000044a123000] [size=4096 bytes]
>   [mapped with DMA_TO_DEVICE] [unmapped with
> DMA_BIDIRECTIONAL]
> 
> The issue was found during video playback when GStreamer performed a
> write-only DMA_BUF_IOCTL_SYNC on a udmabuf.  It can be reproduced
> with CONFIG_DMA_API_DEBUG=y by creating a udmabuf from a memfd,
> performing a write-only sync (DMA_BUF_SYNC_WRITE without
> DMA_BUF_SYNC_READ), and closing the file descriptor.
> 
> Fix this by storing the DMA direction used when the sg_table is first
> created in begin_cpu_udmabuf(), and passing that same direction to
> put_sg_table() in release_udmabuf().
> 
> Fixes: 284562e1f348 ("udmabuf: implement
> begin_cpu_access/end_cpu_access hooks")
> Cc: [email protected]
> Signed-off-by: Mikhail Gavrilov <[email protected]>
> ---
>  drivers/dma-buf/udmabuf.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/dma-buf/udmabuf.c b/drivers/dma-buf/udmabuf.c
> index 94b8ecb892bb..d0836febefdd 100644
> --- a/drivers/dma-buf/udmabuf.c
> +++ b/drivers/dma-buf/udmabuf.c
> @@ -40,6 +40,7 @@ struct udmabuf {
>       struct folio **pinned_folios;
> 
>       struct sg_table *sg;
> +     enum dma_data_direction sg_dir;
>       struct miscdevice *device;
>       pgoff_t *offsets;
>  };
> @@ -235,7 +236,7 @@ static void release_udmabuf(struct dma_buf *buf)
>       struct device *dev = ubuf->device->this_device;
> 
>       if (ubuf->sg)
> -             put_sg_table(dev, ubuf->sg, DMA_BIDIRECTIONAL);
> +             put_sg_table(dev, ubuf->sg, ubuf->sg_dir);
> 
>       deinit_udmabuf(ubuf);
>       kfree(ubuf);
> @@ -253,6 +254,8 @@ static int begin_cpu_udmabuf(struct dma_buf
> *buf,
>               if (IS_ERR(ubuf->sg)) {
>                       ret = PTR_ERR(ubuf->sg);
>                       ubuf->sg = NULL;
> +             } else {
> +                     ubuf->sg_dir = direction;
Reviewed-by: Vivek Kasireddy <[email protected]>

Thanks,
Vivek

>               }
>       } else {
>               dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
> --
> 2.53.0

Reply via email to