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 <[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 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


Reply via email to