> 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