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 out a read-only view of a buffer it
> holds with read-write access. Currently there is no mechanism to do
> this: the file flags set at allocation time are fixed for the
> lifetime of the dma-buf, and dup(2) and dup3(2) cannot change the
> access mode of the new fd.
> 
> Add DMA_BUF_IOCTL_DERIVE, which takes a struct dma_buf_derive carrying
> the desired access flags and returns a new file descriptor for the same
> buffer with those flags applied. Permission escalation is rejected with
> EACCES.
> 
> The new fd aliases the same struct dma_buf, same dma_resv, same exporter
> ops, same underlying memory. Importers that attach to either fd operate
> on the same object and observe the same fence timeline.
> 
> To support multiple struct file instances sharing one struct dma_buf,
> two small internal adjustments are required. First, move
> __dma_buf_list_del() to dma_buf_release() so that list removal fires
> exactly once when the dentry is destroyed. Second, update
> dma_buf_file_release() to call dma_buf_put() only for the files that
> are not primary dmabuf files, leaving the primary fd's refcount managed
> by the normal dentry lifecycle.


> Finally, enforce the access restriction in dma_buf_mmap_internal():
> a shared writable mapping (MAP_SHARED + PROT_WRITE) on a read-only fd
> is rejected with -EACCES. Without this check, O_RDONLY on a dma-buf
> fd would be cosmetic, as the VFS does not enforce f_mode for writable
> mmap on anonymous inodes.

Clear NAK to that since that would break the existing uAPI.

Regards,
Christian.

