[PATCH] dma-buf: Reject private mappings in dma_buf_mmap_internal() and dma_buf_mmap()
From: Chris Wiseman
Date: Mon Nov 02 2026 - 13:44:50 EST
DMA-BUF mappings do not support copy-on-write. Mapping a dma-buf with
MAP_PRIVATE and PROT_WRITE faults into do_wp_page() -> wp_page_copy()
and allocates private anonymous pages whose writes never reach the
underlying buffer.
Individual exporters like cma_heap_mmap(), system_heap_mmap(), and
mmap_udmabuf() already require (VM_SHARED | VM_MAYSHARE), and DRM
helpers like ttm_bo_mmap_obj() and drm_gem_shmem_object_mmap() reject
vma_is_cow_mapping(vma). However, dma_buf_mmap_internal() and
dma_buf_mmap() do not check for this and pass MAP_PRIVATE mappings
straight through to dmabuf->ops->mmap().
Reject mappings with -EINVAL in both dma_buf_mmap_internal() and
dma_buf_mmap() when neither VM_SHARED nor VM_MAYSHARE is set. Checking
both flags still allows read-only MAP_SHARED mappings, where do_mmap()
clears VM_SHARED and leaves VM_MAYSHARE set.
Suggested-by: T.J. Mercier <tjmercier@xxxxxxxxxx>
Suggested-by: Christian König <christian.koenig@xxxxxxx>
Signed-off-by: Chris Wiseman <chriswiseman@xxxxxxxxxx>
---
Note: For the prior discussion Christian mentioned, commit f91142c62161
("drm/ttm: nuke VM_MIXEDMAP on BO mappings v3") added the
vma_is_cow_mapping() check in TTM, and commit fa5239f2af98
("drm/amdgpu: workaround failed COW checks for Thunk VMAs") worked
around a legacy ROCm KFD Thunk PROT_NONE, MAP_PRIVATE mapping on native
amdgpu GEM nodes.
Tested in ARM64 QEMU (Linux 7.3.0+) across /dev/dma_heap/system:
BASE:
[CASE 1: MAP_SHARED O_RDWR] ret=0 buf[0]=0x00000000cafebabe
[CASE 2: MAP_SHARED O_RDONLY] ret=0
[CASE 3: MAP_PRIVATE PROT_RDWR] ret=0 underlying_buf[0]=0x00000000cafebabe
[CASE 4: MAP_PRIVATE PROT_READ] ret=0
[CASE 5: MAP_PRIVATE PROT_NONE] ret=0
PATCHED:
[CASE 1: MAP_SHARED O_RDWR] ret=0 buf[0]=0x00000000cafebabe
[CASE 2: MAP_SHARED O_RDONLY] ret=0
[CASE 3: MAP_PRIVATE PROT_RDWR] ret=-22 (-EINVAL)
[CASE 4: MAP_PRIVATE PROT_READ] ret=-22 (-EINVAL)
[CASE 5: MAP_PRIVATE PROT_NONE] ret=-22 (-EINVAL)
drivers/dma-buf/dma-buf.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index 1ccaffabbdbe..51e9a4dc56be 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -235,6 +235,10 @@ static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
if (!dmabuf->ops->mmap)
return -EINVAL;
+ /* check for private mappings */
+ if (!(vma->vm_flags & (VM_SHARED | VM_MAYSHARE)))
+ return -EINVAL;
+
/* check for overflowing the buffer's size */
if (vma->vm_pgoff + vma_pages(vma) >
dmabuf->size >> PAGE_SHIFT)
@@ -1610,6 +1614,10 @@ int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
if (!dmabuf->ops->mmap)
return -EINVAL;
+ /* check for private mappings */
+ if (!(vma->vm_flags & (VM_SHARED | VM_MAYSHARE)))
+ return -EINVAL;
+
/* check for offset overflow */
if (pgoff + vma_pages(vma) < pgoff)
return -EOVERFLOW;
--
2.51.0