Linux Media Controller development
 help / color / mirror / Atom feed
From: "Christian König" <christian.koenig@amd.com>
To: Baineng Shou <shoubaineng@gmail.com>,
	Sumit Semwal <sumit.semwal@linaro.org>,
	Benjamin Gaignard <benjamin.gaignard@collabora.com>,
	Brian Starkey <Brian.Starkey@arm.com>,
	John Stultz <jstultz@google.com>,
	"T . J . Mercier" <tjmercier@google.com>,
	Sandeep Patil <sspatil@android.com>,
	"Andrew F . Davis" <afd@ti.com>
Cc: stable@vger.kernel.org, linux-media@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] dma-buf: dma-heap: don't publish fd before copy_to_user() succeeds
Date: Fri, 10 Jul 2026 14:33:43 +0200	[thread overview]
Message-ID: <99679491-8dae-460b-b662-81b130829956@amd.com> (raw)
In-Reply-To: <20260710105740.3080070-1-shoubaineng@gmail.com>

On 7/10/26 12:57, Baineng Shou wrote:
> DMA_HEAP_IOCTL_ALLOC allocates a dma-buf and installs an fd into the
> caller's fd table via dma_buf_fd() -> fd_install() before
> dma_heap_ioctl() copies the result back to userspace.  If the trailing
> copy_to_user() fails, userspace never learns the fd number, but the
> fd (and the underlying dma-buf reference) are already visible to
> other threads in the same process and are leaked for the lifetime of
> the process.
> 
> The obvious "close it on the failure path" fix is unsafe: once
> fd_install() has run, another thread can already dup() the fd, send
> it via SCM_RIGHTS, or close() it and let its number be reused, so a
> subsequent close_fd() from the ioctl path can operate on an unrelated
> file.  This was pointed out by Christian König on v1 [1].

IIRC it was Greg who pointed that out numerous times, I'm just repeating what I was told.

> 
> Restructure the allocation path so that fd_install() is the last,
> unfailable step of a successful ioctl:
> 
>   1. heap->ops->allocate()      creates the dma_buf.
>   2. get_unused_fd_flags()      reserves an fd number in the caller's
>                                 fd table without publishing it, so
>                                 no other thread can observe it.
>   3. copy_to_user()             delivers the fd number to userspace;
>                                 on failure the fd is returned with
>                                 put_unused_fd() and the dma_buf
>                                 reference is dropped with
>                                 dma_buf_put(), leaving no user-
>                                 visible state behind.
>   4. fd_install()               publishes the fd -- from here on the
>                                 ioctl cannot fail.
> 
> To make this possible, dma_heap_ioctl_allocate() is refactored to
> return the struct dma_buf * directly (returning ERR_PTR on failure)
> so the caller holds the dmabuf reference across steps 3 and 4.
> The fd is written into the kdata buffer before copy_to_user() so
> the reserved fd number reaches userspace atomically with the install.
> 
> The failure at step 3 is easily reachable from userspace: pass a
> struct dma_heap_allocation_data that lives in a page whose protection
> is flipped to PROT_READ between copy_from_user() and copy_to_user()
> (e.g. via mprotect()).  Before this change each such ioctl leaks one
> dmabuf fd; after it, the fd table is unchanged on failure and only
> /dev/dma_heap/<name> remains open.
> 
> No UAPI or heap-driver interface change.
> 
> [1] https://lore.kernel.org/dri-devel/175e98de-f414-47d7-81c1-c0fe0a8f7f62@amd.com/
> 
> Fixes: c02a81fba74f ("dma-buf: Add dma-buf heaps framework")
> Cc: stable@vger.kernel.org
> Signed-off-by: Baineng Shou <shoubaineng@gmail.com>

Patch looks sane to me, but somebody with more background in DMA-buf heaps should probably take a look as well.

Acked-by: Christian König <christian.koenig@amd.com>

