[PATCH v1 3/8] kho: validate preservation metadata in kho_restore_vmalloc()
Pasha Tatashin
pasha.tatashin at soleen.com
Sun Oct 18 19:11:06 PDT 2026
kho_restore_vmalloc() currently dereferences preservation before
checking for NULL, does not reject unknown KHO vmalloc flags in
kho_flags_to_vmalloc(), and does not validate preservation->first.phys,
preservation->total_pages, preservation->order, or potential size
overflow before allocating the pages array and shifting by order.
Introduce kho_vmalloc_validate() in kexec_handover_internal.h to
validate the struct kho_vmalloc header and convert its flags before
restoring the vmalloc area.
Signed-off-by: Pasha Tatashin <pasha.tatashin at soleen.com>
---
kernel/liveupdate/kexec_handover.c | 17 ++++++------
kernel/liveupdate/kexec_handover_internal.h | 30 +++++++++++++++++++++
2 files changed, 38 insertions(+), 9 deletions(-)
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index f427cdd9a969..a9c52d998855 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -1384,28 +1384,27 @@ EXPORT_SYMBOL_GPL(kho_unpreserve_vmalloc);
*/
void *kho_restore_vmalloc(const struct kho_vmalloc *preservation)
{
- struct kho_vmalloc_chunk *chunk = KHOSER_LOAD_PTR(preservation->first);
kasan_vmalloc_flags_t kasan_flags = KASAN_VMALLOC_PROT_NORMAL;
- unsigned int align, order, shift, vm_flags;
- unsigned long total_pages, contig_pages;
+ unsigned long total_pages, contig_pages, align, idx = 0;
+ unsigned int order, shift, vm_flags;
+ struct kho_vmalloc_chunk *chunk;
unsigned long addr, size;
struct vm_struct *area;
struct page **pages;
- unsigned int idx = 0;
int err;
- vm_flags = kho_flags_to_vmalloc(preservation->flags);
- if (vm_flags & ~KHO_VMALLOC_SUPPORTED_FLAGS)
+ if (!kho_vmalloc_validate(preservation, &vm_flags))
return NULL;
+ chunk = KHOSER_LOAD_PTR(preservation->first);
total_pages = preservation->total_pages;
pages = kvmalloc_objs(*pages, total_pages);
if (!pages)
return NULL;
order = preservation->order;
- contig_pages = (1 << order);
+ contig_pages = 1UL << order;
shift = PAGE_SHIFT + order;
- align = 1 << shift;
+ align = 1UL << shift;
while (chunk) {
struct page *page;
@@ -1420,7 +1419,7 @@ void *kho_restore_vmalloc(const struct kho_vmalloc *preservation)
if (!page)
goto err_free_pages_array;
- for (int j = 0; j < contig_pages; j++)
+ for (unsigned long j = 0; j < contig_pages; j++)
pages[idx++] = page + j;
phys += contig_pages * PAGE_SIZE;
diff --git a/kernel/liveupdate/kexec_handover_internal.h b/kernel/liveupdate/kexec_handover_internal.h
index 094b4917a0f1..878e21bd8b00 100644
--- a/kernel/liveupdate/kexec_handover_internal.h
+++ b/kernel/liveupdate/kexec_handover_internal.h
@@ -7,6 +7,7 @@
#include <linux/libfdt.h>
#include <linux/list.h>
#include <linux/mm.h>
+#include <linux/overflow.h>
#include <linux/printk.h>
#include <linux/types.h>
#include <linux/unaligned.h>
@@ -23,6 +24,9 @@ static inline unsigned int kho_flags_to_vmalloc(unsigned short kho_flags)
{
unsigned int vm_flags = 0;
+ if (kho_flags & ~(KHO_VMALLOC_ALLOC | KHO_VMALLOC_HUGE_VMAP))
+ return ~KHO_VMALLOC_SUPPORTED_FLAGS;
+
if (kho_flags & KHO_VMALLOC_ALLOC)
vm_flags |= VM_ALLOC;
if (kho_flags & KHO_VMALLOC_HUGE_VMAP)
@@ -31,6 +35,32 @@ static inline unsigned int kho_flags_to_vmalloc(unsigned short kho_flags)
return vm_flags;
}
+static inline bool kho_vmalloc_validate(const struct kho_vmalloc *preservation,
+ unsigned int *vm_flags)
+{
+ size_t alloc_size;
+
+ if (!preservation || !preservation->first.phys ||
+ !PAGE_ALIGNED(preservation->first.phys) ||
+ !preservation->total_pages)
+ return false;
+
+ if (preservation->order >= BITS_PER_LONG - PAGE_SHIFT ||
+ (1UL << preservation->order) > preservation->total_pages ||
+ !IS_ALIGNED(preservation->total_pages, 1UL << preservation->order))
+ return false;
+
+ if (check_mul_overflow((size_t)preservation->total_pages, PAGE_SIZE,
+ &alloc_size))
+ return false;
+
+ *vm_flags = kho_flags_to_vmalloc(preservation->flags);
+ if (*vm_flags & ~KHO_VMALLOC_SUPPORTED_FLAGS)
+ return false;
+
+ return true;
+}
+
/**
* kho_encode_radix_key - Encodes a physical address and order into a radix key.
* @phys: The physical address of the page.
--
2.56.0.176.g3575e65682-goog
More information about the kexec
mailing list