[SRU][N][PATCH 1/1] mm/huge_memory: fix dereferencing invalid pmd migration entry
Massimiliano Pellizzer
massimiliano.pellizzer at canonical.com
Wed Oct 15 12:32:59 UTC 2025
From: Gavin Guo <gavinguo at igalia.com>
commit be6e843fc51a584672dfd9c4a6a24c8cb81d5fb7 upstream.
When migrating a THP, concurrent access to the PMD migration entry during
a deferred split scan can lead to an invalid address access, as
illustrated below. To prevent this invalid access, it is necessary to
check the PMD migration entry and return early. In this context, there is
no need to use pmd_to_swp_entry and pfn_swap_entry_to_page to verify the
equality of the target folio. Since the PMD migration entry is locked, it
cannot be served as the target.
Mailing list discussion and explanation from Hugh Dickins: "An anon_vma
lookup points to a location which may contain the folio of interest, but
might instead contain another folio: and weeding out those other folios is
precisely what the "folio != pmd_folio((*pmd)" check (and the "risk of
replacing the wrong folio" comment a few lines above it) is for."
BUG: unable to handle page fault for address: ffffea60001db008
CPU: 0 UID: 0 PID: 2199114 Comm: tee Not tainted 6.14.0+ #4 NONE
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:split_huge_pmd_locked+0x3b5/0x2b60
Call Trace:
<TASK>
try_to_migrate_one+0x28c/0x3730
rmap_walk_anon+0x4f6/0x770
unmap_folio+0x196/0x1f0
split_huge_page_to_list_to_order+0x9f6/0x1560
deferred_split_scan+0xac5/0x12a0
shrinker_debugfs_scan_write+0x376/0x470
full_proxy_write+0x15c/0x220
vfs_write+0x2fc/0xcb0
ksys_write+0x146/0x250
do_syscall_64+0x6a/0x120
entry_SYSCALL_64_after_hwframe+0x76/0x7e
The bug is found by syzkaller on an internal kernel, then confirmed on
upstream.
Link: https://lkml.kernel.org/r/20250421113536.3682201-1-gavinguo@igalia.com
Link: https://lore.kernel.org/all/20250414072737.1698513-1-gavinguo@igalia.com/
Link: https://lore.kernel.org/all/20250418085802.2973519-1-gavinguo@igalia.com/
Fixes: 84c3fc4e9c56 ("mm: thp: check pmd migration entry in common path")
Signed-off-by: Gavin Guo <gavinguo at igalia.com>
Acked-by: David Hildenbrand <david at redhat.com>
Acked-by: Hugh Dickins <hughd at google.com>
Acked-by: Zi Yan <ziy at nvidia.com>
Reviewed-by: Gavin Shan <gshan at redhat.com>
Cc: Florent Revest <revest at google.com>
Cc: Matthew Wilcox (Oracle) <willy at infradead.org>
Cc: Miaohe Lin <linmiaohe at huawei.com>
Cc: <stable at vger.kernel.org>
Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
[gavin: backport the migration checking logic to __split_huge_pmd]
Signed-off-by: Gavin Guo <gavinguo at igalia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
(cherry picked from commit 3977946f61cdba87b6b5aaf7d7094e96089583a5 linux-6.6.y)
CVE-2025-37958
Signed-off-by: Massimiliano Pellizzer <massimiliano.pellizzer at canonical.com>
---
mm/huge_memory.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 7759b7a4934be..303c9571d13cf 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -2625,12 +2625,14 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
{
spinlock_t *ptl;
struct mmu_notifier_range range;
+ bool pmd_migration;
mmu_notifier_range_init(&range, MMU_NOTIFY_CLEAR, 0, vma->vm_mm,
address & HPAGE_PMD_MASK,
(address & HPAGE_PMD_MASK) + HPAGE_PMD_SIZE);
mmu_notifier_invalidate_range_start(&range);
ptl = pmd_lock(vma->vm_mm, pmd);
+ pmd_migration = is_pmd_migration_entry(*pmd);
/*
* If caller asks to setup a migration entry, we need a folio to check
@@ -2639,13 +2641,12 @@ void __split_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd,
VM_BUG_ON(freeze && !folio);
VM_WARN_ON_ONCE(folio && !folio_test_locked(folio));
- if (pmd_trans_huge(*pmd) || pmd_devmap(*pmd) ||
- is_pmd_migration_entry(*pmd)) {
+ if (pmd_trans_huge(*pmd) || pmd_devmap(*pmd) || pmd_migration) {
/*
- * It's safe to call pmd_page when folio is set because it's
- * guaranteed that pmd is present.
+ * Do not apply pmd_folio() to a migration entry; and folio lock
+ * guarantees that it must be of the wrong folio anyway.
*/
- if (folio && folio != page_folio(pmd_page(*pmd)))
+ if (folio && (pmd_migration || folio != page_folio(pmd_page(*pmd))))
goto out;
__split_huge_pmd_locked(vma, pmd, range.start, freeze);
}
--
2.48.1
More information about the kernel-team
mailing list