* [PATCH v2 0/2] mm/page_isolation: fix UBSAN shift-out-of-bounds in isolate_single_pageblock @ 2026-08-21 2:54 Qi Xi 2026-08-21 2:55 ` [PATCH v2 1/2] mm/page_isolation: fix UBSAN shift-out-of-bounds warning Qi Xi 2026-08-21 2:55 ` [PATCH v2 2/2] mm/page_isolation: guard compound_order() against racing Qi Xi 0 siblings, 2 replies; 5+ messages in thread From: Qi Xi @ 2026-08-21 2:54 UTC (permalink / raw) To: Andrew Morton, Vlastimil Babka Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, Johannes Weiner, Zi Yan, linux-mm, linux-kernel, sunnanyong, wangkefeng.wang, xiqi2 Patch 1 fixes a UBSAN shift-out-of-bounds warning in the PageBuddy branch of isolate_single_pageblock() triggered by concurrent buddy allocation. Patch 2 addresses the same class of issue in the PageCompound branch, where racy compound_order() and compound_head() reads could lead to out-of-range shifts or incorrect page skipping. Changes since v1: - Patch 1: drop VM_WARN_ON_ONCE, bail out with -EBUSY instead - Patch 2: new patch addressing PageCompound branch race Qi Xi (2): mm/page_isolation: fix UBSAN shift-out-of-bounds warning mm/page_isolation: guard compound_order() against racing mm/page_isolation.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) -- 2.33.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/2] mm/page_isolation: fix UBSAN shift-out-of-bounds warning 2026-08-21 2:54 [PATCH v2 0/2] mm/page_isolation: fix UBSAN shift-out-of-bounds in isolate_single_pageblock Qi Xi @ 2026-08-21 2:55 ` Qi Xi 2026-08-21 19:53 ` Zi Yan 2026-08-21 2:55 ` [PATCH v2 2/2] mm/page_isolation: guard compound_order() against racing Qi Xi 1 sibling, 1 reply; 5+ messages in thread From: Qi Xi @ 2026-08-21 2:55 UTC (permalink / raw) To: Andrew Morton, Vlastimil Babka Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, Johannes Weiner, Zi Yan, linux-mm, linux-kernel, sunnanyong, wangkefeng.wang, xiqi2 A contig-range allocation racing with buddy allocation on the adjacent pageblock can trigger: UBSAN: shift-out-of-bounds in mm/page_isolation.c:393:15 shift exponent -749042176 is negative Call trace: isolate_single_pageblock start_isolate_page_range alloc_contig_frozen_range_noprof alloc_contig_range_noprof isolate_single_pageblock() first calls set_migratetype_isolate() with zone->lock held, which marks the pageblock MIGRATE_ISOLATE and moves any free page straddling the boundary out of the way. Once the lock is dropped, it scans the MAX_ORDER_NR_PAGES-aligned window [start_pfn, boundary_pfn) locklessly, only to skip the free pages already handled above and to detect in-use pages straddling the boundary. Since this scan only reads page state to decide how far to skip and returns -EBUSY on a straddling in-use page, it does not take the lock. The window also covers the adjacent pageblock, whose free pages stay on the normal movable/CMA freelist and can be allocated concurrently. So after the scan observes PageBuddy(page), another CPU can allocate the page, leaving a stale value in page->private that makes "1 << order" shift out of range. Use buddy_order_unsafe() with READ_ONCE to read the order, and validate it is within MAX_PAGE_ORDER before shifting to prevent UBSAN warnings. Since pageblock_isolate_and_move_free_pages() already handles free pages straddling boundary_pfn under zone->lock, bail out with -EBUSY instead of VM_WARN_ON_ONCE() when a PageBuddy page appears to cross the boundary during the lockless scan. Fixes: b2c9e2fbba32 ("mm: make alloc_contig_range work at pageblock granularity") Cc: stable@vger.kernel.org Signed-off-by: Qi Xi <xiqi2@huawei.com> --- mm/page_isolation.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mm/page_isolation.c b/mm/page_isolation.c index 32ce8a7d9df3..f2b648a68531 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -387,13 +387,15 @@ static int isolate_single_pageblock(unsigned long boundary_pfn, } if (PageBuddy(page)) { - int order = buddy_order(page); + unsigned int order = buddy_order_unsafe(page); - /* pageblock_isolate_and_move_free_pages() handled this */ - VM_WARN_ON_ONCE(pfn + (1 << order) > boundary_pfn); - - pfn += 1UL << order; - continue; + /* buddy_order_unsafe() is racy. Validate the order before shifting. */ + if (order <= MAX_PAGE_ORDER && + pfn + (1UL << order) <= boundary_pfn) { + pfn += 1UL << order; + continue; + } + goto failed; } /* -- 2.33.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] mm/page_isolation: fix UBSAN shift-out-of-bounds warning 2026-08-21 2:55 ` [PATCH v2 1/2] mm/page_isolation: fix UBSAN shift-out-of-bounds warning Qi Xi @ 2026-08-21 19:53 ` Zi Yan 0 siblings, 0 replies; 5+ messages in thread From: Zi Yan @ 2026-08-21 19:53 UTC (permalink / raw) To: Qi Xi, Andrew Morton, Vlastimil Babka Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, Johannes Weiner, linux-mm, linux-kernel, sunnanyong, wangkefeng.wang On Thu Aug 20, 2026 at 10:55 PM EDT, Qi Xi wrote: > A contig-range allocation racing with buddy allocation on the adjacent > pageblock can trigger: > > UBSAN: shift-out-of-bounds in mm/page_isolation.c:393:15 > shift exponent -749042176 is negative > Call trace: > isolate_single_pageblock > start_isolate_page_range > alloc_contig_frozen_range_noprof > alloc_contig_range_noprof > > isolate_single_pageblock() first calls set_migratetype_isolate() with > zone->lock held, which marks the pageblock MIGRATE_ISOLATE and moves any > free page straddling the boundary out of the way. Once the lock is > dropped, it scans the MAX_ORDER_NR_PAGES-aligned window [start_pfn, > boundary_pfn) locklessly, only to skip the free pages already handled > above and to detect in-use pages straddling the boundary. Since this > scan only reads page state to decide how far to skip and returns -EBUSY > on a straddling in-use page, it does not take the lock. > > The window also covers the adjacent pageblock, whose free pages stay on > the normal movable/CMA freelist and can be allocated concurrently. So > after the scan observes PageBuddy(page), another CPU can allocate the > page, leaving a stale value in page->private that makes "1 << order" shift > out of range. > > Use buddy_order_unsafe() with READ_ONCE to read the order, and validate > it is within MAX_PAGE_ORDER before shifting to prevent UBSAN warnings. > > Since pageblock_isolate_and_move_free_pages() already handles free pages > straddling boundary_pfn under zone->lock, bail out with -EBUSY instead > of VM_WARN_ON_ONCE() when a PageBuddy page appears to cross the boundary > during the lockless scan. > > Fixes: b2c9e2fbba32 ("mm: make alloc_contig_range work at pageblock granularity") > Cc: stable@vger.kernel.org > Signed-off-by: Qi Xi <xiqi2@huawei.com> > --- > mm/page_isolation.c | 14 ++++++++------ > 1 file changed, 8 insertions(+), 6 deletions(-) > > diff --git a/mm/page_isolation.c b/mm/page_isolation.c > index 32ce8a7d9df3..f2b648a68531 100644 > --- a/mm/page_isolation.c > +++ b/mm/page_isolation.c > @@ -387,13 +387,15 @@ static int isolate_single_pageblock(unsigned long boundary_pfn, > } > > if (PageBuddy(page)) { > - int order = buddy_order(page); > + unsigned int order = buddy_order_unsafe(page); > > - /* pageblock_isolate_and_move_free_pages() handled this */ > - VM_WARN_ON_ONCE(pfn + (1 << order) > boundary_pfn); > - > - pfn += 1UL << order; > - continue; > + /* buddy_order_unsafe() is racy. Validate the order before shifting. */ > + if (order <= MAX_PAGE_ORDER && Can you add a comment like below to clarify the check? Thanks. /* * pageblock_isolate_and_move_free_pages() splits * cross-boundary PageBuddy, verify it. */ > + pfn + (1UL << order) <= boundary_pfn) { > + pfn += 1UL << order; > + continue; > + } > + goto failed; > } > > /* With that, feel free to add Reviewed-by: Zi Yan <ziy@nvidia.com> -- Best Regards, Yan, Zi ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 2/2] mm/page_isolation: guard compound_order() against racing 2026-08-21 2:54 [PATCH v2 0/2] mm/page_isolation: fix UBSAN shift-out-of-bounds in isolate_single_pageblock Qi Xi 2026-08-21 2:55 ` [PATCH v2 1/2] mm/page_isolation: fix UBSAN shift-out-of-bounds warning Qi Xi @ 2026-08-21 2:55 ` Qi Xi 2026-08-21 19:54 ` Zi Yan 1 sibling, 1 reply; 5+ messages in thread From: Qi Xi @ 2026-08-21 2:55 UTC (permalink / raw) To: Andrew Morton, Vlastimil Babka Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, Johannes Weiner, Zi Yan, linux-mm, linux-kernel, sunnanyong, wangkefeng.wang, xiqi2 The PageCompound branch reads compound_head() without holding a reference. A racing split or free can cause compound_head() to return a stale pointer, and compound_nr() reads the order from that stale head, leading to out-of-range shifts and making the skip distance meaningless. Read the order explicitly with compound_order() and validate it is within MAX_FOLIO_ORDER before shifting. Also verify the derived head_pfn against the legitimate pfn: the head must not be past pfn, must be aligned to nr_pages, and pfn must fall within the compound page. Bail out with -EBUSY if any check fails. Fixes: b2c9e2fbba32 ("mm: make alloc_contig_range work at pageblock granularity") Cc: stable@vger.kernel.org Suggested-by: Zi Yan <ziy@nvidia.com> Signed-off-by: Qi Xi <xiqi2@huawei.com> --- mm/page_isolation.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/mm/page_isolation.c b/mm/page_isolation.c index f2b648a68531..9cf7f92011bd 100644 --- a/mm/page_isolation.c +++ b/mm/page_isolation.c @@ -414,10 +414,28 @@ static int isolate_single_pageblock(unsigned long boundary_pfn, if (PageCompound(page)) { struct page *head = compound_head(page); unsigned long head_pfn = page_to_pfn(head); - unsigned long nr_pages = compound_nr(head); + unsigned int order = compound_order(head); + unsigned long nr_pages; + + /* compound_order() is racy. Cap it at MAX_FOLIO_ORDER. */ + if (order > MAX_FOLIO_ORDER) + goto failed; + + nr_pages = 1UL << order; + + /* + * compound_head() is also racy, so the derived head_pfn + * needs additional checks to make sure it is valid. + * Otherwise, just fail the check. pfn comes from + * __first_valid_page() as a legitimate PFN, so use it to + * check head_pfn. + */ + if (head_pfn > pfn || !IS_ALIGNED(head_pfn, nr_pages) || + pfn - head_pfn >= nr_pages) + goto failed; if (head_pfn + nr_pages <= boundary_pfn || - PageHuge(page)) { + PageHuge(head)) { pfn = head_pfn + nr_pages; continue; } -- 2.33.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 2/2] mm/page_isolation: guard compound_order() against racing 2026-08-21 2:55 ` [PATCH v2 2/2] mm/page_isolation: guard compound_order() against racing Qi Xi @ 2026-08-21 19:54 ` Zi Yan 0 siblings, 0 replies; 5+ messages in thread From: Zi Yan @ 2026-08-21 19:54 UTC (permalink / raw) To: Qi Xi, Andrew Morton, Vlastimil Babka Cc: Suren Baghdasaryan, Michal Hocko, Brendan Jackman, Johannes Weiner, linux-mm, linux-kernel, sunnanyong, wangkefeng.wang On Thu Aug 20, 2026 at 10:55 PM EDT, Qi Xi wrote: > The PageCompound branch reads compound_head() without holding a reference. > A racing split or free can cause compound_head() to return a stale pointer, > and compound_nr() reads the order from that stale head, leading to > out-of-range shifts and making the skip distance meaningless. > > Read the order explicitly with compound_order() and validate it is within > MAX_FOLIO_ORDER before shifting. Also verify the derived head_pfn against > the legitimate pfn: the head must not be past pfn, must be aligned to > nr_pages, and pfn must fall within the compound page. Bail out with > -EBUSY if any check fails. > > Fixes: b2c9e2fbba32 ("mm: make alloc_contig_range work at pageblock granularity") > Cc: stable@vger.kernel.org > Suggested-by: Zi Yan <ziy@nvidia.com> > Signed-off-by: Qi Xi <xiqi2@huawei.com> > --- > mm/page_isolation.c | 22 ++++++++++++++++++++-- > 1 file changed, 20 insertions(+), 2 deletions(-) > LGTM. Reviewed-by: Zi Yan <ziy@nvidia.com> -- Best Regards, Yan, Zi ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-21 19:54 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-21 2:54 [PATCH v2 0/2] mm/page_isolation: fix UBSAN shift-out-of-bounds in isolate_single_pageblock Qi Xi 2026-08-21 2:55 ` [PATCH v2 1/2] mm/page_isolation: fix UBSAN shift-out-of-bounds warning Qi Xi 2026-08-21 19:53 ` Zi Yan 2026-08-21 2:55 ` [PATCH v2 2/2] mm/page_isolation: guard compound_order() against racing Qi Xi 2026-08-21 19:54 ` Zi Yan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox