mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:24:45 +01:00
kexec: derive purgatory entry from symbol
kexec_load_purgatory() derives image->start by locating e_entry inside an
SHF_EXECINSTR section. If the purgatory object contains multiple
executable sections with overlapping sh_addr, the entrypoint check can
match more than once and trigger a WARN.
Derive the entry section from the purgatory_start symbol when present and
compute image->start from its final placement. Keep the existing e_entry
fallback for purgatories that do not expose the symbol.
WARNING: kernel/kexec_file.c:1009 at kexec_load_purgatory+0x395/0x3c0, CPU#10: kexec/1784
Call Trace:
<TASK>
bzImage64_load+0x133/0xa00
__do_sys_kexec_file_load+0x2b3/0x5c0
do_syscall_64+0x81/0x610
entry_SYSCALL_64_after_hwframe+0x76/0x7e
[me@linux.beauty: move helper to avoid forward declaration, per Baoquan]
Link: https://lkml.kernel.org/r/20260128043511.316860-1-me@linux.beauty
Link: https://lkml.kernel.org/r/20260120124005.148381-1-me@linux.beauty
Fixes: 8652d44f46 ("kexec: support purgatories with .text.hot sections")
Signed-off-by: Li Chen <me@linux.beauty>
Acked-by: Baoquan He <bhe@redhat.com>
Cc: Alexander Graf <graf@amazon.com>
Cc: Eric Biggers <ebiggers@kernel.org>
Cc: Li Chen <me@linux.beauty>
Cc: Philipp Rudo <prudo@redhat.com>
Cc: Ricardo Ribalda Delgado <ribalda@chromium.org>
Cc: Ross Zwisler <zwisler@google.com>
Cc: Sourabh Jain <sourabhjain@linux.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
5138c936c2
commit
480e1d5c64
1 changed files with 74 additions and 57 deletions
|
|
@ -882,6 +882,60 @@ out_free_sha_regions:
|
|||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY
|
||||
/*
|
||||
* kexec_purgatory_find_symbol - find a symbol in the purgatory
|
||||
* @pi: Purgatory to search in.
|
||||
* @name: Name of the symbol.
|
||||
*
|
||||
* Return: pointer to symbol in read-only symtab on success, NULL on error.
|
||||
*/
|
||||
static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
|
||||
const char *name)
|
||||
{
|
||||
const Elf_Shdr *sechdrs;
|
||||
const Elf_Ehdr *ehdr;
|
||||
const Elf_Sym *syms;
|
||||
const char *strtab;
|
||||
int i, k;
|
||||
|
||||
if (!pi->ehdr)
|
||||
return NULL;
|
||||
|
||||
ehdr = pi->ehdr;
|
||||
sechdrs = (void *)ehdr + ehdr->e_shoff;
|
||||
|
||||
for (i = 0; i < ehdr->e_shnum; i++) {
|
||||
if (sechdrs[i].sh_type != SHT_SYMTAB)
|
||||
continue;
|
||||
|
||||
if (sechdrs[i].sh_link >= ehdr->e_shnum)
|
||||
/* Invalid strtab section number */
|
||||
continue;
|
||||
strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
|
||||
syms = (void *)ehdr + sechdrs[i].sh_offset;
|
||||
|
||||
/* Go through symbols for a match */
|
||||
for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) {
|
||||
if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL)
|
||||
continue;
|
||||
|
||||
if (strcmp(strtab + syms[k].st_name, name) != 0)
|
||||
continue;
|
||||
|
||||
if (syms[k].st_shndx == SHN_UNDEF ||
|
||||
syms[k].st_shndx >= ehdr->e_shnum) {
|
||||
pr_debug("Symbol: %s has bad section index %d.\n",
|
||||
name, syms[k].st_shndx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Found the symbol we are looking for */
|
||||
return &syms[k];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/*
|
||||
* kexec_purgatory_setup_kbuf - prepare buffer to load purgatory.
|
||||
* @pi: Purgatory to be loaded.
|
||||
|
|
@ -960,6 +1014,10 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
|||
unsigned long offset;
|
||||
size_t sechdrs_size;
|
||||
Elf_Shdr *sechdrs;
|
||||
const Elf_Sym *entry_sym;
|
||||
u16 entry_shndx = 0;
|
||||
unsigned long entry_off = 0;
|
||||
bool start_fixed = false;
|
||||
int i;
|
||||
|
||||
/*
|
||||
|
|
@ -977,6 +1035,12 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
|||
bss_addr = kbuf->mem + kbuf->bufsz;
|
||||
kbuf->image->start = pi->ehdr->e_entry;
|
||||
|
||||
entry_sym = kexec_purgatory_find_symbol(pi, "purgatory_start");
|
||||
if (entry_sym) {
|
||||
entry_shndx = entry_sym->st_shndx;
|
||||
entry_off = entry_sym->st_value;
|
||||
}
|
||||
|
||||
for (i = 0; i < pi->ehdr->e_shnum; i++) {
|
||||
unsigned long align;
|
||||
void *src, *dst;
|
||||
|
|
@ -994,6 +1058,13 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
|||
|
||||
offset = ALIGN(offset, align);
|
||||
|
||||
if (!start_fixed && entry_sym && i == entry_shndx &&
|
||||
(sechdrs[i].sh_flags & SHF_EXECINSTR) &&
|
||||
entry_off < sechdrs[i].sh_size) {
|
||||
kbuf->image->start = kbuf->mem + offset + entry_off;
|
||||
start_fixed = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the segment contains the entry point, if so,
|
||||
* calculate the value of image->start based on it.
|
||||
|
|
@ -1004,13 +1075,14 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
|
|||
* is not set to the initial value, and warn the user so they
|
||||
* have a chance to fix their purgatory's linker script.
|
||||
*/
|
||||
if (sechdrs[i].sh_flags & SHF_EXECINSTR &&
|
||||
if (!start_fixed && sechdrs[i].sh_flags & SHF_EXECINSTR &&
|
||||
pi->ehdr->e_entry >= sechdrs[i].sh_addr &&
|
||||
pi->ehdr->e_entry < (sechdrs[i].sh_addr
|
||||
+ sechdrs[i].sh_size) &&
|
||||
!WARN_ON(kbuf->image->start != pi->ehdr->e_entry)) {
|
||||
kbuf->image->start == pi->ehdr->e_entry) {
|
||||
kbuf->image->start -= sechdrs[i].sh_addr;
|
||||
kbuf->image->start += kbuf->mem + offset;
|
||||
start_fixed = true;
|
||||
}
|
||||
|
||||
src = (void *)pi->ehdr + sechdrs[i].sh_offset;
|
||||
|
|
@ -1128,61 +1200,6 @@ out_free_kbuf:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* kexec_purgatory_find_symbol - find a symbol in the purgatory
|
||||
* @pi: Purgatory to search in.
|
||||
* @name: Name of the symbol.
|
||||
*
|
||||
* Return: pointer to symbol in read-only symtab on success, NULL on error.
|
||||
*/
|
||||
static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
|
||||
const char *name)
|
||||
{
|
||||
const Elf_Shdr *sechdrs;
|
||||
const Elf_Ehdr *ehdr;
|
||||
const Elf_Sym *syms;
|
||||
const char *strtab;
|
||||
int i, k;
|
||||
|
||||
if (!pi->ehdr)
|
||||
return NULL;
|
||||
|
||||
ehdr = pi->ehdr;
|
||||
sechdrs = (void *)ehdr + ehdr->e_shoff;
|
||||
|
||||
for (i = 0; i < ehdr->e_shnum; i++) {
|
||||
if (sechdrs[i].sh_type != SHT_SYMTAB)
|
||||
continue;
|
||||
|
||||
if (sechdrs[i].sh_link >= ehdr->e_shnum)
|
||||
/* Invalid strtab section number */
|
||||
continue;
|
||||
strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
|
||||
syms = (void *)ehdr + sechdrs[i].sh_offset;
|
||||
|
||||
/* Go through symbols for a match */
|
||||
for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) {
|
||||
if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL)
|
||||
continue;
|
||||
|
||||
if (strcmp(strtab + syms[k].st_name, name) != 0)
|
||||
continue;
|
||||
|
||||
if (syms[k].st_shndx == SHN_UNDEF ||
|
||||
syms[k].st_shndx >= ehdr->e_shnum) {
|
||||
pr_debug("Symbol: %s has bad section index %d.\n",
|
||||
name, syms[k].st_shndx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Found the symbol we are looking for */
|
||||
return &syms[k];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
|
||||
{
|
||||
struct purgatory_info *pi = &image->purgatory_info;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue