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 <[email protected]> --- 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

