Skip to content
/ linux Public

Commit 36eb314

Browse files
FirstLoveLifeSasha Levin
authored andcommitted
kexec: derive purgatory entry from symbol
[ Upstream commit 480e1d5 ] 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: 8652d44 ("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>
1 parent 3bdc376 commit 36eb314

File tree

1 file changed

+74
-57
lines changed

1 file changed

+74
-57
lines changed

kernel/kexec_file.c

Lines changed: 74 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,60 @@ static int kexec_calculate_store_digests(struct kimage *image)
882882
}
883883

884884
#ifdef CONFIG_ARCH_SUPPORTS_KEXEC_PURGATORY
885+
/*
886+
* kexec_purgatory_find_symbol - find a symbol in the purgatory
887+
* @pi: Purgatory to search in.
888+
* @name: Name of the symbol.
889+
*
890+
* Return: pointer to symbol in read-only symtab on success, NULL on error.
891+
*/
892+
static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
893+
const char *name)
894+
{
895+
const Elf_Shdr *sechdrs;
896+
const Elf_Ehdr *ehdr;
897+
const Elf_Sym *syms;
898+
const char *strtab;
899+
int i, k;
900+
901+
if (!pi->ehdr)
902+
return NULL;
903+
904+
ehdr = pi->ehdr;
905+
sechdrs = (void *)ehdr + ehdr->e_shoff;
906+
907+
for (i = 0; i < ehdr->e_shnum; i++) {
908+
if (sechdrs[i].sh_type != SHT_SYMTAB)
909+
continue;
910+
911+
if (sechdrs[i].sh_link >= ehdr->e_shnum)
912+
/* Invalid strtab section number */
913+
continue;
914+
strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
915+
syms = (void *)ehdr + sechdrs[i].sh_offset;
916+
917+
/* Go through symbols for a match */
918+
for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) {
919+
if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL)
920+
continue;
921+
922+
if (strcmp(strtab + syms[k].st_name, name) != 0)
923+
continue;
924+
925+
if (syms[k].st_shndx == SHN_UNDEF ||
926+
syms[k].st_shndx >= ehdr->e_shnum) {
927+
pr_debug("Symbol: %s has bad section index %d.\n",
928+
name, syms[k].st_shndx);
929+
return NULL;
930+
}
931+
932+
/* Found the symbol we are looking for */
933+
return &syms[k];
934+
}
935+
}
936+
937+
return NULL;
938+
}
885939
/*
886940
* kexec_purgatory_setup_kbuf - prepare buffer to load purgatory.
887941
* @pi: Purgatory to be loaded.
@@ -960,6 +1014,10 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
9601014
unsigned long offset;
9611015
size_t sechdrs_size;
9621016
Elf_Shdr *sechdrs;
1017+
const Elf_Sym *entry_sym;
1018+
u16 entry_shndx = 0;
1019+
unsigned long entry_off = 0;
1020+
bool start_fixed = false;
9631021
int i;
9641022

9651023
/*
@@ -977,6 +1035,12 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
9771035
bss_addr = kbuf->mem + kbuf->bufsz;
9781036
kbuf->image->start = pi->ehdr->e_entry;
9791037

1038+
entry_sym = kexec_purgatory_find_symbol(pi, "purgatory_start");
1039+
if (entry_sym) {
1040+
entry_shndx = entry_sym->st_shndx;
1041+
entry_off = entry_sym->st_value;
1042+
}
1043+
9801044
for (i = 0; i < pi->ehdr->e_shnum; i++) {
9811045
unsigned long align;
9821046
void *src, *dst;
@@ -994,6 +1058,13 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
9941058

9951059
offset = ALIGN(offset, align);
9961060

1061+
if (!start_fixed && entry_sym && i == entry_shndx &&
1062+
(sechdrs[i].sh_flags & SHF_EXECINSTR) &&
1063+
entry_off < sechdrs[i].sh_size) {
1064+
kbuf->image->start = kbuf->mem + offset + entry_off;
1065+
start_fixed = true;
1066+
}
1067+
9971068
/*
9981069
* Check if the segment contains the entry point, if so,
9991070
* calculate the value of image->start based on it.
@@ -1004,13 +1075,14 @@ static int kexec_purgatory_setup_sechdrs(struct purgatory_info *pi,
10041075
* is not set to the initial value, and warn the user so they
10051076
* have a chance to fix their purgatory's linker script.
10061077
*/
1007-
if (sechdrs[i].sh_flags & SHF_EXECINSTR &&
1078+
if (!start_fixed && sechdrs[i].sh_flags & SHF_EXECINSTR &&
10081079
pi->ehdr->e_entry >= sechdrs[i].sh_addr &&
10091080
pi->ehdr->e_entry < (sechdrs[i].sh_addr
10101081
+ sechdrs[i].sh_size) &&
1011-
!WARN_ON(kbuf->image->start != pi->ehdr->e_entry)) {
1082+
kbuf->image->start == pi->ehdr->e_entry) {
10121083
kbuf->image->start -= sechdrs[i].sh_addr;
10131084
kbuf->image->start += kbuf->mem + offset;
1085+
start_fixed = true;
10141086
}
10151087

10161088
src = (void *)pi->ehdr + sechdrs[i].sh_offset;
@@ -1128,61 +1200,6 @@ int kexec_load_purgatory(struct kimage *image, struct kexec_buf *kbuf)
11281200
return ret;
11291201
}
11301202

1131-
/*
1132-
* kexec_purgatory_find_symbol - find a symbol in the purgatory
1133-
* @pi: Purgatory to search in.
1134-
* @name: Name of the symbol.
1135-
*
1136-
* Return: pointer to symbol in read-only symtab on success, NULL on error.
1137-
*/
1138-
static const Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi,
1139-
const char *name)
1140-
{
1141-
const Elf_Shdr *sechdrs;
1142-
const Elf_Ehdr *ehdr;
1143-
const Elf_Sym *syms;
1144-
const char *strtab;
1145-
int i, k;
1146-
1147-
if (!pi->ehdr)
1148-
return NULL;
1149-
1150-
ehdr = pi->ehdr;
1151-
sechdrs = (void *)ehdr + ehdr->e_shoff;
1152-
1153-
for (i = 0; i < ehdr->e_shnum; i++) {
1154-
if (sechdrs[i].sh_type != SHT_SYMTAB)
1155-
continue;
1156-
1157-
if (sechdrs[i].sh_link >= ehdr->e_shnum)
1158-
/* Invalid strtab section number */
1159-
continue;
1160-
strtab = (void *)ehdr + sechdrs[sechdrs[i].sh_link].sh_offset;
1161-
syms = (void *)ehdr + sechdrs[i].sh_offset;
1162-
1163-
/* Go through symbols for a match */
1164-
for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) {
1165-
if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL)
1166-
continue;
1167-
1168-
if (strcmp(strtab + syms[k].st_name, name) != 0)
1169-
continue;
1170-
1171-
if (syms[k].st_shndx == SHN_UNDEF ||
1172-
syms[k].st_shndx >= ehdr->e_shnum) {
1173-
pr_debug("Symbol: %s has bad section index %d.\n",
1174-
name, syms[k].st_shndx);
1175-
return NULL;
1176-
}
1177-
1178-
/* Found the symbol we are looking for */
1179-
return &syms[k];
1180-
}
1181-
}
1182-
1183-
return NULL;
1184-
}
1185-
11861203
void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name)
11871204
{
11881205
struct purgatory_info *pi = &image->purgatory_info;

0 commit comments

Comments
 (0)