> 
> Signed-off-by: Albert Esteve <[email protected]>
> ---
>  drivers/dma-buf/dma-buf.c    | 58 
> +++++++++++++++++++++++++++++++++++++++++++-
>  include/uapi/linux/dma-buf.h | 28 +++++++++++++++++++++
>  2 files changed, 85 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
> index 71f37544a5c61..34a3872365730 100644
> --- a/drivers/dma-buf/dma-buf.c
> +++ b/drivers/dma-buf/dma-buf.c
> @@ -180,6 +180,7 @@ static void dma_buf_release(struct dentry *dentry)
>        */
>       BUG_ON(dmabuf->cb_in.active || dmabuf->cb_out.active);
>  
> +     __dma_buf_list_del(dmabuf);
>       dmabuf->ops->release(dmabuf);
>  
>       if (dmabuf->resv == (struct dma_resv *)&dmabuf[1])
> @@ -193,10 +194,13 @@ static void dma_buf_release(struct dentry *dentry)
>  
>  static int dma_buf_file_release(struct inode *inode, struct file *file)
>  {
> +     struct dma_buf *dmabuf = file->private_data;
> +
>       if (!is_dma_buf_file(file))
>               return -EINVAL;
>  
> -     __dma_buf_list_del(file->private_data);
> +     if (file != dmabuf->file)
> +             dma_buf_put(dmabuf);
>  
>       return 0;
>  }
> @@ -232,6 +236,11 @@ static int dma_buf_mmap_internal(struct file *file, 
> struct vm_area_struct *vma)
>       if (!is_dma_buf_file(file))
>               return -EINVAL;
>  
> +     if ((vma->vm_flags & VM_WRITE) &&
> +         (vma->vm_flags & VM_SHARED) &&
> +         !(file->f_mode & FMODE_WRITE))
> +             return -EACCES;
> +
>       dmabuf = file->private_data;
>  
>       /* check if buffer supports mmap */
> @@ -537,6 +546,50 @@ static long dma_buf_import_sync_file(struct dma_buf 
> *dmabuf,
>  }
>  #endif
>  
> +static const struct file_operations dma_buf_fops;
> +
> +static int dma_buf_ioctl_derive(struct dma_buf *dmabuf, struct file *file,
> +                             void __user *udata)
> +{
> +     struct dma_buf_derive params;
> +     struct file *new_file;
> +     int new_fd;
> +
> +     if (copy_from_user(&params, udata, sizeof(params)))
> +             return -EFAULT;
> +
> +     if (params.flags & ~(O_ACCMODE | O_CLOEXEC))
> +             return -EINVAL;
> +
> +     /* Escalating permissions is not allowed. */
> +     if ((params.flags & O_ACCMODE) == O_RDWR &&
> +         !(file->f_mode & FMODE_WRITE))
> +             return -EACCES;
> +
> +     new_file = alloc_file_clone(dmabuf->file, params.flags, &dma_buf_fops);
> +     if (IS_ERR(new_file))
> +             return PTR_ERR(new_file);
> +
> +     get_dma_buf(dmabuf);
> +     new_file->private_data = dmabuf;
> +
> +     new_fd = get_unused_fd_flags(params.flags & O_CLOEXEC ? O_CLOEXEC : 0);
> +     if (new_fd < 0) {
> +             fput(new_file);
> +             return new_fd;
> +     }
> +
> +     params.fd = new_fd;
> +     if (copy_to_user(udata, &params, sizeof(params))) {
> +             put_unused_fd(new_fd);
> +             fput(new_file);
> +             return -EFAULT;
> +     }
> +
> +     fd_install(new_fd, new_file);
> +     return 0;
> +}
> +
>  static long dma_buf_ioctl(struct file *file,
>                         unsigned int cmd, unsigned long arg)
>  {
> @@ -587,6 +640,9 @@ static long dma_buf_ioctl(struct file *file,
>               return dma_buf_import_sync_file(dmabuf, (const void __user 
> *)arg);
>  #endif
>  
> +     case DMA_BUF_IOCTL_DERIVE:
> +             return dma_buf_ioctl_derive(dmabuf, file, (void __user *)arg);
> +
>       default:
>               return -ENOTTY;
>       }
> diff --git a/include/uapi/linux/dma-buf.h b/include/uapi/linux/dma-buf.h
> index e827c9d20c5d3..d0cf616228e55 100644
> --- a/include/uapi/linux/dma-buf.h
> +++ b/include/uapi/linux/dma-buf.h
> @@ -168,6 +168,33 @@ struct dma_buf_import_sync_file {
>       __s32 fd;
>  };
>  
> +/**
> + * struct dma_buf_derive - Obtain a dma-buf fd with reduced access 
> permissions
> + *
> + * Userspace can perform a DMA_BUF_IOCTL_DERIVE to obtain a second file
> + * descriptor for the same dma-buf with a subset of the calling fd's
> + * permissions.  This allows a producer holding read-write access to hand a
> + * read-only view to a less-privileged consumer without giving up its own
> + * write access or allocating a separate buffer.
> + *
> + * Unlike first-export ioctls, the new fd is not a re-export. It shares the
> + * same reservation object, exporter ops, and underlying memory as the
> + * original.
> + *
> + * The requested permissions must not exceed those of the calling fd.
> + */
> +struct dma_buf_derive {
> +     /**
> +      * @flags: Requested access flags.
> +      *
> +      * Accepts O_RDONLY or O_RDWR, optionally combined with O_CLOEXEC.
> +      * All other bits must be zero.
> +      */
> +     __u32 flags;
> +     /** @fd: Returned file descriptor with the requested permissions */
> +     __s32 fd;
> +};
> +
>  #define DMA_BUF_BASE         'b'
>  #define DMA_BUF_IOCTL_SYNC   _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
>  
> @@ -179,5 +206,6 @@ struct dma_buf_import_sync_file {
>  #define DMA_BUF_SET_NAME_B   _IOW(DMA_BUF_BASE, 1, __u64)
>  #define DMA_BUF_IOCTL_EXPORT_SYNC_FILE       _IOWR(DMA_BUF_BASE, 2, struct 
> dma_buf_export_sync_file)
>  #define DMA_BUF_IOCTL_IMPORT_SYNC_FILE       _IOW(DMA_BUF_BASE, 3, struct 
> dma_buf_import_sync_file)
> +#define DMA_BUF_IOCTL_DERIVE         _IOWR(DMA_BUF_BASE, 4, struct 
> dma_buf_derive)
>  
>  #endif
> 


Reply via email to