Re: [PATCH] mm/huge_memory: set PG_has_hwpoisoned only after new folio head is established
From: Lance Yang
Date: Wed Jul 01 2026 - 11:25:42 EST
On Wed, Jul 01, 2026 at 09:46:22AM -0400, Rik van Riel wrote:
>__split_folio_to_order() copies the hwpoison state onto each new
>sub-folio while splitting a folio to a non-zero order. It did so via
>
> if (handle_hwpoison && page_range_has_hwpoisoned(new_head, new_nr_pages))
> folio_set_has_hwpoisoned(new_folio);
>
>*before* clear_compound_head(new_head)/prep_compound_page(new_head, ...)
>turn @new_head from a tail page into a proper folio head.
>
>PG_has_hwpoisoned is a FOLIO_SECOND_PAGE flag, so folio_set_has_hwpoisoned()
>resolves to folio_flags(folio, 1). With the new compound_info-based
>page-flags layout, folio_flags() asserts the page is not a tail:
>
> VM_BUG_ON_PGFLAGS(page->compound_info & 1, page);
> VM_BUG_ON_PGFLAGS(n > 0 && !test_bit(PG_head, &page->flags.f), page);
>
>At the original call site @new_head still has the tail marker
>(compound_info bit 0 set, PG_head clear), so on CONFIG_DEBUG_VM kernels
>this hits:
>
> kernel BUG at include/linux/page-flags.h:354
> folio_flags+0x82
> folio_set_has_hwpoisoned
> __split_folio_to_order
> __split_unmapped_folio
> __folio_split
> truncate_inode_partial_folio (shmem hole-punch / MADV_REMOVE)
>
>Reproduced by syzkaller: hwpoison-inject a few subpages of a large shmem
>folio, then MADV_REMOVE (fallocate punch hole) on the same range, which
>splits the partial folio to a non-zero order.
Hmm, a bit weird ... for shmem, hwpoison-inject should call
try_to_split_thp_page(..., 0), i.e. a uniform split to order-0, no?
So MADV_REMOVE should no longer see a large poisoned folio. Am I missing
something, or is there a syzkaller link?
>Move the folio_set_has_hwpoisoned() call to after
>clear_compound_head()/prep_compound_page(), where @new_folio is a real
>order-new_order head folio (handle_hwpoison implies new_order != 0, so a
>second page always exists). The flag still lands on the same struct page
>(page[1] of the new folio); only the ordering relative to compound-head
>setup changes, satisfying the FOLIO_SECOND_PAGE precondition.
>
>Signed-off-by: Rik van Riel <riel@xxxxxxxxxxx>
>Assisted-by: Claude:claude-opus-4-8
>Fixes: fa5a06170036 ("mm/huge_memory: preserve PG_has_hwpoisoned if a folio is split to >0 order")
>---
Anyway, I used a local split_huge_pages_pid() hack to create this exact
situation: a large shmem folio with PG_has_hwpoisoned set and a poisoned
subpage before the non-uniform split. The BUG is gone with this patch :)
Tested-by: Lance Yang <lance.yang@xxxxxxxxx>