[RFC PATCH 04/40] mm: mm_init: fix zone assignment for pages in unavailable ranges
From: Rik van Riel
Date: Wed May 20 2026 - 11:26:51 EST
init_unavailable_range() initializes struct pages for memory holes between
memblock regions. It receives a zone ID from its caller, but that zone ID
is simply the last zone processed in memmap_init()'s iteration -- it does
not necessarily match the zone that actually spans each PFN in the hole.
When an unavailable range straddles a zone boundary (e.g. a hole between
DMA32 and Normal), all pages in the hole get tagged with the wrong zone in
page->flags. Any later page_zone() call on such a page returns the wrong
zone, which can cause accounting confusion or crashes when code assumes the
returned zone is valid for that page.
Fix by looking up the correct zone for each PFN in the hole. This is init-
only code running once at boot, so the per-page zone lookup has no
performance impact.
Signed-off-by: Rik van Riel <riel@xxxxxxxxxxx>
Assisted-by: Claude:claude-opus-4.7 syzkaller
---
mm/mm_init.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/mm/mm_init.c b/mm/mm_init.c
index 1bc909da9c13..47a222e49fc9 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -834,9 +834,27 @@ static void __init init_unavailable_range(unsigned long spfn,
{
unsigned long pfn;
u64 pgcnt = 0;
+ pg_data_t *pgdat = NODE_DATA(node);
+ int zid = zone;
for_each_valid_pfn(pfn, spfn, epfn) {
- __init_single_page(pfn_to_page(pfn), pfn, zone, node);
+ /*
+ * The caller's zone may not match the PFN when unavailable
+ * ranges straddle zone boundaries. Look up the correct zone
+ * so page->flags encodes the right zone for page_zone().
+ */
+ if (!zone_spans_pfn(&pgdat->node_zones[zid], pfn)) {
+ int z;
+
+ for (z = 0; z < MAX_NR_ZONES; z++) {
+ if (zone_spans_pfn(&pgdat->node_zones[z], pfn)) {
+ zid = z;
+ break;
+ }
+ }
+ }
+
+ __init_single_page(pfn_to_page(pfn), pfn, zid, node);
__SetPageReserved(pfn_to_page(pfn));
pgcnt++;
}
--
2.54.0