[PATCH v1 2/2] kho: unwind restored pages on kho_restore_vmalloc error
From: Chenghao Duan
Date: Fri Aug 07 2026 - 03:57:02 EST
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 <duanchenghao@xxxxxxxxxx>
---
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