Skip to content

Commit f1eb1ba

Browse files
xzpeterakpm00
authored andcommitted
mm/uffd: always wr-protect pte in pte|pmd_mkuffd_wp()
This patch is a cleanup to always wr-protect pte/pmd in mkuffd_wp paths. The reasons I still think this patch is worthwhile, are: (1) It is a cleanup already; diffstat tells. (2) It just feels natural after I thought about this, if the pte is uffd protected, let's remove the write bit no matter what it was. (2) Since x86 is the only arch that supports uffd-wp, it also redefines pte|pmd_mkuffd_wp() in that it should always contain removals of write bits. It means any future arch that want to implement uffd-wp should naturally follow this rule too. It's good to make it a default, even if with vm_page_prot changes on VM_UFFD_WP. (3) It covers more than vm_page_prot. So no chance of any potential future "accident" (like pte_mkdirty() sparc64 or loongarch, even though it just got its pte_mkdirty fixed <1 month ago). It'll be fairly clear when reading the code too that we don't worry anything before a pte_mkuffd_wp() on uncertainty of the write bit. We may call pte_wrprotect() one more time in some paths (e.g. thp split), but that should be fully local bitop instruction so the overhead should be negligible. Although this patch should logically also fix all the known issues on uffd-wp too recently on page migration (not for numa hint recovery - that may need another explcit pte_wrprotect), but this is not the plan for that fix. So no fixes, and stable doesn't need this. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Peter Xu <[email protected]> Acked-by: David Hildenbrand <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: Ives van Hoorne <[email protected]> Cc: Mike Kravetz <[email protected]> Cc: Nadav Amit <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 04a42e7 commit f1eb1ba

File tree

7 files changed

+32
-52
lines changed

7 files changed

+32
-52
lines changed

arch/x86/include/asm/pgtable.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,11 @@ static inline pte_t pte_clear_flags(pte_t pte, pteval_t clear)
289289
return native_make_pte(v & ~clear);
290290
}
291291

292+
static inline pte_t pte_wrprotect(pte_t pte)
293+
{
294+
return pte_clear_flags(pte, _PAGE_RW);
295+
}
296+
292297
#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP
293298
static inline int pte_uffd_wp(pte_t pte)
294299
{
@@ -313,7 +318,7 @@ static inline int pte_uffd_wp(pte_t pte)
313318

314319
static inline pte_t pte_mkuffd_wp(pte_t pte)
315320
{
316-
return pte_set_flags(pte, _PAGE_UFFD_WP);
321+
return pte_wrprotect(pte_set_flags(pte, _PAGE_UFFD_WP));
317322
}
318323

319324
static inline pte_t pte_clear_uffd_wp(pte_t pte)
@@ -332,11 +337,6 @@ static inline pte_t pte_mkold(pte_t pte)
332337
return pte_clear_flags(pte, _PAGE_ACCESSED);
333338
}
334339

335-
static inline pte_t pte_wrprotect(pte_t pte)
336-
{
337-
return pte_clear_flags(pte, _PAGE_RW);
338-
}
339-
340340
static inline pte_t pte_mkexec(pte_t pte)
341341
{
342342
return pte_clear_flags(pte, _PAGE_NX);
@@ -401,6 +401,11 @@ static inline pmd_t pmd_clear_flags(pmd_t pmd, pmdval_t clear)
401401
return native_make_pmd(v & ~clear);
402402
}
403403

404+
static inline pmd_t pmd_wrprotect(pmd_t pmd)
405+
{
406+
return pmd_clear_flags(pmd, _PAGE_RW);
407+
}
408+
404409
#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_WP
405410
static inline int pmd_uffd_wp(pmd_t pmd)
406411
{
@@ -409,7 +414,7 @@ static inline int pmd_uffd_wp(pmd_t pmd)
409414

410415
static inline pmd_t pmd_mkuffd_wp(pmd_t pmd)
411416
{
412-
return pmd_set_flags(pmd, _PAGE_UFFD_WP);
417+
return pmd_wrprotect(pmd_set_flags(pmd, _PAGE_UFFD_WP));
413418
}
414419

415420
static inline pmd_t pmd_clear_uffd_wp(pmd_t pmd)
@@ -428,11 +433,6 @@ static inline pmd_t pmd_mkclean(pmd_t pmd)
428433
return pmd_clear_flags(pmd, _PAGE_DIRTY);
429434
}
430435

