FAILED: patch "[PATCH] mm/hugetlb: fix swap entry corruption when clearing uffd-wp" failed to apply to 6.6-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.6-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.6.y
git checkout FETCH_HEAD
git cherry-pick -x 83abe2fd5b3aeb3123b5408a5a91709c5538fb23
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026080516-turkey-eccentric-1ef1@gregkh' --subject-prefix 'PATCH 6.6.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 83abe2fd5b3aeb3123b5408a5a91709c5538fb23 Mon Sep 17 00:00:00 2001
From: "Kiryl Shutsemau (Meta)" <[email protected]>
Date: Wed, 8 Jul 2026 10:01:10 +0100
Subject: [PATCH] mm/hugetlb: fix swap entry corruption when clearing uffd-wp
at fork()
copy_hugetlb_page_range() clears the uffd-wp bit of migration and hwpoison entries with huge_pte_clear_uffd_wp(), which operates on the present-PTE bit position. Swap entries keep the uffd-wp state elsewhere -- the migration branch reads and sets it with pte_swp_uffd_wp() and pte_swp_mkuffd_wp() -- and the present-PTE position falls into the swap payload. On x86-64 it lands in the inverted swap offset, where a naturally-aligned hugetlb PFN always has the affected bit set, so the clear advances the encoded PFN by two pages.
No userfaultfd needs to be involved: the clear is guarded only by the child VMA not being uffd-wp registered, so a plain fork() with an in-flight hugetlb migration entry (or a poisoned hugetlb page) corrupts the entry copied into the child. Instrumenting the clear and forking after MADV_HWPOISON on a 2MB anon hugetlb page shows:
offset before=120e00
offset after =120e02
The fallout is mostly latent: rmap walks match migration entries by folio range and remove_migration_pte() rebuilds the PTE from the folio, so a within-folio PFN skew heals once migration completes. But any path that re-encodes the corrupted offset -- e.g. hugetlb_change_protection() rewriting a writable migration entry via make_readable_migration_entry(swp_offset(entry)) -- propagates it.
Migration entries legitimately carry uffd-wp, so clear it with pte_swp_clear_uffd_wp(), matching copy_nonpresent_pte() and move_huge_pte().
A hwpoison entry, on the other hand, never carries the uffd-wp bit: it is installed fresh by make_hwpoison_entry() (try_to_unmap_one() does not preserve uffd-wp on the hwpoison path) and hugetlb_change_protection() leaves hwpoison entries untouched. There was nothing to clear there, only the corruption, so drop the clear entirely.
mm/hugetlb.c +7 -3
@@ -4917,8 +4917,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, softleaf =softleaf_from_pte (entry);if (unlikely (softleaf_is_hwpoison (softleaf))) { -if (!userfaultfd_wp (dst_vma)) - entry =huge_pte_clear_uffd_wp (entry); +/* +* A hwpoison entry never carries the uffd-wp bit: it is +* installed fresh by make_hwpoison_entry() and +* hugetlb_change_protection() leaves it untouched, so +* there is nothing to clear for the child. +*/ set_huge_pte_at (dst, addr, dst_pte, entry, sz); }else if (unlikely (softleaf_is_migration (softleaf))) {bool uffd_wp =pte_swp_uffd_wp (entry);
@@ -4936,7 +4940,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,set_huge_pte_at (src, addr, src_pte, entry, sz); }if (!userfaultfd_wp (dst_vma)) - entry =huge_pte_clear_uffd_wp (entry); + entry =pte_swp_clear_uffd_wp (entry);set_huge_pte_at (dst, addr, dst_pte, entry, sz); }else if (unlikely (pte_is_marker (entry))) {const pte_marker marker =copy_pte_marker (softleaf, dst_vma);
PATCH 1/1 mm/hugetlb: fix swap entry corruption when clearing uffd-wp at fork()
From: "Kiryl Shutsemau (Meta)" <[email protected]>
copy_hugetlb_page_range() clears the uffd-wp bit of migration and hwpoison entries with huge_pte_clear_uffd_wp(), which operates on the present-PTE bit position. Swap entries keep the uffd-wp state elsewhere -- the migration branch reads and sets it with pte_swp_uffd_wp() and pte_swp_mkuffd_wp() -- and the present-PTE position falls into the swap payload. On x86-64 it lands in the inverted swap offset, where a naturally-aligned hugetlb PFN always has the affected bit set, so the clear advances the encoded PFN by two pages.
No userfaultfd needs to be involved: the clear is guarded only by the child VMA not being uffd-wp registered, so a plain fork() with an in-flight hugetlb migration entry (or a poisoned hugetlb page) corrupts the entry copied into the child. Instrumenting the clear and forking after MADV_HWPOISON on a 2MB anon hugetlb page shows:
offset before=120e00
offset after =120e02
The fallout is mostly latent: rmap walks match migration entries by folio range and remove_migration_pte() rebuilds the PTE from the folio, so a within-folio PFN skew heals once migration completes. But any path that re-encodes the corrupted offset -- e.g. hugetlb_change_protection() rewriting a writable migration entry via make_readable_migration_entry(swp_offset(entry)) -- propagates it.
Migration entries legitimately carry uffd-wp, so clear it with pte_swp_clear_uffd_wp(), matching copy_nonpresent_pte() and move_huge_pte().
A hwpoison entry, on the other hand, never carries the uffd-wp bit: it is installed fresh by make_hwpoison_entry() (try_to_unmap_one() does not preserve uffd-wp on the hwpoison path) and hugetlb_change_protection() leaves hwpoison entries untouched. There was nothing to clear there, only the corruption, so drop the clear entirely.
--- mm/hugetlb.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
mm/hugetlb.c +7 -3
@@ -5112,8 +5112,12 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,*/ ; }else if (unlikely (is_hugetlb_entry_hwpoisoned (entry))) { -if (!userfaultfd_wp (dst_vma)) - entry =huge_pte_clear_uffd_wp (entry); +/* +* A hwpoison entry never carries the uffd-wp bit: it is +* installed fresh by make_hwpoison_entry() and +* hugetlb_change_protection() leaves it untouched, so +* there is nothing to clear for the child. +*/ set_huge_pte_at (dst, addr, dst_pte, entry, sz); }else if (unlikely (is_hugetlb_entry_migration (entry))) {swp_entry_t swp_entry =pte_to_swp_entry (entry);
@@ -5132,7 +5136,7 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,set_huge_pte_at (src, addr, src_pte, entry, sz); }if (!userfaultfd_wp (dst_vma)) - entry =huge_pte_clear_uffd_wp (entry); + entry =pte_swp_clear_uffd_wp (entry);set_huge_pte_at (dst, addr, dst_pte, entry, sz); }else if (unlikely (is_pte_marker (entry))) { pte_marker marker = copy_pte_marker(
Signature · 1 line
2.54.0
Re: [PATCH 6.6.y] mm/hugetlb: fix swap entry corruption when clearing uffd-wp at fork()
Quoted 3 lines · Kiryl Shutsemau
Migration entries legitimately carry uffd-wp, so clear it with
pte_swp_clear_uffd_wp(), matchingcopy_nonpresent_pte()andmove_huge_pte().
Queued for 6.6, thanks.
Signature · 2 lines
Thanks, Sasha