Hi Jinjie, > 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.
This is independed of other fixes and can be sent as a separate x86b fix. > Cc: Thomas Gleixner <[email protected]> > Cc: Ingo Molnar <[email protected]> > Cc: Borislav Petkov <[email protected]> > Cc: Dave Hansen <[email protected]> > Cc: "H. Peter Anvin" <[email protected]> > Cc: "Mike Rapoport (Microsoft)" <[email protected]> > Cc: Vishal Verma <[email protected]> > Cc: Baoquan He <[email protected]> > Cc: Chao Gao <[email protected]> > Cc: Sean Christopherson <[email protected]> > Cc: Eric DeVolder <[email protected]> > Cc: Hari Bathini <[email protected]> > Cc: Andrew Morton <[email protected]> > Cc: Sourabh Jain <[email protected]> > Cc: [email protected] > 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 <[email protected]> > --- > 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 e681ec9cf1dc8..3c9f4fbbe7ff0 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; > + } HIGMEM is only enabled on 32-bit and there elfsz cannot be "massive" because it is the size of the headers just built for the CPUs and RAM ranges actually present, not the large memsz reserved for hotplug. On x86_32 (NR_CPUS <= 64) that is a few KiB at most. Also, crashkernel is capped at CRASH_ADDR_HIGH_MAX = SZ_512M on X86_32, which is below the default lowmem limit. For lowmem pages, kmap_local_page() returns the contiguous direct-map address, so the old code was already fine. So the overflow needs the crash region to be in highmem, either through crashkernel=size@offset above lowmem or through a large vmalloc= setting, and an elfcorehdr larger than one page. It is still worth fixing, but please drop "massive" from the subject. The changelog should also mention the conditions when this could happen. -- Sincerely yours, Mike.
