Skip to contentSkip to conversation

FAILED: patch "[PATCH] arm64: sdei: Preserve the interrupted context's exception registers" failed to apply to 6.1-stable tree

PATCH Applied Sasha Levin 2 participants 2 messages Nov 10

Subsystems (2)
lore
Share & tools
lore

Sasha Levin

author maintainer
#0 lore raw

The patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.

To reproduce the conflict and resubmit, you may use the following commands:

git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x 58c1eb84e97701c583605ba2fd553b69afba2245
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '[email protected]' --subject-prefix 'PATCH 6.1.y' 'HEAD^..'

Thanks,
Sasha

------------------ original commit in Linus's tree ------------------

From 58c1eb84e97701c583605ba2fd553b69afba2245 Mon Sep 17 00:00:00 2001
From: "Kiryl Shutsemau (Meta)" <[email protected]>
Date: Thu, 5 Nov 2026 12:08:34 +0000
Subject: [PATCH] arm64: sdei: Preserve the interrupted context's exception
registers

An SDEI event is dispatched by firmware regardless of PSTATE.DAIF, so it can interrupt the kernel where the exception registers are live:

- exception entry, before kernel_entry has saved ELR_EL1/SPSR_EL1 and before the C handler has read ESR_EL1/FAR_EL1;
- exception return, after kernel_exit has loaded ELR_EL1/SPSR_EL1 and before the ERET.

An IRQ or pseudo-NMI cannot arrive there with PSTATE.I set. Firmware saves only x0-x17, PC and PSTATE around an SDEI event, so a synchronous exception taken by the handler overwrites the interrupted context's ELR, SPSR, ESR and FAR. do_sdei_event() only notices that ELR changed, and warns.

The interrupted context then carries on with the handler's state. A task interrupted on its way into the kernel can be sent SIGILL for the handler's kernel data abort. A task interrupted on its way back to user space ERETs to EL1, at the handler's fixup address, with user registers loaded.

With SDEI delivering NMI backtraces, this happens in normal operation. The handler runs show_regs(), which reads through possibly-NULL pointers with the _nofault() accessors. print_worker_info() faults this way for a kworker that is not running a work item, and a single sysrq-l warned once per such kworker.

Save ELR, SPSR, ESR and FAR in __sdei_handler() before irqentry_nmi_enter(), which can take an exception too, and restore them after irqentry_nmi_exit().

Once they are restored, a nested exception is harmless if its handler is safe in NMI context. That holds for EL1 data aborts on translation, permission and access-flag faults, which never take the mmap lock in NMI context, and for BRK. Warn once, with the syndrome, about any other exception class.

Fixes f5df26961853 ("arm64: kernel: Add arch-specific SDEI entry code and CPU masking")
---
 arch/arm64/kernel/entry-common.c | 21 ++++++++++++-
 arch/arm64/kernel/sdei.c         | 53 ++++++++++++++++++++++++++------
 2 files changed, 64 insertions(+), 10 deletions(-)
