[PATCH] mm/hugetlb: clear the hugetlb flag before leaking a hwpoisoned folio

From: Mani Tofigh via B4 Relay

Date: Wed Oct 28 2026 - 11:47:12 EST


From: Mani Tofigh <mt4022@xxxxxxxxxxxx>

If hugetlb_update_hwpoison() fails to allocate a raw_hwp_page, the folio
is marked raw_hwp_unreliable and __update_and_free_hugetlb_folio() leaks
it intentionally. With hugetlb_free_vmemmap=on, it's leaked before the
hugetlb flag is cleared, since commit 32c877191e02 ("hugetlb: do not
clear hugetlb dtor until allocating vmemmap") doesn't remove the flag
until the vmemmap is restored. The leaked folio is then a hugetlb folio
with refcount 0 and on no list (the same state as a hugetlb folio that
is being freed), but nothing is freeing it, and
dissolve_free_hugetlb_folio() keeps retrying on it. Offlining its
memory block then never completes:

[ 3.271255] Memory failure: 0x20007: recovery action for free huge page: Failed
[ 41.729695] INFO: task hugeoffline:77 blocked for more than 20 seconds.
...
[ 41.733014] get_online_mems+0xc1/0xd0
[ 41.733026] soft_offline_page+0xfd/0x9b0

alloc_and_dissolve_hugetlb_folio() loops on it the same way.

Check raw_hwp_unreliable after the hugetlb flag is cleared so that the
folio is leaked as a non-hugetlb folio, as without vmemmap
optimization. With the fix, no task hangs.

This bug was identified and confirmed with the help of fault-injection
testing on a deterministic hypervisor currently under development. The
C reproducer below however chains the specific events that trigger it.

Fixes: 32c877191e02 ("hugetlb: do not clear hugetlb dtor until allocating vmemmap")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Mani Tofigh <mt4022@xxxxxxxxxxxx>
---
This changes the same lines as patch 07/15 of Matthew's "Use
generic_file_read_iter() in hugetlbfs" series [1]. On top of that
series, the check goes right after the folio_clear_hugetlb() call.

[1] https://lore.kernel.org/all/20260805210557.1118966-8-willy@xxxxxxxxxxxxx/

Reproducer: https://github.com/manitofigh/files/blob/main/linux-repros/hugeoffline.c
---
mm/hugetlb.c | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 4a008afaedf9..4d6b7ab49502 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -1472,13 +1472,6 @@ static void __update_and_free_hugetlb_folio(struct hstate *h,
return;
}

- /*
- * If we don't know which subpages are hwpoisoned, we can't free
- * the hugepage, so it's leaked intentionally.
- */
- if (folio_test_hugetlb_raw_hwp_unreliable(folio))
- return;
-
/*
* If folio is not vmemmap optimized (!clear_flag), then the folio
* is no longer identified as a hugetlb page. hugetlb_vmemmap_restore_folio
@@ -1506,6 +1499,14 @@ static void __update_and_free_hugetlb_folio(struct hstate *h,
spin_unlock_irq(&hugetlb_lock);
}

+ /*
+ * If we don't know which subpages are hwpoisoned, we can't free
+ * the hugepage, so it's leaked intentionally, but only once it is
+ * no longer a hugetlb folio.
+ */
+ if (folio_test_hugetlb_raw_hwp_unreliable(folio))
+ return;
+
/*
* Move PageHWPoison flag from head page to the raw error pages,
* which makes any healthy subpages reusable.

---
base-commit: bae9a042b03b27924c6fd5bdc00a20d6a44d6041
change-id: 20261028-hugetlb-hwpoison-leak-b8d344361413

Best regards,
--
Mani Tofigh <mt4022@xxxxxxxxxxxx>