FAILED: patch "[PATCH] mm/hugetlb: fix swap entry corruption when clearing uffd-wp" failed to apply to 6.1-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.1-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.1.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-marathon-disjoin-9332@gregkh' --subject-prefix 'PATCH 6.1.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.
[ kas: 6.1 predates the huge_pte_*uffd_wp() -> pte_swp_*uffd_wp()
conversion in copy_hugetlb_page_range() (commit 5a2f8d22ace4), so apply the fix inline: convert the migration branch's uffd-wp read and set to the swap-position helpers too, otherwise the src re-encode (huge_pte_mkuffd_wp) corrupts the offset the same way; and drop the hwpoison clear ]
--- mm/hugetlb.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
mm/hugetlb.c +10 -8
@@ -5106,14 +5106,16 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,*/ ; }else if (unlikely (is_hugetlb_entry_hwpoisoned (entry))) { -bool uffd_wp =huge_pte_uffd_wp (entry); - -if (!userfaultfd_wp (dst_vma) && uffd_wp) - 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); }else if (unlikely (is_hugetlb_entry_migration (entry))) {swp_entry_t swp_entry =pte_to_swp_entry (entry); -bool uffd_wp =huge_pte_uffd_wp (entry); +bool uffd_wp =pte_swp_uffd_wp (entry);if (!is_readable_migration_entry (swp_entry) && cow) {/*
@@ -5124,11 +5126,11 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src, swp_offset(swp_entry)); entry =swp_entry_to_pte (swp_entry);if (userfaultfd_wp (src_vma) && uffd_wp) - entry =huge_pte_mkuffd_wp (entry); + entry =pte_swp_mkuffd_wp (entry);set_huge_pte_at (src, addr, src_pte, entry); } -if (!userfaultfd_wp (dst_vma) && uffd_wp) - entry =huge_pte_clear_uffd_wp (entry); +if (!userfaultfd_wp (dst_vma)) + entry =pte_swp_clear_uffd_wp (entry);set_huge_pte_at (dst, addr, dst_pte, entry); }else if (unlikely (is_pte_marker (entry))) {/*
Signature · 1 line
2.54.0
Re: [PATCH 6.1.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.1, thanks.
Signature · 2 lines
Thanks, Sasha