Skip to content

Commit 76a736b

Browse files
committed
mm/hugetlb: fix huge_pmd_unshare() vs GUP-fast race
jira VULN-71586 cve CVE-2025-38085 commit-author Jann Horn <[email protected]> commit 1013af4 upstream-diff Stable 5.15 backport a3d864c901a300c295692d129159fc3001a56185 was used for the actual cherry-pick. Additionally the 2ba99c5 minus changes in `mm/khugepaged.c' was included to expose the `tlb_remove_table_sync_one' function. huge_pmd_unshare() drops a reference on a page table that may have previously been shared across processes, potentially turning it into a normal page table used in another process in which unrelated VMAs can afterwards be installed. If this happens in the middle of a concurrent gup_fast(), gup_fast() could end up walking the page tables of another process. While I don't see any way in which that immediately leads to kernel memory corruption, it is really weird and unexpected. Fix it with an explicit broadcast IPI through tlb_remove_table_sync_one(), just like we do in khugepaged when removing page tables for a THP collapse. Link: https://lkml.kernel.org/r/[email protected] Link: https://lkml.kernel.org/r/[email protected] Fixes: 39dde65 ("[PATCH] shared page table for hugetlb page") Signed-off-by: Jann Horn <[email protected]> Reviewed-by: Lorenzo Stoakes <[email protected]> Cc: Liam Howlett <[email protected]> Cc: Muchun Song <[email protected]> Cc: Oscar Salvador <[email protected]> Cc: Vlastimil Babka <[email protected]> Cc: <[email protected]> Signed-off-by: Andrew Morton <[email protected]> (cherry picked from commit 1013af4) Signed-off-by: Marcin Wcisło <[email protected]>
1 parent d7056d3 commit 76a736b

File tree

3 files changed

+12
-3
lines changed

3 files changed

+12
-3
lines changed

include/asm-generic/tlb.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,16 @@ extern void tlb_remove_table(struct mmu_gather *tlb, void *table);
222222
#define tlb_needs_table_invalidate() (true)
223223
#endif
224224

225+
void tlb_remove_table_sync_one(void);
226+
225227
#else
226228

227229
#ifdef tlb_needs_table_invalidate
228230
#error tlb_needs_table_invalidate() requires MMU_GATHER_RCU_TABLE_FREE
229231
#endif
230232

233+
static inline void tlb_remove_table_sync_one(void) { }
234+
231235
#endif /* CONFIG_MMU_GATHER_RCU_TABLE_FREE */
232236

233237

mm/hugetlb.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6778,6 +6778,13 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
67786778
return 0;
67796779

67806780
pud_clear(pud);
6781+
/*
6782+
* Once our caller drops the rmap lock, some other process might be
6783+
* using this page table as a normal, non-hugetlb page table.
6784+
* Wait for pending gup_fast() in other threads to finish before letting
6785+
* that happen.
6786+
*/
6787+
tlb_remove_table_sync_one();
67816788
atomic_dec(&virt_to_page(ptep)->pt_share_count);
67826789
mm_dec_nr_pmds(mm);
67836790
/*

mm/mmu_gather.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ static void tlb_remove_table_smp_sync(void *arg)
140140
/* Simply deliver the interrupt */
141141
}
142142

143-
static void tlb_remove_table_sync_one(void)
143+
void tlb_remove_table_sync_one(void)
144144
{
145145
/*
146146
* This isn't an RCU grace period and hence the page-tables cannot be
@@ -164,8 +164,6 @@ static void tlb_remove_table_free(struct mmu_table_batch *batch)
164164

165165
#else /* !CONFIG_MMU_GATHER_RCU_TABLE_FREE */
166166

167-
static void tlb_remove_table_sync_one(void) { }
168-
169167
static void tlb_remove_table_free(struct mmu_table_batch *batch)
170168
{
171169
__tlb_remove_table_free(batch);

0 commit comments

Comments
 (0)