arch/arm64/kernel/entry-common.c +20 -1
index 36b85b9e1575d..beb56891fc4fa 100644
@@ -1041,7 +1041,7 @@ asmlinkage void noinstr __noreturn handle_bad_stack(struct pt_regs *regs)
 asmlinkage noinstr unsigned long
 __sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
 {
-	unsigned long vbar;
+	unsigned long elr, spsr, esr, far, vbar;
 	irqentry_state_t state;
 	unsigned long ret;
 
@@ -1067,6 +1067,17 @@ __sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
 	else if (cpu_has_pan())
 		set_pstate_pan(0);
 
+	/*
+	 * Nothing saved the interrupted context's exception registers on the
+	 * way in, and it may be in the middle of exception entry or return
+	 * (SDEI ignores DAIF). Save them before anything can take an exception
+	 * and overwrite them.
+	 */
+	elr = read_sysreg(elr_el1);
+	spsr = read_sysreg(spsr_el1);
+	esr = read_sysreg(esr_el1);
+	far = read_sysreg(far_el1);
+
 	/*
 	 * With KPTI, VBAR_EL1 holds the trampoline vectors from the return to
 	 * EL0 until the next exception from EL0 maps the kernel. They only
@@ -1086,6 +1097,14 @@ __sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
 	ret = do_sdei_event(regs, arg, vbar);
 	irqentry_nmi_exit(regs, state);
 
+	/*
+	 * Put them back unconditionally. An unchanged ELR does not rule out a
+	 * nested exception: one may have returned to the same address.
+	 */
+	write_sysreg(elr, elr_el1);
+	write_sysreg(spsr, spsr_el1);
+	write_sysreg(esr, esr_el1);
+	write_sysreg(far, far_el1);
 	write_sysreg(vbar, vbar_el1);
 
 	return ret;
arch/arm64/kernel/sdei.c +44 -9

74 lines not on this page · raw message

Signature · 1 line
2.53.0
#1 lore raw

PATCH 2/2 arm64: sdei: Preserve the interrupted context's exception registers

An SDEI event is dispatched by firmware regardless of PSTATE.DAIF, so it can interrupt the kernel where the exception registers are live:

- exception entry, before kernel_entry has saved ELR_EL1/SPSR_EL1 and before the C handler has read ESR_EL1/FAR_EL1;
- exception return, after kernel_exit has loaded ELR_EL1/SPSR_EL1 and before the ERET.

An IRQ or pseudo-NMI cannot arrive there with PSTATE.I set. Firmware saves only x0-x17, PC and PSTATE around an SDEI event, so a synchronous exception taken by the handler overwrites the interrupted context's ELR, SPSR, ESR and FAR. do_sdei_event() only notices that ELR changed, and warns.

The interrupted context then carries on with the handler's state. A task interrupted on its way into the kernel can be sent SIGILL for the handler's kernel data abort. A task interrupted on its way back to user space ERETs to EL1, at the handler's fixup address, with user registers loaded.

With SDEI delivering NMI backtraces, this happens in normal operation. The handler runs show_regs(), which reads through possibly-NULL pointers with the _nofault() accessors. print_worker_info() faults this way for a kworker that is not running a work item, and a single sysrq-l warned once per such kworker.

Save ELR, SPSR, ESR and FAR in __sdei_handler() before irqentry_nmi_enter(), which can take an exception too, and restore them after irqentry_nmi_exit().

Once they are restored, a nested exception is harmless if its handler is safe in NMI context. That holds for EL1 data aborts on translation, permission and access-flag faults, which never take the mmap lock in NMI context, and for BRK. Warn once, with the syndrome, about any other exception class.

Fixes f5df26961853 ("arm64: kernel: Add arch-specific SDEI entry code and CPU masking")
(cherry picked from commit 58c1eb84e97701c583605ba2fd553b69afba2245)

[ kas: keep arm64_enter_nmi()/arm64_exit_nmi() in __sdei_handler();
open-code the fault type check, no esr_fsc_is_*() helpers here ]

---
 arch/arm64/kernel/entry-common.c | 21 +++++++++++-
 arch/arm64/kernel/sdei.c         | 55 ++++++++++++++++++++++++++------
 2 files changed, 66 insertions(+), 10 deletions(-)
arch/arm64/kernel/entry-common.c +20 -1
index 833ad10421c2..cc35632f9a4b 100644
@@ -855,7 +855,7 @@ asmlinkage void noinstr handle_bad_stack(struct pt_regs *regs)
 asmlinkage noinstr unsigned long
 __sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
 {
-	unsigned long vbar;
+	unsigned long elr, spsr, esr, far, vbar;
 	unsigned long ret;
 
 	/*
@@ -880,6 +880,17 @@ __sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
 	else if (cpu_has_pan())
 		set_pstate_pan(0);
 
+	/*
+	 * Nothing saved the interrupted context's exception registers on the
+	 * way in, and it may be in the middle of exception entry or return
+	 * (SDEI ignores DAIF). Save them before anything can take an exception
+	 * and overwrite them.
+	 */
+	elr = read_sysreg(elr_el1);
+	spsr = read_sysreg(spsr_el1);
+	esr = read_sysreg(esr_el1);
+	far = read_sysreg(far_el1);
+
 	/*
 	 * With KPTI, VBAR_EL1 holds the trampoline vectors from the return to
 	 * EL0 until the next exception from EL0 maps the kernel. They only
@@ -899,6 +910,14 @@ __sdei_handler(struct pt_regs *regs, struct sdei_registered_event *arg)
 	ret = do_sdei_event(regs, arg, vbar);
 	arm64_exit_nmi(regs);
 
+	/*
+	 * Put them back unconditionally. An unchanged ELR does not rule out a
+	 * nested exception: one may have returned to the same address.
+	 */
+	write_sysreg(elr, elr_el1);
+	write_sysreg(spsr, spsr_el1);
+	write_sysreg(esr, esr_el1);
+	write_sysreg(far, far_el1);
 	write_sysreg(vbar, vbar_el1);
 
 	return ret;
arch/arm64/kernel/sdei.c +46 -9

76 lines not on this page · raw message

Signature · 1 line
2.54.0