Sashiko AI code review pointed out a there is a TOCTOU (Time-of-Check to Time-of-Use) race condition in prepare_elf_headers() between the initial pass that counts System RAM ranges and the second pass that populates them. If a memory hotplug event occurs between these two steps, the number of memory regions may increase, causing an out-of-bounds write to the cmem->ranges[] array.
Directly introducing get_online_mems() inside prepare_elf_headers() would trigger an immediate recursive read-after-write deadlock when invoked by the runtime hotplug notification path (which already holds the hotplug write lock). To eliminate the TOCTOU window safely without deadlock risks, move the get_online_mems() read lock to the top-level architecture image loaders. Since these top-level loaders are strictly executed on the initial system call path and are never re-entered by the runtime hotplug notifier, this approach physically isolates the locking contexts. The system memory ranges are forced to be statically frozen during the entire layout generation, eradicating the buffer overflow vulnerability. Cc: Thomas Gleixner <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Baoquan He <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: [email protected] Fixes: 8d5f894a3108 ("x86: kexec_file: lift CRASH_MAX_RANGES limit on crash_mem buffer") Signed-off-by: Jinjie Ruan <[email protected]> --- arch/x86/kernel/crash.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c index cd796818d94d..f319308b06ee 100644 --- a/arch/x86/kernel/crash.c +++ b/arch/x86/kernel/crash.c @@ -25,6 +25,7 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/memblock.h> +#include <linux/memory_hotplug.h> #include <asm/bootparam.h> #include <asm/processor.h> @@ -226,6 +227,9 @@ static int prepare_elf64_ram_headers_callback(struct resource *res, void *arg) { struct crash_mem *cmem = arg; + if (WARN_ON_ONCE(cmem->nr_ranges >= cmem->max_nr_ranges)) + return -EAGAIN; + cmem->ranges[cmem->nr_ranges].start = res->start; cmem->ranges[cmem->nr_ranges].end = res->end; cmem->nr_ranges++; @@ -419,10 +423,14 @@ int crash_load_segments(struct kimage *image) .buf_max = ULONG_MAX, .top_down = false }; /* Prepare elf headers and add a segment */ + get_online_mems(); ret = prepare_elf_headers(&kbuf.buffer, &kbuf.bufsz, &pnum); - if (ret) + if (ret) { + put_online_mems(); return ret; + } + put_online_mems(); image->elf_headers = kbuf.buffer; image->elf_headers_sz = kbuf.bufsz; kbuf.memsz = kbuf.bufsz; -- 2.34.1
