[PATCH v5 09/17] x86/crash: Fix massive out-of-bounds write on 32-bit Highmem
From: Jinjie Ruan
Date: Fri Sep 18 2026 - 06:10:55 EST
On 32-bit x86 systems with HIGHMEM, kmap_local_page() only maps a single
4KB page. However, the elfcorehdr segment can span several pages (up to
hundreds of kilobytes).
The original code blindly copies 'elfsz' bytes at once via
memcpy_flushcache(), overwriting adjacent fixmap entries or critical
virtual addresses.
Fix this by copying the new elfcorehdr page by page.
Cc: Thomas Gleixner <tglx@xxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Borislav Petkov <bp@xxxxxxxxx>
Cc: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
Cc: "H. Peter Anvin" <hpa@xxxxxxxxx>
Cc: "Mike Rapoport (Microsoft)" <rppt@xxxxxxxxxx>
Cc: Vishal Verma <vishal.l.verma@xxxxxxxxx>
Cc: Baoquan He <baoquan.he@xxxxxxxxx>
Cc: Chao Gao <chao.gao@xxxxxxxxx>
Cc: Sean Christopherson <seanjc@xxxxxxxxxx>
Cc: Eric DeVolder <eric.devolder@xxxxxxxxxx>
Cc: Hari Bathini <hbathini@xxxxxxxxxxxxx>
Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc: Sourabh Jain <sourabhjain@xxxxxxxxxxxxx>
Cc: stable@xxxxxxxxxxxxxxx
Fixes: ea53ad9cf73b ("x86/crash: add x86 crash hotplug support")
Link: https://sashiko.dev/#/patchset/20260907125404.922123-1-ruanjinjie%40huawei.com
Signed-off-by: Jinjie Ruan <ruanjinjie@xxxxxxxxxx>
---
arch/x86/kernel/crash.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index e681ec9cf1dc..3c9f4fbbe7ff 100644
--- a/arch/x86/kernel/crash.c
+++ b/arch/x86/kernel/crash.c
@@ -447,9 +447,10 @@ unsigned int arch_crash_get_elfcorehdr_size(void)
*/
void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
{
- void *elfbuf = NULL, *old_elfcorehdr;
unsigned long mem, memsz;
unsigned long elfsz = 0;
+ void *elfbuf = NULL;
+ unsigned long done;
/*
* As crash_prepare_elf64_headers() has already described all
@@ -484,21 +485,20 @@ void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
/*
* Copy new elfcorehdr over the old elfcorehdr at destination.
- */
- old_elfcorehdr = kmap_local_page(pfn_to_page(mem >> PAGE_SHIFT));
- if (!old_elfcorehdr) {
- pr_err("mapping elfcorehdr segment failed\n");
- goto out;
- }
-
- /*
- * Temporarily invalidate the crash image while the
- * elfcorehdr is updated.
+ * The segment is physically contiguous but can span several pages.
+ * On 32-bit Highmem architectures, kmap_local_page() maps only a
+ * single page at a time, so copy page by page.
*/
xchg(&kexec_crash_image, NULL);
- memcpy_flushcache(old_elfcorehdr, elfbuf, elfsz);
+ for (done = 0; done < elfsz; ) {
+ size_t chunk = min_t(size_t, PAGE_SIZE, elfsz - done);
+ void *dst = kmap_local_page(pfn_to_page((mem + done) >> PAGE_SHIFT));
+
+ memcpy_flushcache(dst, elfbuf + done, chunk);
+ kunmap_local(dst);
+ done += chunk;
+ }
xchg(&kexec_crash_image, image);
- kunmap_local(old_elfcorehdr);
pr_debug("updated elfcorehdr\n");
out:
--
2.34.1