Re: [PATCH 1/2] arm64: sdei: Run the event handler on the kernel vectors

From: Usama Arif

Date: Wed Nov 04 2026 - 14:09:34 EST


On Wed, 4 Nov 2026 18:09:33 +0000 "Kiryl Shutsemau (Meta)" <kas@xxxxxxxxxx> wrote:

> With KPTI, VBAR_EL1 holds the trampoline vectors from the return to EL0
> until the next exception from EL0 maps the kernel and installs the
> kernel vectors. The trampoline vectors only handle exceptions from EL0;
> their current-EL half is zero-filled.
>
> An SDEI event is dispatched regardless of PSTATE.DAIF, so it lands there
> whenever the CPU is in user space. The SDEI entry trampoline maps the
> kernel but leaves VBAR_EL1 alone. An exception taken by the handler then
> executes the zeros, takes an undefined instruction exception to the same
> vector, and loops there forever. The event never completes and the CPU
> is lost; RCU stall reports are the first sign of it.
>
> RAS notifications land in user space whenever a task consumes the
> error, and NMI backtraces land wherever the CPU happens to be. The GHES
> handler takes no exception on its own, but any exception in the handler
> path does it: a WARN, a kprobe hit, or a fault fixed up by a _nofault()
> accessor.
>
> Install the kernel vectors in do_sdei_event() when VBAR_EL1 holds the
> trampoline vectors, and restore it before returning to firmware,
> including when the handler fails. The IRQ-like resume into user space
> keeps using the interrupted value, as it must go through the trampoline
> to map the kernel.
>
> Fixes: 79e9aa59dc29 ("arm64: sdei: Add trampoline code for remapping the kernel")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
> ---
> arch/arm64/kernel/sdei.c | 22 ++++++++++++++++++++--
> 1 file changed, 20 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm64/kernel/sdei.c b/arch/arm64/kernel/sdei.c
> index 778f2a1faac8..9fa2a3aeed48 100644
> --- a/arch/arm64/kernel/sdei.c
> +++ b/arch/arm64/kernel/sdei.c
> @@ -18,6 +18,7 @@
> #include <asm/sections.h>
> #include <asm/stacktrace.h>
> #include <asm/sysreg.h>
> +#include <asm/vectors.h>
> #include <asm/vmap_stack.h>
>
> unsigned long sdei_exit_mode;
> @@ -214,6 +215,20 @@ unsigned long __kprobes do_sdei_event(struct pt_regs *regs,
> if (arm64_kernel_unmapped_at_el0())
> clobbered_registers++;
>
> + /*
> + * With KPTI, VBAR_EL1 holds the trampoline vectors from the return to
> + * EL0 until the next exception from EL0 maps the kernel. They only
> + * handle exceptions from EL0: an exception taken by the handler would
> + * run their empty current-EL half and never come back. Run the handler
> + * on the kernel vectors. The IRQ-like resume below keeps using the
> + * interrupted VBAR, so it still goes through the trampoline.
> + */
> + if (arm64_kernel_unmapped_at_el0() &&
> + vbar == (unsigned long)this_cpu_read(this_cpu_vector)) {
> + write_sysreg((unsigned long)vectors, vbar_el1);
> + isb();
> + }
> +

Does this cover the whole path that can take an exception? The
swap is done inside do_sdei_event(), but __sdei_handler() calls
irqentry_nmi_enter() before it and irqentry_nmi_exit() after it,
and both of those still run with the trampoline vectors in
VBAR_EL1 when the event interrupted user space.

Both can take an exception. ct_nmi_enter() opens with
WARN_ON_ONCE(ct_nmi_nesting() < 0), and ct_nmi_exit() has
WARN_ON_ONCE(ct_nmi_nesting() <= 0) and
WARN_ON_ONCE(!rcu_is_watching_curr_cpu()). A WARN is a BRK, so
either would run the zero filled current-EL half and lose the CPU
in the way the commit message describes.

Would doing the swap in __sdei_handler(), around the whole NMI
entry and exit pair, close that window?


> /* Retrieve the missing registers values */
> for (i = 0; i < clobbered_registers; i++) {
> /* from within the handler, this call always succeeds */
> @@ -221,8 +236,6 @@ unsigned long __kprobes do_sdei_event(struct pt_regs *regs,
> }
>
> err = sdei_event_handler(regs, arg);
> - if (err)
> - return SDEI_EV_FAILED;
>
> if (elr != read_sysreg(elr_el1)) {
> /*
> @@ -233,6 +246,11 @@ unsigned long __kprobes do_sdei_event(struct pt_regs *regs,
> pr_warn("unsafe: exception during handler\n");
> }
>
> + write_sysreg(vbar, vbar_el1);
> +
> + if (err)
> + return SDEI_EV_FAILED;
> +
> mode = regs->pstate & (PSR_MODE32_BIT | PSR_MODE_MASK);
>
> /*
> --
> 2.54.0
>
>