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 --- 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