mark_free_pages() walks every valid PFN of a zone and calls page_zone() on its struct page. A PFN in an offlined memory block is still valid, but its memmap is no longer initialised: offlining poisons it. With CONFIG_DEBUG_VM that is caught on the first such page: page:ffffea0005400000 is uninitialized and poisoned page dumped because: VM_BUG_ON_PAGE(PagePoisoned(page)) kernel BUG at ./include/linux/mm.h:2402! RIP: 0010:mark_free_pages+0x49a/0x4b0 kernel/power/snapshot.c:1266 count_data_pages kernel/power/snapshot.c:1417 [inline] hibernate_preallocate_memory+0x14f/0x750 kernel/power/snapshot.c:1859 hibernation_snapshot+0xd5/0x3d0 kernel/power/hibernate.c:416 hibernate+0x28f/0x680 kernel/power/hibernate.c:813 syzbot's reproducer offlines memory42 and then writes "disk" to /sys/power/state; that page is the first one of memory42. Without CONFIG_DEBUG_VM the walk reads stale page flags instead. saveable_page() and saveable_highmem_page() already use pfn_to_online_page() for this since commit 5b56db37218e ("PM/Hibernate: use pfn_to_online_page()"). Do the same here. Offline pages are never on a free list, so the second loop in this function does not need it. Fixes: d33695b16a9f ("mm/memory_hotplug: poison memmap in remove_pfn_range_from_zone()") Reported-by: syzbot+609c80047e3e808b465c@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Yogesh Gaur --- Built with W=1 only; not runtime-tested. kernel/power/snapshot.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index b209712cb2c3..0cd8f05c1395 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -1256,14 +1256,14 @@ static void mark_free_pages(struct zone *zone) max_zone_pfn = zone_end_pfn(zone); for_each_valid_pfn(pfn, zone->zone_start_pfn, max_zone_pfn) { - page = pfn_to_page(pfn); - if (!--page_count) { touch_nmi_watchdog(); page_count = WD_PAGE_COUNT; } - if (page_zone(page) != zone) + /* the memmap of offline memory is not initialised */ + page = pfn_to_online_page(pfn); + if (!page || page_zone(page) != zone) continue; if (!swsusp_page_is_forbidden(page)) -- 2.55.0.windows.5