From: sashiko-bot@kernel.org
To: "Vlastimil Babka (SUSE)" <vbabka@kernel.org>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Zi Yan" <ziy@nvidia.com>,
"Shakeel Butt" <shakeel.butt@linux.dev>,
"Mike Rapoport" <rppt@kernel.org>,
"David Hildenbrand" <david@kernel.org>,
"Oscar Salvador" <osalvador@suse.de>,
"Matthew Wilcox (Oracle)" <willy@infradead.org>
Cc: "Suren Baghdasaryan" <surenb@google.com>,
"Michal Hocko" <mhocko@suse.com>,
"Brendan Jackman" <brendan.jackman@linux.dev>,
"Johannes Weiner" <hannes@cmpxchg.org>,
linux-mm@kvack.org, "Josef Bacik" <josef@toxicpanda.com>
Subject: Re: [PATCH 6/9] mm/compaction: trust zone->contiguous only in the span
Date: Wed, 28 Oct 2026 15:47:46 +0000 [thread overview]
Message-ID: <sashiko-outbox-188078@kernel.org> (raw)
In-Reply-To: <20261027-pgalloc-ai-hotfixes-v1-6-9c7d1aca801e@kernel.org>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] mm/compaction: pageblock_pfn_to_page() ignores end_pfn in span check
- [High] mm/compaction: racy zone_spans_pfn() check in pageblock_pfn_to_page()
--
commit fc93d5609d6ae896a622cb4ad47040ba39704072
Author: Josef Bacik <josef@toxicpanda.com>
mm/compaction: trust zone->contiguous only in the span
This commit restricts the fast path in pageblock_pfn_to_page() so that
zone->contiguous is only trusted when start_pfn falls within the zone span,
and pairs smp_store_release() in set_zone_contiguous() with
smp_load_acquire() in pageblock_pfn_to_page().
> diff --git a/mm/page_alloc.h b/mm/page_alloc.h
> index 2b1017a91928c..e24d1203c5148 100644
> --- a/mm/page_alloc.h
> +++ b/mm/page_alloc.h
> @@ -216,7 +216,12 @@ extern struct page *__pageblock_pfn_to_page(unsigned long start_pfn,
> static inline struct page *pageblock_pfn_to_page(unsigned long start_pfn,
> unsigned long end_pfn, struct zone *zone)
> {
> - if (zone->contiguous)
> + /*
> + * A scanner may still be at a pfn that has left the zone: the zone is
> + * shrunk before set_zone_contiguous() says that it has no holes.
> + */
> + if (smp_load_acquire(&zone->contiguous) &&
> + zone_spans_pfn(zone, start_pfn))
> return pfn_to_page(start_pfn);
[Severity: High]
Can pageblock_pfn_to_page() still return a non-NULL page for a range that has
left the zone because it only checks start_pfn and ignores end_pfn?
When the lowest memory block at the start of a multi-block zone is offlined
and removed while compact_zone() is running with cc->migrate_pfn in that
removed lower block, remove_pfn_range_from_zone() advances
zone->zone_start_pfn via shrink_zone_span() and sets zone->contiguous = true
via set_zone_contiguous() when the remaining zone has no holes.
On the next call to compact_zone() -> isolate_migratepages() in
mm/compaction.c, block_start_pfn is clamped up to cc->zone->zone_start_pfn
while low_pfn and block_end_pfn remain in the removed range below
cc->zone->zone_start_pfn:
mm/compaction.c:isolate_migratepages() {
low_pfn = fast_find_migrateblock(cc);
block_start_pfn = pageblock_start_pfn(low_pfn);
if (block_start_pfn < cc->zone->zone_start_pfn)
block_start_pfn = cc->zone->zone_start_pfn;
...
block_end_pfn = pageblock_end_pfn(low_pfn);
...
page = pageblock_pfn_to_page(block_start_pfn,
block_end_pfn, cc->zone);
...
if (isolate_migratepages_block(cc, low_pfn, block_end_pfn,
isolate_mode))
return ISOLATE_ABORT;
}
Because block_start_pfn was clamped to cc->zone->zone_start_pfn,
zone_spans_pfn(zone, start_pfn) evaluates to true even though
start_pfn >= end_pfn and end_pfn is in the removed range.
pageblock_pfn_to_page() then returns pfn_to_page(start_pfn), and
isolate_migratepages() passes low_pfn and block_end_pfn to
isolate_migratepages_block(), which dereferences the unmapped or poisoned
memmap for low_pfn.
The same start_pfn clamping occurs in isolate_migratepages_range(),
isolate_freepages_range(), and fast_isolate_around():
mm/compaction.c:fast_isolate_around() {
start_pfn = max(pageblock_start_pfn(pfn), cc->zone->zone_start_pfn);
end_pfn = min(pageblock_end_pfn(pfn), zone_end_pfn(cc->zone));
page = pageblock_pfn_to_page(start_pfn, end_pfn, cc->zone);
}
When pfn < cc->zone->zone_start_pfn, start_pfn > end_pfn, so
pageblock_pfn_to_page() returns pfn_to_page(cc->zone->zone_start_pfn) and
fast_isolate_around() -> isolate_freepages_block() falsely sets the pageblock
skip hint at the new start of the zone. Additionally, if zone_end_pfn(zone) is
not pageblock-aligned or shrinks from the top while remaining contiguous,
start_pfn < zone_end_pfn(zone) < end_pfn allows isolate_migratepages() to scan
past zone_end_pfn(zone).
Note that __pageblock_pfn_to_page() in mm/page_alloc.c also does not check
that start_pfn < end_pfn or that end_pfn - 1 is online when start_pfn has been
clamped into the online zone:
mm/page_alloc.c:__pageblock_pfn_to_page() {
end_pfn--;
if (!pfn_valid(end_pfn))
return NULL;
start_page = pfn_to_online_page(start_pfn);
}
Should pageblock_pfn_to_page() and __pageblock_pfn_to_page() verify that
start_pfn < end_pfn and that both start_pfn and end_pfn - 1 are spanned and
online?
[Severity: High]
Is reading zone->contiguous before zone_spans_pfn(zone, start_pfn) without
zone->span_seqlock safe against concurrent zone span changes?
Reading zone->contiguous first and the zone span second only pairs with the
false to true transition in set_zone_contiguous(). On the true to false
transition in move_pfn_range_to_zone() and remove_pfn_range_from_zone() in
mm/memory_hotplug.c, clear_zone_contiguous() clears zone->contiguous with a
plain store before the zone span is modified:
mm/mm_init.h:clear_zone_contiguous() {
zone->contiguous = false;
}
mm/memory_hotplug.c:move_pfn_range_to_zone() {
clear_zone_contiguous(zone);
if (zone_is_empty(zone))
init_currently_empty_zone(zone, start_pfn, nr_pages);
resize_zone_range(zone, start_pfn, nr_pages);
resize_pgdat_range(pgdat, start_pfn, nr_pages);
...
memmap_init_range(nr_pages, nid, zone_idx(zone), start_pfn, 0,
MEMINIT_HOTPLUG, altmap, migratetype,
isolate_pageblock);
}
Because the writer clears zone->contiguous first and updates the span second,
a reader in pageblock_pfn_to_page() executing in the same order can load
zone->contiguous == true just before clear_zone_contiguous(zone) and then
observe the expanded span from resize_zone_range() in
zone_spans_pfn(zone, start_pfn). When a contiguous zone is expanded across a
PFN hole or before memmap_init_range() initializes the new range and its
sections are marked online, a compaction scanner with a PFN in that hole or
new range can bypass __pageblock_pfn_to_page() and access an unmapped hole or
uninitialized memmap.
Furthermore, zone_spans_pfn() in pageblock_pfn_to_page() reads
zone->zone_start_pfn and zone->spanned_pages without zone->span_seqlock
(which remains unguarded even after later commits in the series resize zones
under span_seqlock and check pageblock pfns under span_seqlock in
__pageblock_pfn_to_page()):
mm/memory_hotplug.c:shrink_zone_span() {
if (pfn) {
zone->spanned_pages = zone_end_pfn(zone) - pfn;
zone->zone_start_pfn = pfn;
} else {
zone->zone_start_pfn = 0;
zone->spanned_pages = 0;
}
}
When shrink_zone_span() raises zone->zone_start_pfn and reduces
zone->spanned_pages, a reader in pageblock_pfn_to_page() can observe the new
zone->zone_start_pfn together with the old zone->spanned_pages, causing
zone_end_pfn(zone) to transiently extend past the actual upper end of the
zone into a previously removed PFN range.
Should pageblock_pfn_to_page() read the zone span and zone->contiguous under
zone->span_seqlock, and ensure clear_zone_contiguous() is properly ordered
against span updates?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261027-pgalloc-ai-hotfixes-v1-0-9c7d1aca801e@kernel.org?part=6
next prev parent reply other threads:[~2026-10-28 15:47 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 15:46 [PATCH 0/9] mm, page_alloc: several stable fixes Vlastimil Babka (SUSE)
2026-10-27 15:46 ` [PATCH 1/9] mm/page_alloc: don't undo a racing start_isolate_page_range() Vlastimil Babka (SUSE)
2026-10-27 15:48 ` Zi Yan
2026-10-27 18:31 ` Gregory Price (Meta)
2026-10-27 15:46 ` [PATCH 2/9] mm/page_alloc: clear ->private of deferred free pages Vlastimil Babka (SUSE)
2026-10-27 18:38 ` Zi Yan
2026-10-27 15:46 ` [PATCH 3/9] mm: page_frag: refill the cache for large fragments Vlastimil Babka (SUSE)
2026-10-27 15:46 ` [PATCH 4/9] mm/page_alloc: keep the pagesets of an empty zone Vlastimil Babka (SUSE)
2026-10-27 15:46 ` [PATCH 5/9] mm, page_alloc: pass zone pointer to trace_reclaim_retry_zone Vlastimil Babka (SUSE)
2026-10-27 18:35 ` Gregory Price (Meta)
2026-10-27 18:39 ` Zi Yan
2026-10-27 15:46 ` [PATCH 6/9] mm/compaction: trust zone->contiguous only in the span Vlastimil Babka (SUSE)
2026-10-28 15:47 ` sashiko-bot [this message]
2026-10-27 15:46 ` [PATCH 7/9] mm: page_isolation: do not take slab pages for free Vlastimil Babka (SUSE)
2026-10-27 21:04 ` Zi Yan
2026-10-27 15:46 ` [PATCH 8/9] mm/memory_hotplug: resize zones under their seqlock Vlastimil Babka (SUSE)
2026-10-27 15:46 ` [PATCH 9/9] mm/page_alloc: check pageblock pfns under span seqlock Vlastimil Babka (SUSE)
2026-10-28 15:47 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-188078@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=brendan.jackman@linux.dev \
--cc=david@kernel.org \
--cc=hannes@cmpxchg.org \
--cc=josef@toxicpanda.com \
--cc=linux-mm@kvack.org \
--cc=mhocko@suse.com \
--cc=osalvador@suse.de \
--cc=rppt@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=shakeel.butt@linux.dev \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
--cc=willy@infradead.org \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox