diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-25 17:33:22 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-25 17:33:22 -1000 |
| commit | b3593aba315322d875f295eef8b1a19e24658ba6 (patch) | |
| tree | 3294c7ad1453b0fe3570f936f6eb0dc834fe82e6 /kernel | |
| parent | 394023fbe34fccec5d4db2576169d21e14c8d3d4 (diff) | |
| parent | 833d2b79ed9520e77c5d83eb11644e5239b2c5bb (diff) | |
Merge tag 'kexec-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux
Pull kexec updates from Mike Rapoport:
- Fix probe error codes and error propagation:
- make architecture functions that probe loaded image consistenly
return -ENOEXEC when they do not recognize the image format
- make kexec_image_probe_default() distinguish image format
mismatches (-ENOEXEC) from real errors (e.g. -ENOMEM),
propagating real errors immediately and only continuing to the
next loader on -ENOEXEC
- Make sure the next kernel does not land on hardware-poisoned pages
- Remove unused arch_kexec_locate_mem_hole hook
* tag 'kexec-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/liveupdate/linux:
kexec_file: remove unused arch_kexec_locate_mem_hole hook
kexec: fix error propagation in kexec_image_probe_default()
kexec: return -ENOEXEC from image probe functions on mismatch
kexec: keep the next kernel off hardware-poisoned pages
kexec_file: stop the top-down search before it underflows
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/kexec_core.c | 10 | ||||
| -rw-r--r-- | kernel/kexec_elf.c | 4 | ||||
| -rw-r--r-- | kernel/kexec_file.c | 34 |
3 files changed, 39 insertions, 9 deletions
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c index dc770b9a6d05..7ee8c9f078f6 100644 --- a/kernel/kexec_core.c +++ b/kernel/kexec_core.c @@ -213,6 +213,16 @@ int sanity_check_segment_list(struct kimage *image) #endif /* + * Reject destinations that land on hardware-poisoned memory: the + * relocation copy would machine-check on the bad frame. + */ + for (i = 0; i < nr_segments; i++) { + if (range_first_hwpoison(image->segment[i].mem, + image->segment[i].memsz) != PHYS_ADDR_MAX) + return -EHWPOISON; + } + + /* * The destination addresses are searched from system RAM rather than * being allocated from the buddy allocator, so they are not guaranteed * to be accepted by the current kernel. Accept the destination diff --git a/kernel/kexec_elf.c b/kernel/kexec_elf.c index 3a5c25b2adc9..89a444a00693 100644 --- a/kernel/kexec_elf.c +++ b/kernel/kexec_elf.c @@ -172,7 +172,7 @@ static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr) default: pr_debug("Unknown ELF class.\n"); - return -EINVAL; + return -ENOEXEC; } return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC; @@ -236,7 +236,7 @@ static int elf_read_phdr(const char *buf, size_t len, default: pr_debug("Unknown ELF class.\n"); - return -EINVAL; + return -ENOEXEC; } return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC; diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 59fb9d71e9d8..a8455481f639 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -68,17 +68,19 @@ int kexec_image_probe_default(struct kimage *image, void *buf, unsigned long buf_len) { const struct kexec_file_ops * const *fops; - int ret = -ENOEXEC; for (fops = &kexec_file_loaders[0]; *fops && (*fops)->probe; ++fops) { - ret = (*fops)->probe(buf, buf_len); - if (!ret) { + int err = (*fops)->probe(buf, buf_len); + + if (!err) { image->fops = *fops; - return ret; + return 0; } + if (err != -ENOEXEC) + return err; } - return ret; + return -ENOEXEC; } static void *kexec_image_load_default(struct kimage *image) @@ -475,6 +477,7 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end, { struct kimage *image = kbuf->image; unsigned long temp_start, temp_end; + phys_addr_t poison; temp_end = min(end, kbuf->buf_max); temp_start = temp_end - kbuf->memsz + 1; @@ -484,7 +487,9 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end, /* align down start */ temp_start = ALIGN_DOWN(temp_start, kbuf->buf_align); - if (temp_start < start || temp_start < kbuf->buf_min) + /* A candidate above the range means the walk wrapped around */ + if (temp_start < start || temp_start < kbuf->buf_min || + temp_start > end) return 0; temp_end = temp_start + kbuf->memsz - 1; @@ -504,6 +509,13 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end, continue; } + poison = range_first_hwpoison(temp_start, kbuf->memsz); + if (poison != PHYS_ADDR_MAX) { + /* we hit a poisoned page */ + temp_start = poison - kbuf->memsz; + continue; + } + /* We found a suitable memory range */ break; } while (1); @@ -520,6 +532,7 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end, { struct kimage *image = kbuf->image; unsigned long temp_start, temp_end; + phys_addr_t poison; temp_start = max(start, kbuf->buf_min); @@ -546,6 +559,13 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end, continue; } + poison = range_last_hwpoison(temp_start, kbuf->memsz); + if (poison != PHYS_ADDR_MAX) { + /* we hit a poisoned page */ + temp_start = poison + PAGE_SIZE; + continue; + } + /* We found a suitable memory range */ break; } while (1); @@ -784,7 +804,7 @@ int kexec_add_buffer(struct kexec_buf *kbuf) kbuf->cma = NULL; /* Walk the RAM ranges and allocate a suitable range for the buffer */ - ret = arch_kexec_locate_mem_hole(kbuf); + ret = kexec_locate_mem_hole(kbuf); if (ret) return ret; |
