The statement phys += contig_pages * PAGE_SIZE at the end of the inner for-loop in kho_restore_vmalloc() is redundant code. The variable 'phys' is local to each loop iteration (re-assigned from chunk->phys[i] on every iteration) and is never referenced after this increment, so the statement has no functional impact. Remove it to avoid confusion during code review. Signed-off-by: Chenghao Duan --- kernel/liveupdate/kexec_handover.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 175c08a6e41e..ba03ff5baa9e 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -1191,8 +1191,6 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) for (int j = 0; j < contig_pages; j++) pages[idx++] = page + j; - - phys += contig_pages * PAGE_SIZE; } page = kho_restore_pages(virt_to_phys(chunk), 1); -- 2.25.1 In kho_restore_vmalloc(), when kho_restore_pages() succeeds, the recovered pages are handed to the buddy allocator (via adjust_managed_page_count()). If any later step (e.g. __get_vm_area_node() or vmap_pages_range()) fails, the original error path only called kvfree(pages), leaking those folio pages. Fix by tracking how many folio groups have been restored with restored_idx. On any failure, use err_unwind_restored to walk restored_idx backwards and return each folio group to the buddy via __free_pages() before freeing the pages array. Signed-off-by: Chenghao Duan --- kernel/liveupdate/kexec_handover.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index ba03ff5baa9e..ed35405f59ab 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -1161,6 +1161,7 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) struct vm_struct *area; struct page **pages; unsigned int idx = 0; + unsigned int restored_idx = 0; int err; vm_flags = kho_flags_to_vmalloc(preservation->flags); @@ -1183,11 +1184,11 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) phys_addr_t phys = chunk->phys[i]; if (idx + contig_pages > total_pages) - goto err_free_pages_array; + goto err_unwind_restored; page = kho_restore_pages(phys, contig_pages); if (!page) - goto err_free_pages_array; + goto err_unwind_restored; for (int j = 0; j < contig_pages; j++) pages[idx++] = page + j; @@ -1195,13 +1196,14 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) page = kho_restore_pages(virt_to_phys(chunk), 1); if (!page) - goto err_free_pages_array; + goto err_unwind_restored; + restored_idx = idx; chunk = KHOSER_LOAD_PTR(chunk->hdr.next); __free_page(page); } if (idx != total_pages) - goto err_free_pages_array; + goto err_unwind_restored; area = __get_vm_area_node(total_pages * PAGE_SIZE, align, shift, vm_flags | VM_UNINITIALIZED, @@ -1209,7 +1211,7 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) NUMA_NO_NODE, GFP_KERNEL, __builtin_return_address(0)); if (!area) - goto err_free_pages_array; + goto err_unwind_restored; addr = (unsigned long)area->addr; size = get_vm_area_size(area); @@ -1231,7 +1233,16 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation) err_free_vm_area: free_vm_area(area); -err_free_pages_array: +err_unwind_restored: + /* + * Pages already restored via kho_restore_pages() have been given to + * the buddy allocator (via adjust_managed_page_count()). Return them + * to the buddy so that failure leaves the system in a clean state. + */ + while (restored_idx > 0) { + restored_idx -= contig_pages; + __free_pages(pages[restored_idx], order); + } kvfree(pages); return NULL; } -- 2.25.1