Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
62b7c59
riscv: kexec_file: Fix crashk_low_res not exclude bug
ruanjinjie-eng Jun 1, 2026
6fd1985
powerpc/crash: Fix possible memory leak in update_crash_elfcorehdr()
ruanjinjie-eng Jun 1, 2026
36930f6
powerpc/kexec_file: Fix NULL pointer dereference in kexec_extra_fdt_s…
ruanjinjie-eng Jun 1, 2026
0d5cdf3
powerpc/kexec_file: Fix memory range truncation in __merge_memory_ran…
ruanjinjie-eng Jun 1, 2026
b3714a3
powerpc/crash: sort crash memory ranges before preparing elfcorehdr
sourabhjains Jun 1, 2026
8d19495
kexec: Extract kexec_free_segment_cma() from kimage_free_cma()
ruanjinjie-eng Jun 1, 2026
58c6241
arm64: kexec_file: Fix CMA page leaks during segment placement retry …
ruanjinjie-eng Jun 1, 2026
1173243
arm64: kexec_file: Fix image->elf_headers memory leak during retry loop
ruanjinjie-eng Jun 1, 2026
8bab945
kexec: Fix UAF and Double Free in crash_load_dm_crypt_keys()
ruanjinjie-eng Jun 1, 2026
00305c5
crash_core: Introduce CRASH_HOTPLUG_SAFETY_PADDING for memory hotplug…
ruanjinjie-eng Jun 1, 2026
42ea56d
x86: kexec_file: Fix TOCTOU buffer overflow via memory region padding
ruanjinjie-eng Jun 1, 2026
756f0e1
arm64: kexec_file: Fix TOCTOU buffer overflow via memory region padding
ruanjinjie-eng Jun 1, 2026
b71a7cb
riscv: kexec_file: Fix TOCTOU buffer overflow via memory region padding
ruanjinjie-eng Jun 1, 2026
ae2f832
LoongArch: kexec_file: Fix TOCTOU buffer overflow via memory region p…
ruanjinjie-eng Jun 1, 2026
c18c99f
crash: Add crash_prepare_headers() to exclude crash kernel memory
ruanjinjie-eng Jun 1, 2026
977dfc2
arm64: kexec_file: Use crash_prepare_headers() helper to simplify code
ruanjinjie-eng Jun 1, 2026
a8fd74e
x86: kexec_file: Use crash_prepare_headers() helper to simplify code
ruanjinjie-eng Jun 1, 2026
0f55b20
riscv: kexec_file: Use crash_prepare_headers() helper to simplify code
ruanjinjie-eng Jun 1, 2026
19623b4
LoongArch: kexec_file: Use crash_prepare_headers() helper to simplify…
ruanjinjie-eng Jun 1, 2026
c23eb90
powerpc/kexec_file: Use crash_exclude_core_ranges() helper
ruanjinjie-eng Jun 1, 2026
a681b2a
arm64: kexec_file: Add support for crashkernel CMA reservation
ruanjinjie-eng Jun 1, 2026
c3993fe
riscv: kexec_file: Add support for crashkernel CMA reservation
ruanjinjie-eng Jun 1, 2026
38114ac
arm64: crash: Add crash hotplug support
ruanjinjie-eng Jun 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Documentation/admin-guide/kernel-parameters.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1119,14 +1119,14 @@ Kernel parameters
It will be ignored when crashkernel=X,high is not used
or memory reserved is below 4G.
crashkernel=size[KMG],cma
[KNL, X86, ppc] Reserve additional crash kernel memory from
CMA. This reservation is usable by the first system's
userspace memory and kernel movable allocations (memory
balloon, zswap). Pages allocated from this memory range
will not be included in the vmcore so this should not
be used if dumping of userspace memory is intended and
it has to be expected that some movable kernel pages
may be missing from the dump.
[KNL, X86, ARM64, RISCV, PPC] Reserve additional crash
kernel memory from CMA. This reservation is usable by
the first system's userspace memory and kernel movable
allocations (memory balloon, zswap). Pages allocated
from this memory range will not be included in the vmcore
so this should not be used if dumping of userspace memory
is intended and it has to be expected that some movable
kernel pages may be missing from the dump.

