From d79beefa549b88bccc2cfe8375a511214682d5ae Mon Sep 17 00:00:00 2001 From: Andrew Au Date: Mon, 15 Jun 2026 18:22:31 +0000 Subject: [PATCH] armv7-m/armv8-m: honor SP modifications on signal return On ARMv7-M/ARMv8-M, the hardware exception return determines the final SP from the physical location of the HW exception frame, ignoring any software modification to REG_R13 in the saved register context. This means signal handlers that adjust SP have their change silently discarded. Fix this by relocating the saved context in arm_sigdeliver before calling arm_fullcontextrestore. If the desired SP (regs[REG_R13]) differs from the implied SP, memmove the entire context frame so that the hardware exception return produces the correct final SP. The fix runs only in the signal return path, adding zero overhead to the normal exception return hot path. Signed-off-by: Andrew Au --- arch/arm/src/armv7-m/arm_sigdeliver.c | 25 +++++++++++++++++++++++++ arch/arm/src/armv8-m/arm_sigdeliver.c | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/arch/arm/src/armv7-m/arm_sigdeliver.c b/arch/arm/src/armv7-m/arm_sigdeliver.c index ad01879ff9e..02b17525288 100644 --- a/arch/arm/src/armv7-m/arm_sigdeliver.c +++ b/arch/arm/src/armv7-m/arm_sigdeliver.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -57,6 +58,9 @@ void arm_sigdeliver(void) { struct tcb_s *rtcb = this_task(); uint32_t *regs = rtcb->xcp.saved_regs; + uint32_t *new_regs; + uint32_t desired_sp; + uint32_t implied_sp; #ifdef CONFIG_SMP /* In the SMP case, we must terminate the critical section while the signal @@ -163,6 +167,27 @@ retry: leave_critical_section(up_irq_save()); #endif + /* If the signal handler modified SP (REG_R13), relocate the saved + * context so that the hardware exception return produces the correct SP. + * + * On ARMv7-M, the exception return path sets PSP to the HW frame address + * and hardware computes final SP = PSP + frame_size. The implied SP is + * determined by the physical location of the context, not by REG_R13. + * To honor a modified SP, we memmove the entire context frame to the + * address where the end of the frame equals the desired SP. + */ + + desired_sp = regs[REG_R13]; + implied_sp = (uint32_t)regs + XCPTCONTEXT_SIZE; + + if (desired_sp != implied_sp) + { + new_regs = (uint32_t *)(desired_sp - XCPTCONTEXT_SIZE); + memmove(new_regs, regs, XCPTCONTEXT_SIZE); + regs = new_regs; + rtcb->xcp.saved_regs = new_regs; + } + rtcb->xcp.regs = rtcb->xcp.saved_regs; arm_fullcontextrestore(); UNUSED(regs); diff --git a/arch/arm/src/armv8-m/arm_sigdeliver.c b/arch/arm/src/armv8-m/arm_sigdeliver.c index 538350a77ab..39e1b0d3be9 100644 --- a/arch/arm/src/armv8-m/arm_sigdeliver.c +++ b/arch/arm/src/armv8-m/arm_sigdeliver.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -57,6 +58,9 @@ void arm_sigdeliver(void) { struct tcb_s *rtcb = this_task(); uint32_t *regs = rtcb->xcp.saved_regs; + uint32_t *new_regs; + uint32_t desired_sp; + uint32_t implied_sp; #ifdef CONFIG_SMP /* In the SMP case, we must terminate the critical section while the signal @@ -163,6 +167,27 @@ retry: leave_critical_section(up_irq_save()); #endif + /* If the signal handler modified SP (REG_R13), relocate the saved + * context so that the hardware exception return produces the correct SP. + * + * On ARMv8-M, the exception return path sets PSP to the HW frame address + * and hardware computes final SP = PSP + frame_size. The implied SP is + * determined by the physical location of the context, not by REG_R13. + * To honor a modified SP, we memmove the entire context frame to the + * address where the end of the frame equals the desired SP. + */ + + desired_sp = regs[REG_R13]; + implied_sp = (uint32_t)regs + XCPTCONTEXT_SIZE; + + if (desired_sp != implied_sp) + { + new_regs = (uint32_t *)(desired_sp - XCPTCONTEXT_SIZE); + memmove(new_regs, regs, XCPTCONTEXT_SIZE); + regs = new_regs; + rtcb->xcp.saved_regs = new_regs; + } + rtcb->xcp.regs = rtcb->xcp.saved_regs; arm_fullcontextrestore(); UNUSED(regs);