When vmalloc allocates high-order pages and splits them via split_page(), tail pages may retain stale page->private values from previous use by the buddy allocator. This causes a use-after-free in the swap subsystem. The swap code uses vmalloc_to_page() to get struct page pointers for swap_map, then uses page->private to track swap count continuations. In add_swap_count_ continuation(), the condition "if (!page_private(head))" assumes fresh pages have page->private == 0, but tail pages from split_page() may have non-zero stale values. When page->private accidentally contains a value like SWP_CONTINUED (32), swap_count_continued() incorrectly assumes the continuation list is valid and iterates over uninitialized page->lru, which may contain LIST_POISON values from a previous list_del(), causing a crash: KASAN: maybe wild-memory-access in range [0xdead000000000100-0xdead000000000107] RIP: 0010:__do_sys_swapoff+0x1151/0x1860 Fix this by clearing page->private for tail pages in split_page(). Note that we don't touch page->lru to avoid breaking split_free_page() which may have the head page on a list. Fixes: 3b8000ae185c ("mm/vmalloc: huge vmalloc backing pages should be split rather than compound") Cc: stable@vger.kernel.org Signed-off-by: Mikhail Gavrilov --- mm/page_alloc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index cbf758e27aa2..3604a00e2118 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -3122,8 +3122,14 @@ void split_page(struct page *page, unsigned int order) VM_BUG_ON_PAGE(PageCompound(page), page); VM_BUG_ON_PAGE(!page_count(page), page); - for (i = 1; i < (1 << order); i++) + for (i = 1; i < (1 << order); i++) { set_page_refcounted(page + i); + /* + * Tail pages may have stale page->private from buddy + * allocator or previous use. Clear it. + */ + set_page_private(page + i, 0); + } split_page_owner(page, order, 0); pgalloc_tag_split(page_folio(page), order, 0); split_page_memcg(page, order); -- 2.53.0