Merge tag 'mm-stable-2023-02-20-13-37' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / mm / userfaultfd.c
index 0499907b6f1a306d8fb3edeabf862ac037a4ce30..53c3d916ff661ef8559590ddfdefba7d1bf1187a 100644 (file)
@@ -74,24 +74,10 @@ int mfill_atomic_install_pte(struct mm_struct *dst_mm, pmd_t *dst_pmd,
        _dst_pte = pte_mkdirty(_dst_pte);
        if (page_in_cache && !vm_shared)
                writable = false;
-
-       /*
-        * Always mark a PTE as write-protected when needed, regardless of
-        * VM_WRITE, which the user might change.
-        */
-       if (wp_copy) {
-               _dst_pte = pte_mkuffd_wp(_dst_pte);
-               writable = false;
-       }
-
        if (writable)
                _dst_pte = pte_mkwrite(_dst_pte);
-       else
-               /*
-                * We need this to make sure write bit removed; as mk_pte()
-                * could return a pte with write bit set.
-                */
-               _dst_pte = pte_wrprotect(_dst_pte);
+       if (wp_copy)
+               _dst_pte = pte_mkuffd_wp(_dst_pte);
 
        dst_pte = pte_offset_map_lock(dst_mm, dst_pmd, dst_addr, &ptl);
 
@@ -724,21 +710,31 @@ ssize_t mcopy_continue(struct mm_struct *dst_mm, unsigned long start,
                              mmap_changing, 0);
 }
 
-void uffd_wp_range(struct mm_struct *dst_mm, struct vm_area_struct *dst_vma,
+long uffd_wp_range(struct mm_struct *dst_mm, struct vm_area_struct *dst_vma,
                   unsigned long start, unsigned long len, bool enable_wp)
 {
+       unsigned int mm_cp_flags;
        struct mmu_gather tlb;
-       pgprot_t newprot;
+       long ret;
 
        if (enable_wp)
-               newprot = vm_get_page_prot(dst_vma->vm_flags & ~(VM_WRITE));
+               mm_cp_flags = MM_CP_UFFD_WP;
        else
-               newprot = vm_get_page_prot(dst_vma->vm_flags);
+               mm_cp_flags = MM_CP_UFFD_WP_RESOLVE;
 
+       /*
+        * vma->vm_page_prot already reflects that uffd-wp is enabled for this
+        * VMA (see userfaultfd_set_vm_flags()) and that all PTEs are supposed
+        * to be write-protected as default whenever protection changes.
+        * Try upgrading write permissions manually.
+        */
+       if (!enable_wp && vma_wants_manual_pte_write_upgrade(dst_vma))
+               mm_cp_flags |= MM_CP_TRY_CHANGE_WRITABLE;
        tlb_gather_mmu(&tlb, dst_mm);
-       change_protection(&tlb, dst_vma, start, start + len, newprot,
-                         enable_wp ? MM_CP_UFFD_WP : MM_CP_UFFD_WP_RESOLVE);
+       ret = change_protection(&tlb, dst_vma, start, start + len, mm_cp_flags);
        tlb_finish_mmu(&tlb);
+
+       return ret;
 }
 
 int mwriteprotect_range(struct mm_struct *dst_mm, unsigned long start,
@@ -747,7 +743,7 @@ int mwriteprotect_range(struct mm_struct *dst_mm, unsigned long start,
 {
        struct vm_area_struct *dst_vma;
        unsigned long page_mask;
-       int err;
+       long err;
 
        /*
         * Sanitize the command parameters:
@@ -786,9 +782,12 @@ int mwriteprotect_range(struct mm_struct *dst_mm, unsigned long start,
                        goto out_unlock;
        }
 
-       uffd_wp_range(dst_mm, dst_vma, start, len, enable_wp);
+       err = uffd_wp_range(dst_mm, dst_vma, start, len, enable_wp);
+
+       /* Return 0 on success, <0 on failures */
+       if (err > 0)
+               err = 0;
 
-       err = 0;
 out_unlock:
        mmap_read_unlock(dst_mm);
        return err;