From: Josef Bacik free_pages_nolock() and free_frozen_pages_nolock() put the page on zone->trylock_free_pages if they get neither the pcp lock nor zone->lock. add_page_to_zone_llist() keeps the order of the page in page->private. The next free_one_page() which does get zone->lock passes these pages to split_large_buddy() with ->private still set. __free_one_page() writes ->private of the head of the resulting free page only. If the deferred page is merged with its lower buddy, it becomes a tail of that free page and keeps the order in ->private. All other free pages which are not the head of a buddy have a ->private of zero, and since commit 819bfbd47e49 ("mm/page_alloc: remove set_page_private() in prep_compound_tail()") the allocator relies on it. When the stale page is allocated again as a tail of a larger page: - prep_compound_tail() hits VM_WARN_ON_ONCE(tail->private); - with the page checks enabled, __free_pages_prepare() reports "BUG: Bad page state", "nonzero private", and leaks the whole page; - without them the value is seen by whoever uses that page next, e.g. as folio->private of a folio which is split off a large folio. Only pages of an order above zero are affected. Those can be freed through _nolock() by the stack depot (order 2) and by SLUB (a new slab which cannot be added to the partial list by kmalloc_nolock()). Clear ->private when the page is taken off the list. Fixes: 8c57b687e833 ("mm, bpf: Introduce free_pages_nolock()") Cc: Assisted-by: LLM Signed-off-by: Josef Bacik Signed-off-by: David Hildenbrand (Arm) Signed-off-by: Vlastimil Babka (SUSE) --- mm/page_alloc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index ee013db45ed1..9a4d3352c037 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1562,6 +1562,8 @@ static void free_one_page(struct zone *zone, struct page *page, llist_for_each_entry_safe(p, tmp, llnode, pcp_llist) { unsigned int p_order = p->private; + /* It may end up as a tail of the merged free page. */ + p->private = 0; split_large_buddy(zone, p, page_to_pfn(p), p_order, fpi_flags); __count_vm_events(PGFREE, 1 << p_order); } -- 2.56.0