A standard crashkernel reservation, as described above,
is still needed to hold the crash kernel and initrd.
Expand Down
3 changes: 3 additions & 0 deletions arch/arm64/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,9 @@ config ARCH_DEFAULT_CRASH_DUMP
config ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION
def_bool CRASH_RESERVE

config ARCH_SUPPORTS_CRASH_HOTPLUG
def_bool y

config TRANS_TABLE
def_bool y
depends on HIBERNATION || KEXEC_CORE
Expand Down
13 changes: 13 additions & 0 deletions arch/arm64/include/asm/kexec.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ extern int load_other_segments(struct kimage *image,
char *cmdline);
#endif

#ifdef CONFIG_CRASH_HOTPLUG
#define pnum_hdr_sz(pnum) ((pnum) * sizeof(Elf64_Phdr) + sizeof(Elf64_Ehdr))

void arch_crash_handle_hotplug_event(struct kimage *image, void *arg);
#define arch_crash_handle_hotplug_event arch_crash_handle_hotplug_event

int arch_crash_hotplug_support(struct kimage *image, unsigned long kexec_flags);
#define arch_crash_hotplug_support arch_crash_hotplug_support

unsigned int arch_crash_get_elfcorehdr_size(void);
#define crash_get_elfcorehdr_size arch_crash_get_elfcorehdr_size
#endif

#endif /* __ASSEMBLER__ */

#endif
2 changes: 1 addition & 1 deletion arch/arm64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o \
obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o
obj-$(CONFIG_ARM64_RELOC_TEST) += arm64-reloc-test.o
arm64-reloc-test-y := reloc_test_core.o reloc_test_syms.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o crash.o
obj-$(CONFIG_VMCORE_INFO) += vmcore_info.o
obj-$(CONFIG_ARM_SDE_INTERFACE) += sdei.o
obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o
Expand Down
152 changes: 152 additions & 0 deletions arch/arm64/kernel/crash.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Architecture specific functions for kexec based crash dumps.
*/

#define pr_fmt(fmt) "crash hp: " fmt

#include <linux/kexec.h>
#include <linux/elf.h>
#include <linux/memblock.h>
#include <linux/vmalloc.h>
#include <linux/cacheflush.h>
#include <linux/crash_core.h>

#include <asm/kexec.h>

#if defined(CONFIG_KEXEC_FILE) || defined(CONFIG_CRASH_HOTPLUG)
unsigned int arch_get_system_nr_ranges(void)
{
/* for exclusion of crashkernel region */
unsigned int nr_ranges = 2 + crashk_cma_cnt + CRASH_HOTPLUG_SAFETY_PADDING;
phys_addr_t start, end;
u64 i;

for_each_mem_range(i, &start, &end)
nr_ranges++;

return nr_ranges;
}

int arch_crash_populate_cmem(struct crash_mem *cmem)
{
phys_addr_t start, end;
u64 i;

for_each_mem_range(i, &start, &end) {
if (unlikely(cmem->nr_ranges >= cmem->max_nr_ranges))
return -EAGAIN;

cmem->ranges[cmem->nr_ranges].start = start;
cmem->ranges[cmem->nr_ranges].end = end - 1;
cmem->nr_ranges++;
}

return 0;
}
#endif

#ifdef CONFIG_CRASH_HOTPLUG
int arch_crash_hotplug_support(struct kimage *image, unsigned long kexec_flags)
{
#ifdef CONFIG_KEXEC_FILE
if (image->file_mode)
return 1;
#endif
/*
* For kexec_load syscall, crash hotplug support requires
* KEXEC_CRASH_HOTPLUG_SUPPORT flag to be passed by userspace.
*/
return kexec_flags & KEXEC_CRASH_HOTPLUG_SUPPORT;
}

