CVE-2026-89996: dma-buf: dma-heap: don't publish fd before copy_to_user() succeeds
In the Linux kernel, the following vulnerability has been resolved:
dma-buf: dma-heap: don't publish fd before copytouser() succeeds
DMAHEAPIOCTLALLOC allocates a dma-buf and installs an fd into the caller's fd table via dmabuffd() -> fdinstall() before dmaheapioctl() copies the result back to userspace. If the trailing copytouser() 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 fdinstall() has run, another thread can already dup() the fd, send it via SCMRIGHTS, or close() it and let its number be reused, so a subsequent closefd() from the ioctl path can operate on an unrelated file. This was pointed out by Christian König on v1 [1].
Restructure the allocation path so that fdinstall() is the last, unfailable step of a successful ioctl:
1. heap->ops->allocate() creates the dmabuf. 2. getunusedfdflags() reserves an fd number in the caller's fd table without publishing it, so no other thread can observe it. 3. copytouser() delivers the fd number to userspace; on failure the fd is returned with putunusedfd() and the dmabuf reference is dropped with dmabufput(), leaving no user- visible state behind. 4. dmabuffdinstall() publishes the fd and emits the tracedmabuffd tracepoint -- from here on the ioctl cannot fail.
A new dmabuffdinstall() helper is introduced in dma-buf.c to wrap fdinstall() together with the DMABUFTRACE() call, preserving the export tracing that dmabuffd() provides. dmaheapioctlallocate() is refactored to return the struct dmabuf directly (returning ERRPTR on failure) so the caller holds the dmabuf reference across steps 3 and 4.
The failure at step 3 is easily reachable from userspace: pass a struct dmaheapallocationdata that lives in a page whose protection is flipped to PROTREAD between copyfromuser() and copytouser() (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/dmaheap/<name> remains open.
No UAPI or heap-driver interface change.
[1] https://lore.kernel.org/dri-devel/175e98de-f414-47d7-81c1-c0fe0a8f7f62@amd.com/
Affected Software
Event History
Frequently Asked Questions
What conditions are required for this issue to occur?
A process must invoke DMA_HEAP_IOCTL_ALLOC and the final copy_to_user() that returns the allocated file descriptor number must fail. The descriptor has already been installed in the process fd table at that point.
Who can observe or use the leaked descriptor?
Other threads in the same process can observe the installed descriptor before the ioctl reports failure. They may duplicate it, pass it using SCM_RIGHTS, or close it and allow the descriptor number to be reused.
Why is closing the descriptor on the ioctl failure path not a safe workaround?
After fd_install(), another thread may have closed or reused that fd number. A later close_fd() by the ioctl path could therefore close an unrelated file instead of the original dma-buf descriptor.
What does the resolved implementation change?
It reserves the fd number before copying it to userspace, but does not publish the fd until copy_to_user() succeeds. fd_install() becomes the final unfailable step, preventing other threads from observing an fd when the ioctl ultimately fails.