mm/hugetlb: fix huge_pmd_unshare() vs GUP-fast race
commit 1013af4f585fccc4d3e5c5824d174de2257f7d6d upstream.
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/20250528-hugetlb-fixes-splitrace-v2-2-1329349bad1a@google.com
Link: https://lkml.kernel.org/r/20250527-hugetlb-fixes-splitrace-v1-2-f4136f5ec58a@google.com
Fixes: 39dde65c99
("[PATCH] shared page table for hugetlb page")
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com>
Cc: Liam Howlett <liam.howlett@oracle.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Oscar Salvador <osalvador@suse.de>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
6a86053630
commit
fe68429041
@@ -7062,6 +7062,13 @@ int huge_pmd_unshare(struct mm_struct *mm, struct vm_area_struct *vma,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pud_clear(pud);
|
pud_clear(pud);
|
||||||
|
/*
|
||||||
|
* Once our caller drops the rmap lock, some other process might be
|
||||||
|
* using this page table as a normal, non-hugetlb page table.
|
||||||
|
* Wait for pending gup_fast() in other threads to finish before letting
|
||||||
|
* that happen.
|
||||||
|
*/
|
||||||
|
tlb_remove_table_sync_one();
|
||||||
ptdesc_pmd_pts_dec(virt_to_ptdesc(ptep));
|
ptdesc_pmd_pts_dec(virt_to_ptdesc(ptep));
|
||||||
mm_dec_nr_pmds(mm);
|
mm_dec_nr_pmds(mm);
|
||||||
return 1;
|
return 1;
|
||||||
|
Reference in New Issue
Block a user