> ---
>  drivers/dma-buf/dma-heap.c | 80 +++++++++++++++++++-------------------
>  1 file changed, 40 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c
> index a76bf3f8b071..0a9bf62eb06c 100644
> --- a/drivers/dma-buf/dma-heap.c
> +++ b/drivers/dma-buf/dma-heap.c
> @@ -55,33 +55,6 @@ MODULE_PARM_DESC(mem_accounting,
>  		 "Enable cgroup-based memory accounting for dma-buf heap allocations (default=false).");
>  EXPORT_SYMBOL_NS_GPL(mem_accounting, "DMA_BUF_HEAP");
>  
> -static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
> -				 u32 fd_flags,
> -				 u64 heap_flags)
> -{
> -	struct dma_buf *dmabuf;
> -	int fd;
> -
> -	/*
> -	 * Allocations from all heaps have to begin
> -	 * and end on page boundaries.
> -	 */
> -	len = PAGE_ALIGN(len);
> -	if (!len)
> -		return -EINVAL;
> -
> -	dmabuf = heap->ops->allocate(heap, len, fd_flags, heap_flags);
> -	if (IS_ERR(dmabuf))
> -		return PTR_ERR(dmabuf);
> -
> -	fd = dma_buf_fd(dmabuf, fd_flags);
> -	if (fd < 0) {
> -		dma_buf_put(dmabuf);
> -		/* just return, as put will call release and that will free */
> -	}
> -	return fd;
> -}
> -
>  static int dma_heap_open(struct inode *inode, struct file *file)
>  {
>  	struct dma_heap *heap;
> @@ -99,30 +72,42 @@ static int dma_heap_open(struct inode *inode, struct file *file)
>  	return 0;
>  }
>  
> -static long dma_heap_ioctl_allocate(struct file *file, void *data)
> +static struct dma_buf *dma_heap_ioctl_allocate(struct file *file, void *data)
>  {
>  	struct dma_heap_allocation_data *heap_allocation = data;
>  	struct dma_heap *heap = file->private_data;
> +	struct dma_buf *dmabuf;
>  	int fd;
> +	size_t len;
>  
>  	if (heap_allocation->fd)
> -		return -EINVAL;
> +		return ERR_PTR(-EINVAL);
>  
>  	if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
> -		return -EINVAL;
> +		return ERR_PTR(-EINVAL);
>  
>  	if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
> -		return -EINVAL;
> +		return ERR_PTR(-EINVAL);
> +
> +	len = PAGE_ALIGN(heap_allocation->len);
> +	if (!len)
> +		return ERR_PTR(-EINVAL);
> +
> +	dmabuf = heap->ops->allocate(heap, len, heap_allocation->fd_flags,
> +				     heap_allocation->heap_flags);
>  
> -	fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
> -				   heap_allocation->fd_flags,
> -				   heap_allocation->heap_flags);
> -	if (fd < 0)
> -		return fd;
> +	if (IS_ERR(dmabuf))
> +		return dmabuf;
> +
> +	fd = get_unused_fd_flags(heap_allocation->fd_flags);
> +	if (fd < 0) {
> +		dma_buf_put(dmabuf);
> +		return ERR_PTR(fd);
> +	}
>  
>  	heap_allocation->fd = fd;
>  
> -	return 0;
> +	return dmabuf;
>  }
>  
>  static unsigned int dma_heap_ioctl_cmds[] = {
> @@ -138,6 +123,8 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
>  	unsigned int in_size, out_size, drv_size, ksize;
>  	int nr = _IOC_NR(ucmd);
>  	int ret = 0;
> +	int fd;
> +	struct dma_buf *dmabuf;
>  
>  	if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
>  		return -EINVAL;
> @@ -174,15 +161,28 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
>  
>  	switch (kcmd) {
>  	case DMA_HEAP_IOCTL_ALLOC:
> -		ret = dma_heap_ioctl_allocate(file, kdata);
> +		dmabuf = dma_heap_ioctl_allocate(file, kdata);
> +
> +		if (IS_ERR(dmabuf)) {
> +			ret = PTR_ERR(dmabuf);
> +			break;
> +		}
> +
> +		fd = ((struct dma_heap_allocation_data *)kdata)->fd;
> +		if (copy_to_user((void __user *)arg, kdata, out_size) != 0) {
> +			put_unused_fd(fd);
> +			dma_buf_put(dmabuf);
> +			ret = -EFAULT;
> +		} else {
> +			fd_install(fd, dmabuf->file);
> +		}
> +
>  		break;
>  	default:
>  		ret = -ENOTTY;
>  		goto err;
>  	}
>  
> -	if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
> -		ret = -EFAULT;
>  err:
>  	if (kdata != stack_kdata)
>  		kfree(kdata);


  reply	other threads:[~2026-07-10 12:33 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-03  8:09 [PATCH] dma-buf: dma-heap: close installed fd when copy_to_user() fails Baineng Shou
2026-07-03  8:26 ` Christian König
2026-07-10 10:57 ` [PATCH v2] dma-buf: dma-heap: don't publish fd before copy_to_user() succeeds Baineng Shou
2026-07-10 12:33   ` Christian König [this message]
2026-07-11  4:14 ` Baineng Shou

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=99679491-8dae-460b-b662-81b130829956@amd.com \
    --to=christian.koenig@amd.com \
    --cc=Brian.Starkey@arm.com \
    --cc=afd@ti.com \
    --cc=benjamin.gaignard@collabora.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=jstultz@google.com \
    --cc=linaro-mm-sig@lists.linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=shoubaineng@gmail.com \
    --cc=sspatil@android.com \
    --cc=stable@vger.kernel.org \
    --cc=sumit.semwal@linaro.org \
    --cc=tjmercier@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox