public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] kexec: derive purgatory entry from symbol
@ 2026-01-28  4:35 Li Chen
  2026-01-28  9:26 ` Baoquan He
  0 siblings, 1 reply; 3+ messages in thread
From: Li Chen @ 2026-01-28  4:35 UTC (permalink / raw)
  To: Andrew Morton, Baoquan He, Mimi Zohar, Tushar Sugandhi,
	Eric Biggers, Alexander Graf, Steven Chen, Coiby Xu, Li Chen,
	Sourabh Jain, Philipp Rudo, Ricardo Ribalda,
	Steven Rostedt (Google), Ross Zwisler, kexec, linux-kernel

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

Fixes: 8652d44f466a ("kexec: support purgatories with .text.hot sections")
Signed-off-by: Li Chen <me@linux.beauty>
---
clangelog:
v2: move the helper body above kexec_purgatory_setup_sechdrs() in v2 to avoid
the forward declaration, as suggested by Baoquan.

 kernel/kexec_file.c | 131 +++++++++++++++++++++++++-------------------
 1 file changed, 74 insertions(+), 57 deletions(-)

diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 3f1d6c4e8ff2..e9550bfbd02e 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -880,6 +880,60 @@ static int kexec_calculate_store_digests(struct kimage *image)
 }
 
 #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.
@@ -958,6 +1012,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;
 
 	/*
@@ -975,6 +1033,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;
@@ -992,6 +1056,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.
@@ -1002,13 +1073,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;
@@ -1126,61 +1198,6 @@ int kexec_load_purgatory(struct kimage *image, struct kexec_buf *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;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH v2] kexec: derive purgatory entry from symbol
  2026-01-28  4:35 [PATCH v2] kexec: derive purgatory entry from symbol Li Chen
@ 2026-01-28  9:26 ` Baoquan He
  2026-01-28 19:19   ` Andrew Morton
  0 siblings, 1 reply; 3+ messages in thread
From: Baoquan He @ 2026-01-28  9:26 UTC (permalink / raw)
  To: Li Chen
  Cc: Andrew Morton, Mimi Zohar, Tushar Sugandhi, Eric Biggers,
	Alexander Graf, Steven Chen, Coiby Xu, Sourabh Jain, Philipp Rudo,
	Ricardo Ribalda, Steven Rostedt (Google), Ross Zwisler, kexec,
	linux-kernel

On 01/28/26 at 12:35pm, Li Chen wrote:
> 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
> 
> Fixes: 8652d44f466a ("kexec: support purgatories with .text.hot sections")
> Signed-off-by: Li Chen <me@linux.beauty>

LGTM,

Acked-by: Baoquan He <bhe@redhat.com>

By the way, a warning need be 'Fixes' tag and back ported to stable
kernel?

Thanks
Baoquan

> ---
> clangelog:
> v2: move the helper body above kexec_purgatory_setup_sechdrs() in v2 to avoid
> the forward declaration, as suggested by Baoquan.
> 
>  kernel/kexec_file.c | 131 +++++++++++++++++++++++++-------------------
>  1 file changed, 74 insertions(+), 57 deletions(-)
> 
> diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
> index 3f1d6c4e8ff2..e9550bfbd02e 100644
> --- a/kernel/kexec_file.c
> +++ b/kernel/kexec_file.c
> @@ -880,6 +880,60 @@ static int kexec_calculate_store_digests(struct kimage *image)
>  }
>  
>  #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.
> @@ -958,6 +1012,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;
>  
>  	/*
> @@ -975,6 +1033,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;
> @@ -992,6 +1056,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.
> @@ -1002,13 +1073,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;
> @@ -1126,61 +1198,6 @@ int kexec_load_purgatory(struct kimage *image, struct kexec_buf *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;
> -- 
> 2.52.0
> 


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH v2] kexec: derive purgatory entry from symbol
  2026-01-28  9:26 ` Baoquan He
@ 2026-01-28 19:19   ` Andrew Morton
  0 siblings, 0 replies; 3+ messages in thread
From: Andrew Morton @ 2026-01-28 19:19 UTC (permalink / raw)
  To: Baoquan He
  Cc: Li Chen, Mimi Zohar, Tushar Sugandhi, Eric Biggers,
	Alexander Graf, Steven Chen, Coiby Xu, Sourabh Jain, Philipp Rudo,
	Ricardo Ribalda, Steven Rostedt (Google), Ross Zwisler, kexec,
	linux-kernel

On Wed, 28 Jan 2026 17:26:10 +0800 Baoquan He <bhe@redhat.com> wrote:

> On 01/28/26 at 12:35pm, Li Chen wrote:
> > 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
> > 
> > Fixes: 8652d44f466a ("kexec: support purgatories with .text.hot sections")
> > Signed-off-by: Li Chen <me@linux.beauty>
> 
> LGTM,
> 
> Acked-by: Baoquan He <bhe@redhat.com>

Thanks.

> By the way, a warning need be 'Fixes' tag and back ported to stable
> kernel?

Yup.  I added the cc:stable.

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-01-28 19:19 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-28  4:35 [PATCH v2] kexec: derive purgatory entry from symbol Li Chen
2026-01-28  9:26 ` Baoquan He
2026-01-28 19:19   ` Andrew Morton

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox