When sharing a dma-buf between components of different trust levels, the allocator may need to hand a consumer a read-only view of a buffer it holds with read-write access. An example is a camera pipeline where the capture component writes frames into a buffer and needs to pass a read-only handle to a downstream processing component that should not be able to modify the data.
However, no such mechanism exists today. The access mode of a dma-buf file descriptor is fixed at export time, and the standard POSIX interfaces for duplicating or changing file descriptors (i.e., dup(2), dup3(2), and fcntl(F_SETFL)) cannot alter the read/write access mode of the copy.
One natural candidate would be reopening via /proc/self/fd/<N> with O_RDONLY, which works for regular files. For dma-buf this would fail (that is, if we were to add a new handler for open f_op) with ENXIO because the dmabuf pseudo-filesystem carries SB_NOUSER, which prevents the VFS from opening its files through path-based resolution from userspace.
Alternatively, exporting the buffer twice would produce two independent dma_buf instances, which breaks fence synchronization.
Therefore we add a new DMA_BUF_IOCTL_DERIVE ioctl, which produces a new file descriptor for an existing dma-buf with a caller-specified subset of the original permissions:
``` struct dma_buf_derive { __u32 flags; __s32 fd; };
struct dma_buf_derive req = { .flags = O_RDONLY | O_CLOEXEC }; ioctl(rw_fd, DMA_BUF_IOCTL_DERIVE, &req); /* req.fd is now a read-only alias of the same buffer */ ```
Permission escalation is rejected with -EACCES. The new fd aliases the same struct dma_buf as the original, same dma_resv, same exporter ops, same underlying memory; so importers attaching to either fd see the same fence timeline and operate on the same object. Access control for which components may receive or pass on restricted descriptors can be layered on top via SELinux file:read and file:write permissions.
A shared writable mapping (PROT_WRITE | MAP_SHARED) on the read-only fd is rejected with -EACCES in dma_buf_mmap_internal().
Two small internal adjustments accompany the ioctl: - __dma_buf_list_del() is moved to dma_buf_release() so it fires exactly once on dentry destruction rather than on every file close. - dma_buf_file_release() is updated to call dma_buf_put() only for files that are not the primary dma-buf file.
This may not be the best approach, but after considering different options and alternatives (as described above), we decided to raise the discussion upstream. Thus, we welcome any alternative proposal or ideas.
The series is structured as: - Patch 1 adds the new ioctl implementation. - Patch 2 adds selftests covering the new ioctl.
Signed-off-by: Albert Esteve aesteve@redhat.com --- Albert Esteve (2): dma-buf: add DMA_BUF_IOCTL_DERIVE for reduced-permission aliases selftests: dma-buf: add DERIVE ioctl tests
drivers/dma-buf/dma-buf.c | 58 ++++++++++- include/uapi/linux/dma-buf.h | 28 +++++ tools/testing/selftests/dmabuf-heaps/dmabuf-heap.c | 114 ++++++++++++++++++++- 3 files changed, 198 insertions(+), 2 deletions(-) --- base-commit: ab5fce87a778cb780a05984a2ca448f2b41aafbf change-id: 20260520-dmabuf-limit-access-73261353841a
Best regards,