unsigned int arch_crash_get_elfcorehdr_size(void)
{
unsigned int phdr_cnt;

/* A program header for possible CPUs, vmcoreinfo and kernel_map */
phdr_cnt = 2 + num_possible_cpus();
if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
phdr_cnt += CONFIG_CRASH_MAX_MEMORY_RANGES;

return pnum_hdr_sz(phdr_cnt);
}

/**
* update_crash_elfcorehdr() - Recreate the elfcorehdr and replace it with old
* elfcorehdr in the kexec segment array.
* @image: the active struct kimage
*/
static void update_crash_elfcorehdr(struct kimage *image)
{
void *elfbuf = NULL, *old_elfcorehdr;
unsigned long mem, memsz;
unsigned long elfsz = 0;

/*
* Create the new elfcorehdr reflecting the changes to CPU and/or
* memory resources.
*/
if (crash_prepare_headers(true, &elfbuf, &elfsz, NULL)) {
pr_err("unable to create new elfcorehdr");
goto out;
}

/*
* Obtain address and size of the elfcorehdr segment, and
* check it against the new elfcorehdr buffer.
*/
mem = image->segment[image->elfcorehdr_index].mem;
memsz = image->segment[image->elfcorehdr_index].memsz;
if (elfsz > memsz) {
pr_err("update elfcorehdr elfsz %lu > memsz %lu",
elfsz, memsz);
goto out;
}

/*
* Copy new elfcorehdr over the old elfcorehdr at destination.
*/
old_elfcorehdr = (void *)__va(mem);
if (!old_elfcorehdr) {
pr_err("mapping elfcorehdr segment failed\n");
goto out;
}

/*
* Temporarily invalidate the crash image while the
* elfcorehdr is updated.
*/
xchg(&kexec_crash_image, NULL);
memcpy((void *)old_elfcorehdr, elfbuf, elfsz);
dcache_clean_inval_poc((unsigned long)old_elfcorehdr,
(unsigned long)old_elfcorehdr + elfsz);
xchg(&kexec_crash_image, image);
pr_debug("updated elfcorehdr\n");

out:
vfree(elfbuf);
}

/**
* arch_crash_handle_hotplug_event() - Handle hotplug elfcorehdr changes
* @image: a pointer to kexec_crash_image
* @arg: struct memory_notify handler for memory hotplug case and
* NULL for CPU hotplug case.
*
* Update the kdump image based on the type of hotplug event:
* - CPU add and remove: No action is needed.
* - Memory add/remove: Update the elfcorehdr to reflect the current memory layout.
*
* Prepare the new elfcorehdr and replace the existing elfcorehdr.
*/
void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
{
if ((image->file_mode || image->elfcorehdr_updated) &&
((image->hp_action == KEXEC_CRASH_HP_ADD_CPU) ||
(image->hp_action == KEXEC_CRASH_HP_REMOVE_CPU)))
return;

update_crash_elfcorehdr(image);
}
#endif /* CONFIG_CRASH_HOTPLUG */
34 changes: 34 additions & 0 deletions arch/arm64/kernel/kexec_image.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#define pr_fmt(fmt) "kexec_file(Image): " fmt

#include <linux/elf.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/kernel.h>
Expand Down Expand Up @@ -89,6 +90,38 @@ static void *image_load(struct kimage *image,

kernel_segment_number = image->nr_segments;

#ifdef CONFIG_CRASH_DUMP
if (image->type == KEXEC_TYPE_CRASH) {
/* load elf core header */
unsigned long headers_sz, pnum = 0;
void *headers;

ret = crash_prepare_headers(true, &headers, &headers_sz, &pnum);
if (ret) {
pr_err("Preparing elf core header failed\n");
return ERR_PTR(ret);
}
image->elf_headers = headers;
image->elf_headers_sz = headers_sz;

#ifdef CONFIG_CRASH_HOTPLUG
/*
* The elfcorehdr segment size accounts for VMCOREINFO, kernel_map
* maximum CPUs and maximum memory ranges.
*/
if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
pnum = 2 + num_possible_cpus() + CONFIG_CRASH_MAX_MEMORY_RANGES;
else
pnum += 2 + num_possible_cpus();

if (pnum < (unsigned long)PN_XNUM)
image->elf_headers_sz = max(pnum_hdr_sz(pnum), headers_sz);
else
pr_err("number of Phdrs %lu exceeds max\n", pnum);
#endif
}
#endif

/*
* The location of the kernel segment may make it impossible to satisfy
* the other segment requirements, so we try repeatedly to find a
Expand All @@ -107,6 +140,7 @@ static void *image_load(struct kimage *image,
* We couldn't find space for the other segments; erase the
* kernel segment and try the next available hole.
*/
kexec_free_segment_cma(image, kernel_segment_number);
image->nr_segments -= 1;
kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
Expand Down
78 changes: 15 additions & 63 deletions arch/arm64/kernel/machine_kexec_file.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

#define pr_fmt(fmt) "kexec_file: " fmt

#include <linux/elf.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/kexec.h>
#include <linux/libfdt.h>
#include <linux/memblock.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/slab.h>
Expand All @@ -39,50 +39,6 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
return kexec_image_post_load_cleanup_default(image);
}