431-
static inline pmd_t pmd_wrprotect(pmd_t pmd)
432-
{
433-
return pmd_clear_flags(pmd, _PAGE_RW);
434-
}
435-
436436
static inline pmd_t pmd_mkdirty(pmd_t pmd)
437437
{
438438
return pmd_set_flags(pmd, _PAGE_DIRTY | _PAGE_SOFT_DIRTY);

include/asm-generic/hugetlb.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ static inline pte_t huge_pte_mkwrite(pte_t pte)
2525
return pte_mkwrite(pte);
2626
}
2727

28+
#ifndef __HAVE_ARCH_HUGE_PTE_WRPROTECT
29+
static inline pte_t huge_pte_wrprotect(pte_t pte)
30+
{
31+
return pte_wrprotect(pte);
32+
}
33+
#endif
34+
2835
static inline pte_t huge_pte_mkdirty(pte_t pte)
2936
{
3037
return pte_mkdirty(pte);
@@ -37,7 +44,7 @@ static inline pte_t huge_pte_modify(pte_t pte, pgprot_t newprot)
3744

3845
static inline pte_t huge_pte_mkuffd_wp(pte_t pte)
3946
{
40-
return pte_mkuffd_wp(pte);
47+
return huge_pte_wrprotect(pte_mkuffd_wp(pte));
4148
}
4249

4350
static inline pte_t huge_pte_clear_uffd_wp(pte_t pte)
@@ -104,13 +111,6 @@ static inline int huge_pte_none_mostly(pte_t pte)
104111
return huge_pte_none(pte) || is_pte_marker(pte);
105112
}
106113

107-
#ifndef __HAVE_ARCH_HUGE_PTE_WRPROTECT
108-
static inline pte_t huge_pte_wrprotect(pte_t pte)
109-
{
110-
return pte_wrprotect(pte);
111-
}
112-
#endif
113-
114114
#ifndef __HAVE_ARCH_PREPARE_HUGEPAGE_RANGE
115115
static inline int prepare_hugepage_range(struct file *file,
116116
unsigned long addr, unsigned long len)

mm/huge_memory.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,17 +1920,15 @@ int change_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
19201920
oldpmd = pmdp_invalidate_ad(vma, addr, pmd);
19211921

19221922
entry = pmd_modify(oldpmd, newprot);
1923-
if (uffd_wp) {
1924-
entry = pmd_wrprotect(entry);
1923+
if (uffd_wp)
19251924
entry = pmd_mkuffd_wp(entry);
1926-
} else if (uffd_wp_resolve) {
1925+
else if (uffd_wp_resolve)
19271926
/*
19281927
* Leave the write bit to be handled by PF interrupt
19291928
* handler, then things like COW could be properly
19301929
* handled.
19311930
*/
19321931
entry = pmd_clear_uffd_wp(entry);
1933-
}
19341932

