mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
mm/page_isolation: guard compound_order() against racing
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.
Link: https://lore.kernel.org/20260825120549.966271-3-xiqi2@huawei.com
Fixes: b2c9e2fbba ("mm: make alloc_contig_range work at pageblock granularity")
Signed-off-by: Qi Xi <xiqi2@huawei.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Suggested-by: Zi Yan <ziy@nvidia.com>
Reviewed-by: Zi Yan <ziy@nvidia.com>
Cc: Brendan Jackman <brendan.jackman@linux.dev>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Nanyong Sun <sunnanyong@huawei.com>
Cc: Suren Baghdasaryan <surenb@google.com>
Cc: Vlastimil Babka <vbabka@kernel.org>
Cc: <stable@vger.kernel.org>
This commit is contained in:
parent
eba0e6297e
commit
4c767c5dd9
|
|
@ -419,10 +419,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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user