#ifdef CONFIG_CRASH_DUMP
static int prepare_elf_headers(void **addr, unsigned long *sz)
{
struct crash_mem *cmem;
unsigned int nr_ranges;
int ret;
u64 i;
phys_addr_t start, end;

nr_ranges = 2; /* for exclusion of crashkernel region */
for_each_mem_range(i, &start, &end)
nr_ranges++;

cmem = kmalloc_flex(*cmem, ranges, nr_ranges);
if (!cmem)
return -ENOMEM;

cmem->max_nr_ranges = nr_ranges;
cmem->nr_ranges = 0;
for_each_mem_range(i, &start, &end) {
cmem->ranges[cmem->nr_ranges].start = start;
cmem->ranges[cmem->nr_ranges].end = end - 1;
cmem->nr_ranges++;
}

/* Exclude crashkernel region */
ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
if (ret)
goto out;

if (crashk_low_res.end) {
ret = crash_exclude_mem_range(cmem, crashk_low_res.start, crashk_low_res.end);
if (ret)
goto out;
}

ret = crash_prepare_elf64_headers(cmem, true, addr, sz);

out:
kfree(cmem);
return ret;
}
#endif

/*
* Tries to add the initrd and DTB to the image. If it is not possible to find
* valid locations, this function will undo changes to the image and return non
Expand All @@ -105,32 +61,25 @@ int load_other_segments(struct kimage *image,
kbuf.buf_min = kernel_load_addr + kernel_size;

#ifdef CONFIG_CRASH_DUMP
/* load elf core header */
void *headers;
unsigned long headers_sz;
if (image->type == KEXEC_TYPE_CRASH) {
ret = prepare_elf_headers(&headers, &headers_sz);
if (ret) {
pr_err("Preparing elf core header failed\n");
goto out_err;
}

kbuf.buffer = headers;
kbuf.bufsz = headers_sz;
kbuf.buffer = image->elf_headers;
kbuf.bufsz = image->elf_headers_sz;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
kbuf.memsz = headers_sz;
kbuf.memsz = image->elf_headers_sz;

#ifdef CONFIG_CRASH_HOTPLUG
if (image->elf_headers_sz < pnum_hdr_sz(PN_XNUM))
image->elfcorehdr_index = image->nr_segments;
#endif

kbuf.buf_align = SZ_64K; /* largest supported page size */
kbuf.buf_max = ULONG_MAX;
kbuf.top_down = true;

ret = kexec_add_buffer(&kbuf);
if (ret) {
vfree(headers);
if (ret)
goto out_err;
}
image->elf_headers = headers;
image->elf_load_addr = kbuf.mem;
image->elf_headers_sz = headers_sz;

kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
Expand Down Expand Up @@ -195,7 +144,10 @@ int load_other_segments(struct kimage *image,
return 0;

out_err:
image->nr_segments = orig_segments;
while (image->nr_segments > orig_segments) {
kexec_free_segment_cma(image, image->nr_segments - 1);
image->nr_segments--;
}
kvfree(dtb);
return ret;
}
Loading
Loading