Skip to content

Commit c1991e0

Browse files
xzpetertorvalds
authored andcommitted
hugetlb/userfaultfd: forbid huge pmd sharing when uffd enabled
Huge pmd sharing could bring problem to userfaultfd. The thing is that userfaultfd is running its logic based on the special bits on page table entries, however the huge pmd sharing could potentially share page table entries for different address ranges. That could cause issues on either: - When sharing huge pmd page tables for an uffd write protected range, the newly mapped huge pmd range will also be write protected unexpectedly, or, - When we try to write protect a range of huge pmd shared range, we'll first do huge_pmd_unshare() in hugetlb_change_protection(), however that also means the UFFDIO_WRITEPROTECT could be silently skipped for the shared region, which could lead to data loss. While at it, a few other things are done altogether: - Move want_pmd_share() from mm/hugetlb.c into linux/hugetlb.h, because that's definitely something that arch code would like to use too - ARM64 currently directly check against CONFIG_ARCH_WANT_HUGE_PMD_SHARE when trying to share huge pmd. Switch to the want_pmd_share() helper. - Move vma_shareable() from huge_pmd_share() into want_pmd_share(). [[email protected]: fix build with !ARCH_WANT_HUGE_PMD_SHARE] Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Peter Xu <[email protected]> Reviewed-by: Mike Kravetz <[email protected]> Reviewed-by: Axel Rasmussen <[email protected]> Tested-by: Naresh Kamboju <[email protected]> Cc: Adam Ruprecht <[email protected]> Cc: Alexander Viro <[email protected]> Cc: Alexey Dobriyan <[email protected]> Cc: Andrea Arcangeli <[email protected]> Cc: Anshuman Khandual <[email protected]> Cc: Cannon Matthews <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Chinwen Chang <[email protected]> Cc: David Rientjes <[email protected]> Cc: "Dr . David Alan Gilbert" <[email protected]> Cc: Huang Ying <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jann Horn <[email protected]> Cc: Jerome Glisse <[email protected]> Cc: Kirill A. Shutemov <[email protected]> Cc: Lokesh Gidra <[email protected]> Cc: "Matthew Wilcox (Oracle)" <[email protected]> Cc: Michael Ellerman <[email protected]> Cc: "Michal Koutn" <[email protected]> Cc: Michel Lespinasse <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: Mina Almasry <[email protected]> Cc: Nicholas Piggin <[email protected]> Cc: Oliver Upton <[email protected]> Cc: Shaohua Li <[email protected]> Cc: Shawn Anastasio <[email protected]> Cc: Steven Price <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Vlastimil Babka <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent aec44e0 commit c1991e0

File tree

4 files changed

+28
-8
lines changed

4 files changed

+28
-8
lines changed

arch/arm64/mm/hugetlbpage.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
284284
*/
285285
ptep = pte_alloc_map(mm, pmdp, addr);
286286
} else if (sz == PMD_SIZE) {
287-
if (IS_ENABLED(CONFIG_ARCH_WANT_HUGE_PMD_SHARE) &&
288-
pud_none(READ_ONCE(*pudp)))
287+
if (want_pmd_share(vma, addr) && pud_none(READ_ONCE(*pudp)))
289288
ptep = huge_pmd_share(mm, vma, addr, pudp);
290289
else
291290
ptep = (pte_t *)pmd_alloc(mm, pudp, addr);

include/linux/hugetlb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,4 +1040,6 @@ static inline __init void hugetlb_cma_check(void)
10401040
}
10411041
#endif
10421042

1043+
bool want_pmd_share(struct vm_area_struct *vma, unsigned long addr);
1044+
10431045
#endif /* _LINUX_HUGETLB_H */

include/linux/userfaultfd_k.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ static inline bool is_mergeable_vm_userfaultfd_ctx(struct vm_area_struct *vma,
5252
return vma->vm_userfaultfd_ctx.ctx == vm_ctx.ctx;
5353
}
5454

55+
/*
56+
* Never enable huge pmd sharing on uffd-wp registered vmas, because uffd-wp
57+
* protect information is per pgtable entry.
58+
*/
59+
static inline bool uffd_disable_huge_pmd_share(struct vm_area_struct *vma)
60+
{
61+
return vma->vm_flags & VM_UFFD_WP;
62+
}
63+
5564
static inline bool userfaultfd_missing(struct vm_area_struct *vma)
5665
{
5766
return vma->vm_flags & VM_UFFD_MISSING;

mm/hugetlb.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5326,6 +5326,15 @@ static bool vma_shareable(struct vm_area_struct *vma, unsigned long addr)
53265326
return false;
53275327
}
53285328

5329+
bool want_pmd_share(struct vm_area_struct *vma, unsigned long addr)
5330+
{
5331+
#ifdef CONFIG_USERFAULTFD
5332+
if (uffd_disable_huge_pmd_share(vma))
5333+
return false;
5334+
#endif
5335+
return vma_shareable(vma, addr);
5336+
}
5337+
53295338
/*
53305339
* Determine if start,end range within vma could be mapped by shared pmd.
53315340
* If yes, adjust start and end to cover range associated with possible
@@ -5382,9 +5391,6 @@ pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,
53825391
pte_t *pte;
53835392
spinlock_t *ptl;
53845393

5385-
if (!vma_shareable(vma, addr))
5386-
return (pte_t *)pmd_alloc(mm, pud, addr);
5387-
53885394
i_mmap_assert_locked(mapping);
53895395
vma_interval_tree_foreach(svma, &mapping->i_mmap, idx, idx) {
53905396
if (svma == vma)
@@ -5448,7 +5454,7 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
54485454
*addr = ALIGN(*addr, HPAGE_SIZE * PTRS_PER_PTE) - HPAGE_SIZE;
54495455
return 1;
54505456
}
5451-
#define want_pmd_share() (1)
5457+
54525458
#else /* !CONFIG_ARCH_WANT_HUGE_PMD_SHARE */
54535459
pte_t *huge_pmd_share(struct mm_struct *mm, struct vm_area_struct *vma,
54545460
unsigned long addr, pud_t *pud)
@@ -5466,7 +5472,11 @@ void adjust_range_if_pmd_sharing_possible(struct vm_area_struct *vma,
54665472
unsigned long *start, unsigned long *end)
54675473
{
54685474
}
5469-
#define want_pmd_share() (0)
5475+
5476+
bool want_pmd_share(struct vm_area_struct *vma, unsigned long addr)
5477+
{
5478+
return false;
5479+
}
54705480
#endif /* CONFIG_ARCH_WANT_HUGE_PMD_SHARE */
54715481

54725482
#ifdef CONFIG_ARCH_WANT_GENERAL_HUGETLB
@@ -5488,7 +5498,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
54885498
pte = (pte_t *)pud;
54895499
} else {
54905500
BUG_ON(sz != PMD_SIZE);
5491-
if (want_pmd_share() && pud_none(*pud))
5501+
if (want_pmd_share(vma, addr) && pud_none(*pud))
54925502
pte = huge_pmd_share(mm, vma, addr, pud);
54935503
else
54945504
pte = (pte_t *)pmd_alloc(mm, pud, addr);

0 commit comments

Comments
 (0)