On 5/21/26 11:10, Albert Esteve wrote:
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.
OH MY GOD! This is the like the sixth time I had to clarify that in the last few weeks, I'm really wondering where that is suddenly coming from.
Creating the DMA-buf with O_RDONLY does *NOT* make the DMA-buf itself read only!
That's a really common misconception. The flag only controls if mmap() can be done read/write or read-only to handle cache coherency issues.
It is still perfectly possible for a device to write into a DMA-buf created with O_RDONLY with DMA!
So long story short there is not such feature as a read only DMA-buf, and putting read-only pages into a DMA-buf and then expecting that nobody can write to them is an absolutely clear No-Go.
If we would want to implement a read-only DMA-buf feature we would need to go over all the different DMA-buf importers in the kernel and add security checks.
Regards, Christian.
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,