This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new d79beefa549 armv7-m/armv8-m: honor SP modifications on signal return
d79beefa549 is described below
commit d79beefa549b88bccc2cfe8375a511214682d5ae
Author: Andrew Au <[email protected]>
AuthorDate: Mon Jun 15 18:22:31 2026 +0000
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 <[email protected]>
---
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 <nuttx/config.h>
#include <stdint.h>
+#include <string.h>
#include <sched.h>
#include <assert.h>
@@ -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 <nuttx/config.h>
#include <stdint.h>
+#include <string.h>
#include <sched.h>
#include <assert.h>
@@ -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);