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 <[email protected]> Suggested-by: Christian König <[email protected]> Signed-off-by: Chris Wiseman <[email protected]> --- 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
