mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
kexec: derive purgatory entry from symbol
[ Upstream commit480e1d5c64] 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> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
+74
-57
@@ -797,6 +797,60 @@ out:
|
||||
}
|
||||
|
||||
#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.
|
||||
@@ -875,6 +929,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;
|
||||
|
||||
/*
|
||||
@@ -892,6 +950,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;
|
||||
@@ -909,6 +973,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.
|
||||
@@ -919,13 +990,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;
|
||||
@@ -1043,61 +1115,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;
|
||||
|
||||
Reference in New Issue
Block a user