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 <[email protected]>
---
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,
-- 
Albert Esteve <[email protected]>


Reply via email to