From: Sasha Levin <sashal@kernel.org>
To: stable@vger.kernel.org
Cc: "David Hildenbrand (Arm)" <david@kernel.org>,
Josef Bacik <josef@toxicpanda.com>,
Muhammad Usama Anjum <usama.anjum@arm.com>,
Sasha Levin <sashal@kernel.org>
Subject: [PATCH 6.12.y 4/4] mm/gup: recheck the PUD under PTL in follow_pud_mask()
Date: Wed, 11 Nov 2026 21:34:44 -0500 [thread overview]
Message-ID: <20261112023429.1348841-4-sashal@kernel.org> (raw)
In-Reply-To: <20261112023429.1348841-1-sashal@kernel.org>
From: "David Hildenbrand (Arm)" <david@kernel.org>
[ Upstream commit 79cb51d538abc459f74e046d27797d36b133ce0e ]
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 commit 1b1676180246 ("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: 1b1676180246 ("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>
[ sashal: adjusted hunk context for the older pud_write() permission check in place of can_follow_write_pud() ]
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
mm/gup.c | 32 ++++++++++++++++++--------------
1 file changed, 18 insertions(+), 14 deletions(-)
diff --git a/mm/gup.c b/mm/gup.c
index b8367869f7928..5c7ebf897476a 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -632,9 +632,6 @@ static struct page *follow_huge_pud(struct vm_area_struct *vma,
if ((flags & FOLL_WRITE) && !pud_write(pud))
return NULL;
- if (!pud_present(pud))
- return NULL;
-
pfn += (addr & ~PUD_MASK) >> PAGE_SHIFT;
page = pfn_to_page(pfn);
@@ -911,15 +908,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))
@@ -968,18 +967,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,
--
2.53.0
prev parent reply other threads:[~2026-11-12 2:34 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 1:09 FAILED: patch "[PATCH] mm/gup: recheck the PUD under PTL in follow_pud_mask()" failed to apply to 6.12-stable tree Sasha Levin
2026-11-12 2:34 ` [PATCH 6.12.y 1/4] mm/gup: remove redundant check for PCI P2PDMA page Sasha Levin
2026-11-12 2:34 ` [PATCH 6.12.y 2/4] mm/gup: remove pXX_devmap usage from get_user_pages() Sasha Levin
2026-11-12 2:34 ` [PATCH 6.12.y 3/4] mm/gup: remove dead pgmap refcounting code Sasha Levin
2026-11-12 2:34 ` Sasha Levin [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261112023429.1348841-4-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=david@kernel.org \
--cc=josef@toxicpanda.com \
--cc=stable@vger.kernel.org \
--cc=usama.anjum@arm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.