mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
mm/gup: recheck the PUD under PTL in follow_pud_mask()
follow_pud_mask() reads the PUD without a lock. If it is a leaf, it takes the lock and calls follow_huge_pud(), which reads the entry again, but only tests whether it is present. A leaf can be zapped concurrently and a page fault can fill the empty entry with a page table. This currently only applies when device-dax makes use of PUD entries, and the fault after e.g., MADV_DONTNEED would find THPs to be disabled. That requires a sequence like 1) Fault in the PUD 2) Disable THP 3) Have GUP race against concurrent MADV_DONTNEED + refault Which is rather unlikely to happen in practice, which is why we never received a report about such a problem. Before commit1b16761802("mm/gup: handle huge pud for follow_pud_mask()"), we performed that check in follow_devmap_pud() by testing for pud_devmap(). Let's fix it by just structuring follow_pud_mask() similar to follow_pmd_mask(), minus protnone stuff that does not apply and minus FOLL_SPLIT_PMD handling. That makes it much easier to spot any such inconsistencies. Perform the pud_bad() check early in follow_pmd_mask() and just forward the PUD value. We can drop the pud_present() check in follow_huge_pud(), just like we have in follow_huge_pmd(). Even though we are performing more changes than strictly necessary, the fix should be easy to backport. Fixes:1b16761802("mm/gup: handle huge pud for follow_pud_mask()") Cc: stable@vger.kernel.org Reported-by: Josef Bacik <josef@toxicpanda.com> Tested-by: Muhammad Usama Anjum <usama.anjum@arm.com> Link: https://patch.msgid.link/20261031-fixes_core-v3-19-d6437171ecc3@kernel.org Signed-off-by: David Hildenbrand (Arm) <david@kernel.org>
This commit is contained in:
parent
fe2817e3d5
commit
79cb51d538
32
mm/gup.c
32
mm/gup.c
|
|
@ -658,9 +658,6 @@ static struct page *follow_huge_pud(struct vm_area_struct *vma,
|
|||
|
||||
assert_spin_locked(pud_lockptr(mm, pudp));
|
||||
|
||||
if (!pud_present(pud))
|
||||
return NULL;
|
||||
|
||||
if ((flags & FOLL_WRITE) &&
|
||||
!can_follow_write_pud(pud, pfn_to_page(pfn), vma, flags))
|
||||
return NULL;
|
||||
|
|
@ -896,15 +893,17 @@ static struct page *follow_page_pte(struct vm_area_struct *vma,
|
|||
}
|
||||
|
||||
static struct page *follow_pmd_mask(struct vm_area_struct *vma,
|
||||
unsigned long address, pud_t *pudp,
|
||||
unsigned int flags,
|
||||
unsigned long *page_mask)
|
||||
unsigned long address, pud_t *pudp, pud_t pud,
|
||||
unsigned int flags, unsigned long *page_mask)
|
||||
{
|
||||
pmd_t *pmd, pmdval;
|
||||
spinlock_t *ptl;
|
||||
struct page *page;
|
||||
struct mm_struct *mm = vma->vm_mm;
|
||||
|
||||
if (unlikely(pud_bad(pud)))
|
||||
return no_page_table(vma, flags, address);
|
||||
|
||||
pmd = pmd_offset(pudp, address);
|
||||
pmdval = pmdp_get_lockless(pmd);
|
||||
if (pmd_none(pmdval))
|
||||
|
|
@ -953,18 +952,23 @@ static struct page *follow_pud_mask(struct vm_area_struct *vma,
|
|||
pud = pudp_get(pudp);
|
||||
if (!pud_present(pud))
|
||||
return no_page_table(vma, flags, address);
|
||||
if (pud_leaf(pud)) {
|
||||
ptl = pud_lock(mm, pudp);
|
||||
page = follow_huge_pud(vma, address, pudp, flags, page_mask);
|
||||
if (!pud_leaf(pud))
|
||||
return follow_pmd_mask(vma, address, pudp, pud, flags, page_mask);
|
||||
|
||||
ptl = pud_lock(mm, pudp);
|
||||
pud = *pudp;
|
||||
if (unlikely(!pud_present(pud))) {
|
||||
spin_unlock(ptl);
|
||||
if (page)
|
||||
return page;
|
||||
return no_page_table(vma, flags, address);
|
||||
}
|
||||
if (unlikely(pud_bad(pud)))
|
||||
return no_page_table(vma, flags, address);
|
||||
if (unlikely(!pud_leaf(pud))) {
|
||||
spin_unlock(ptl);
|
||||
return follow_pmd_mask(vma, address, pudp, pud, flags, page_mask);
|
||||
}
|
||||
|
||||
return follow_pmd_mask(vma, address, pudp, flags, page_mask);
|
||||
page = follow_huge_pud(vma, address, pudp, flags, page_mask);
|
||||
spin_unlock(ptl);
|
||||
return page;
|
||||
}
|
||||
|
||||
static struct page *follow_p4d_mask(struct vm_area_struct *vma,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user