diff options
-rw-r--r-- | mm/userfaultfd.c | 41 |
1 files changed, 24 insertions, 17 deletions
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c index 53c3d916ff66..23cabd02ac52 100644 --- a/mm/userfaultfd.c +++ b/mm/userfaultfd.c @@ -717,6 +717,8 @@ long uffd_wp_range(struct mm_struct *dst_mm, struct vm_area_struct *dst_vma, struct mmu_gather tlb; long ret; + VM_WARN_ONCE(start < dst_vma->vm_start || start + len > dst_vma->vm_end, + "The address range exceeds VMA boundary.\n"); if (enable_wp) mm_cp_flags = MM_CP_UFFD_WP; else @@ -741,9 +743,12 @@ int mwriteprotect_range(struct mm_struct *dst_mm, unsigned long start, unsigned long len, bool enable_wp, atomic_t *mmap_changing) { + unsigned long end = start + len; + unsigned long _start, _end; struct vm_area_struct *dst_vma; unsigned long page_mask; long err; + VMA_ITERATOR(vmi, dst_mm, start); /* * Sanitize the command parameters: @@ -766,28 +771,30 @@ int mwriteprotect_range(struct mm_struct *dst_mm, unsigned long start, goto out_unlock; err = -ENOENT; - dst_vma = find_dst_vma(dst_mm, start, len); + for_each_vma_range(vmi, dst_vma, end) { - if (!dst_vma) - goto out_unlock; - if (!userfaultfd_wp(dst_vma)) - goto out_unlock; - if (!vma_can_userfault(dst_vma, dst_vma->vm_flags)) - goto out_unlock; + if (!userfaultfd_wp(dst_vma)) { + err = -ENOENT; + break; + } - if (is_vm_hugetlb_page(dst_vma)) { - err = -EINVAL; - page_mask = vma_kernel_pagesize(dst_vma) - 1; - if ((start & page_mask) || (len & page_mask)) - goto out_unlock; - } + if (is_vm_hugetlb_page(dst_vma)) { + err = -EINVAL; + page_mask = vma_kernel_pagesize(dst_vma) - 1; + if ((start & page_mask) || (len & page_mask)) + break; + } - err = uffd_wp_range(dst_mm, dst_vma, start, len, enable_wp); + _start = max(dst_vma->vm_start, start); + _end = min(dst_vma->vm_end, end); - /* Return 0 on success, <0 on failures */ - if (err > 0) - err = 0; + err = uffd_wp_range(dst_mm, dst_vma, _start, _end - _start, enable_wp); + /* Return 0 on success, <0 on failures */ + if (err < 0) + break; + err = 0; + } out_unlock: mmap_read_unlock(dst_mm); return err; |