[PATCH RFC v4 04/12] mm/gup: let check_vma_flags() ignore selected VMA flags
From: Rik van Riel
Date: Fri Jul 24 2026 - 18:34:27 EST
check_vma_flags() rejects a VMA whose flags gup cannot handle. A caller
that wants to reach a COWed page in a VM_IO or VM_PFNMAP VMA, where that
page does have a struct page, cannot express the exception and has to
repeat the flag test itself.
Add an ignore_flags argument that check_vma_flags() clears from its local
copy of vma->vm_flags before the flag checks. A caller can drop a single
rejection this way while keeping every other check. All current callers
pass 0.
This prepares for get_user_page_vma(), which passes VM_IO | VM_PFNMAP to
reach those COWed pages.
No functional change intended.
Assisted-by: Claude:claude-opus-4.8
Signed-off-by: Rik van Riel <riel@xxxxxxxxxxx>
---
mm/gup.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/mm/gup.c b/mm/gup.c
index 0692119b7904..dcece7b5253c 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -1197,13 +1197,21 @@ static bool writable_file_mapping_allowed(struct vm_area_struct *vma,
return !vma_needs_dirty_tracking(vma);
}
-static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags)
+static int check_vma_flags(struct vm_area_struct *vma, unsigned long gup_flags,
+ vm_flags_t ignore_flags)
{
vm_flags_t vm_flags = vma->vm_flags;
int write = (gup_flags & FOLL_WRITE);
int foreign = (gup_flags & FOLL_REMOTE);
bool vma_anon = vma_is_anonymous(vma);
+ /*
+ * Opt out of the flag checks that read this local copy (the
+ * VM_IO/VM_PFNMAP gate and the write/read/cow bits); checks that
+ * re-read vma->vm_flags through helpers are unaffected.
+ */
+ vm_flags &= ~ignore_flags;
+
if (vm_flags & (VM_IO | VM_PFNMAP))
return -EFAULT;
@@ -1387,7 +1395,7 @@ static long __get_user_pages(struct mm_struct *mm,
ret = -ENOMEM;
goto out;
}
- if (check_vma_flags(vma, gup_flags)) {
+ if (check_vma_flags(vma, gup_flags, 0)) {
ret = -EINVAL;
goto out;
}
@@ -1408,7 +1416,7 @@ static long __get_user_pages(struct mm_struct *mm,
ret = -EFAULT;
goto out;
}
- ret = check_vma_flags(vma, gup_flags);
+ ret = check_vma_flags(vma, gup_flags, 0);
if (ret)
goto out;
}
--
2.53.0-Meta