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);
next prev parent 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