19351933
/* See change_pte_range(). */
19361934
if ((cp_flags & MM_CP_TRY_CHANGE_WRITABLE) && !pmd_write(entry) &&
@@ -3275,7 +3273,7 @@ void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
32753273
if (is_writable_migration_entry(entry))
32763274
pmde = maybe_pmd_mkwrite(pmde, vma);
32773275
if (pmd_swp_uffd_wp(*pvmw->pmd))
3278-
pmde = pmd_wrprotect(pmd_mkuffd_wp(pmde));
3276+
pmde = pmd_mkuffd_wp(pmde);
32793277
if (!is_migration_entry_young(entry))
32803278
pmde = pmd_mkold(pmde);
32813279
/* NOTE: this may contain setting soft-dirty on some archs */

mm/hugetlb.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5919,7 +5919,7 @@ static vm_fault_t hugetlb_no_page(struct mm_struct *mm,
59195919
* if populated.
59205920
*/
59215921
if (unlikely(pte_marker_uffd_wp(old_pte)))
5922-
new_pte = huge_pte_wrprotect(huge_pte_mkuffd_wp(new_pte));
5922+
new_pte = huge_pte_mkuffd_wp(new_pte);
59235923
set_huge_pte_at(mm, haddr, ptep, new_pte);
59245924

59255925
hugetlb_count_add(pages_per_huge_page(h), mm);
@@ -6728,7 +6728,7 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
67286728
pte = huge_pte_modify(old_pte, newprot);
67296729
pte = arch_make_huge_pte(pte, shift, vma->vm_flags);
67306730
if (uffd_wp)
6731-
pte = huge_pte_mkuffd_wp(huge_pte_wrprotect(pte));
6731+
pte = huge_pte_mkuffd_wp(pte);
67326732
else if (uffd_wp_resolve)
67336733
pte = huge_pte_clear_uffd_wp(pte);
67346734
huge_ptep_modify_prot_commit(vma, address, ptep, old_pte, pte);

mm/memory.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ copy_present_page(struct vm_area_struct *dst_vma, struct vm_area_struct *src_vma
878878
pte = maybe_mkwrite(pte_mkdirty(pte), dst_vma);
879879
if (userfaultfd_pte_wp(dst_vma, *src_pte))
880880
/* Uffd-wp needs to be delivered to dest pte as well */
881-
pte = pte_wrprotect(pte_mkuffd_wp(pte));
881+
pte = pte_mkuffd_wp(pte);
882882
set_pte_at(dst_vma->vm_mm, addr, dst_pte, pte);
883883
return 0;
884884
}
@@ -3950,10 +3950,8 @@ vm_fault_t do_swap_page(struct vm_fault *vmf)
39503950
flush_icache_page(vma, page);
39513951
if (pte_swp_soft_dirty(vmf->orig_pte))
39523952
pte = pte_mksoft_dirty(pte);
3953-
if (pte_swp_uffd_wp(vmf->orig_pte)) {
3953+
if (pte_swp_uffd_wp(vmf->orig_pte))
39543954
pte = pte_mkuffd_wp(pte);
3955-
pte = pte_wrprotect(pte);
3956-
}
39573955
vmf->orig_pte = pte;
39583956

39593957
/* ksm created a completely new copy */
@@ -4296,7 +4294,7 @@ void do_set_pte(struct vm_fault *vmf, struct page *page, unsigned long addr)
42964294
if (write)
42974295
entry = maybe_mkwrite(pte_mkdirty(entry), vma);
42984296
if (unlikely(uffd_wp))
4299-
entry = pte_mkuffd_wp(pte_wrprotect(entry));
4297+
entry = pte_mkuffd_wp(entry);
43004298
/* copy-on-write page */
43014299
if (write && !(vma->vm_flags & VM_SHARED)) {
43024300
inc_mm_counter(vma->vm_mm, MM_ANONPAGES);

mm/mprotect.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,10 @@ static unsigned long change_pte_range(struct mmu_gather *tlb,
177177
oldpte = ptep_modify_prot_start(vma, addr, pte);
178178
ptent = pte_modify(oldpte, newprot);
179179

180-
if (uffd_wp) {
181-
ptent = pte_wrprotect(ptent);
180+
if (uffd_wp)
182181
ptent = pte_mkuffd_wp(ptent);
183-
} else if (uffd_wp_resolve) {
182+
else if (uffd_wp_resolve)
184183
ptent = pte_clear_uffd_wp(ptent);
185-
}
186184

187185
/*
188186
* In some writable, shared mappings, we might want

mm/userfaultfd.c

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -74,24 +74,10 @@ int mfill_atomic_install_pte(struct mm_struct *dst_mm, pmd_t *dst_pmd,
7474
_dst_pte = pte_mkdirty(_dst_pte);
7575
if (page_in_cache && !vm_shared)
7676
writable = false;
77-
78-
/*
79-
* Always mark a PTE as write-protected when needed, regardless of
80-
* VM_WRITE, which the user might change.
81-
*/
82-
if (wp_copy) {
83-
_dst_pte = pte_mkuffd_wp(_dst_pte);
84-
writable = false;
85-
}
86-
8777
if (writable)
8878
_dst_pte = pte_mkwrite(_dst_pte);
89-
else
90-
/*
91-
* We need this to make sure write bit removed; as mk_pte()
92-
* could return a pte with write bit set.
93-
*/
94-
_dst_pte = pte_wrprotect(_dst_pte);
79+
if (wp_copy)
80+
_dst_pte = pte_mkuffd_wp(_dst_pte);
9581

9682
dst_pte = pte_offset_map_lock(dst_mm, dst_pmd, dst_addr, &ptl);
9783

0 commit comments

Comments
 (0)