Skip to content

Commit 60d5b47

Browse files
xzpeterakpm00
authored andcommitted
mm/hugetlb: fix uffd wr-protection for CoW optimization path
This patch fixes an issue that a hugetlb uffd-wr-protected mapping can be writable even with uffd-wp bit set. It only happens with hugetlb private mappings, when someone firstly wr-protects a missing pte (which will install a pte marker), then a write to the same page without any prior access to the page. Userfaultfd-wp trap for hugetlb was implemented in hugetlb_fault() before reaching hugetlb_wp() to avoid taking more locks that userfault won't need. However there's one CoW optimization path that can trigger hugetlb_wp() inside hugetlb_no_page(), which will bypass the trap. This patch skips hugetlb_wp() for CoW and retries the fault if uffd-wp bit is detected. The new path will only trigger in the CoW optimization path because generic hugetlb_fault() (e.g. when a present pte was wr-protected) will resolve the uffd-wp bit already. Also make sure anonymous UNSHARE won't be affected and can still be resolved, IOW only skip CoW not CoR. This patch will be needed for v5.19+ hence copy stable. [[email protected]: v2] Link: https://lkml.kernel.org/r/ZBzOqwF2wrHgBVZb@x1n [[email protected]: v3] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Fixes: 166f3ec ("mm/hugetlb: hook page faults for uffd write protection") Signed-off-by: Peter Xu <[email protected]> Reported-by: Muhammad Usama Anjum <[email protected]> Tested-by: Muhammad Usama Anjum <[email protected]> Acked-by: David Hildenbrand <[email protected]> Reviewed-by: Mike Kravetz <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Axel Rasmussen <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: Nadav Amit <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 3dd4432 commit 60d5b47

File tree

1 file changed

+12
-2
lines changed

1 file changed

+12
-2
lines changed

mm/hugetlb.c

+12-2
Original file line numberDiff line numberDiff line change
@@ -5478,7 +5478,7 @@ static vm_fault_t hugetlb_wp(struct mm_struct *mm, struct vm_area_struct *vma,
54785478
struct folio *pagecache_folio, spinlock_t *ptl)
54795479
{
54805480
const bool unshare = flags & FAULT_FLAG_UNSHARE;
5481-
pte_t pte;
5481+
pte_t pte = huge_ptep_get(ptep);
54825482
struct hstate *h = hstate_vma(vma);
54835483
struct page *old_page;
54845484
struct folio *new_folio;
@@ -5487,6 +5487,17 @@ static vm_fault_t hugetlb_wp(struct mm_struct *mm, struct vm_area_struct *vma,
54875487
unsigned long haddr = address & huge_page_mask(h);
54885488
struct mmu_notifier_range range;
54895489

5490+
/*
5491+
* Never handle CoW for uffd-wp protected pages. It should be only
5492+
* handled when the uffd-wp protection is removed.
5493+
*
5494+
* Note that only the CoW optimization path (in hugetlb_no_page())
5495+
* can trigger this, because hugetlb_fault() will always resolve
5496+
* uffd-wp bit first.
5497+
*/
5498+
if (!unshare && huge_pte_uffd_wp(pte))
5499+
return 0;
5500+
54905501
/*
54915502
* hugetlb does not support FOLL_FORCE-style write faults that keep the
54925503
* PTE mapped R/O such as maybe_mkwrite() would do.
@@ -5500,7 +5511,6 @@ static vm_fault_t hugetlb_wp(struct mm_struct *mm, struct vm_area_struct *vma,
55005511
return 0;
55015512
}
55025513

5503-
pte = huge_ptep_get(ptep);
55045514
old_page = pte_page(pte);
55055515

55065516
delayacct_wpcopy_start();

0 commit comments

Comments
 (0)