GUP rejects VM_IO and VM_PFNMAP mappings early. __access_remote_vm() then falls back to vm_ops->access without checking VMA permissions and without limiting the request to the VMA size. Consequently, /proc//mem can bypass protections even without FOLL_FORCE, or cross into another mapping without checking its permissions. Check VMA permissions before calling ->access and limit each request to the current VMA. Implement similar access permission checks as GUP, however: (1) Reject writes to CoW mappings because the callbacks access backing memory directly and cannot handle CoW. (2) Allow forced writes to non-CoW mappings with VM_MAYWRITE. This intentionally breaks forced writes through legacy amdgpu PROT_NONE, MAP_PRIVATE debugger mappings. The workaround in commit fa5239f2af98 ("drm/amdgpu: workaround failed COW checks for Thunk VMAs") cleared VM_MAYWRITE to avoid CoW, but debugger writes still succeeded by exploiting the missing access-permission checks fixed here. Reads remain supported. Userspace must use MAP_SHARED instead; ROCm 5.5.0, released in May 2023, already does so. Link: https://github.com/ROCm/ROCT-Thunk-Interface/commit/fea44fab6e84eda52fcf6b419b35600c18f4c022 Fixes: 272ddc8b3735 ("proc: don't use FOLL_FORCE for reading cmdline and environment") Cc: stable@vger.kernel.org Cc: Felix Kuehling Cc: Christian König Cc: Simona Vetter Reported-by: Chris Mason Signed-off-by: David Hildenbrand (Arm) --- mm/memory.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/mm/memory.c b/mm/memory.c index d4370c1b331c9..9872f289c1e49 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -7362,6 +7362,7 @@ EXPORT_SYMBOL_GPL(generic_access_phys); static int __access_remote_vm(struct mm_struct *mm, unsigned long addr, void *buf, int len, unsigned int gup_flags) { + const bool force = gup_flags & FOLL_FORCE; void *old_buf = buf; int write = gup_flags & FOLL_WRITE; @@ -7401,15 +7402,35 @@ static int __access_remote_vm(struct mm_struct *mm, unsigned long addr, if (gup_flags & FOLL_ANON) break; + if (write) { + if (!vma_test(vma, VMA_MAYWRITE_BIT)) + break; + if (!vma_test(vma, VMA_WRITE_BIT) && !force) + break; + /* + * In contrast to GUP, we historically allowed + * FOLL_FORCE write access to non-CoW mappings. + * However, we must always reject CoW mappings + * as we cannot CoW here. + */ + if (vma_is_cow_mapping(vma)) + break; + } else if (!vma_test(vma, VMA_READ_BIT)) { + if (!force || !vma_test(vma, VMA_MAYREAD_BIT)) + break; + } + /* * Check if this is a VM_IO | VM_PFNMAP VMA, which * we can access using slightly different code. */ bytes = 0; #ifdef CONFIG_HAVE_IOREMAP_PROT - if (vma->vm_ops && vma->vm_ops->access) + if (vma->vm_ops && vma->vm_ops->access) { + bytes = min_t(unsigned long, len, vma->vm_end - addr); bytes = vma->vm_ops->access(vma, addr, buf, - len, write); + bytes, write); + } #endif if (bytes <= 0) break; -- 2.43.0