From 5f5991a0f256c497332d8e93473a4e1f3da97a8d Mon Sep 17 00:00:00 2001 From: Royyan Zahir Date: Thu, 8 Oct 2026 19:41:36 +0400 Subject: [PATCH] arch/arm: redirect an M-profile thread to its signal action once A signal action queued at syscall return pends PendSV for the running thread, but the same SVC can switch to another thread first. PendSV then saw that thread, the redirect was lost and the handler did not run until a later PendSV landed on the target, which could happen while it was already in arm_sigdeliver: the second redirect overwrote saved_regs, the inner delivery cleared sigdeliver and the outer one branched to address 0. ostest sighand and signest hard faulted in a protected build. The redirect is now checked against the thread being switched in, and a thread already redirected is left alone until arm_sigdeliver restores its context. Signed-off-by: Royyan Zahir --- arch/arm/src/armv6-m/arm_doirq.c | 16 +++++++--------- arch/arm/src/armv6-m/arm_schedulesigaction.c | 5 +++++ arch/arm/src/armv6-m/arm_sigdeliver.c | 1 + arch/arm/src/armv7-m/arm_doirq.c | 16 +++++++--------- arch/arm/src/armv7-m/arm_schedulesigaction.c | 5 +++++ arch/arm/src/armv7-m/arm_sigdeliver.c | 1 + arch/arm/src/armv8-m/arm_doirq.c | 16 +++++++--------- arch/arm/src/armv8-m/arm_schedulesigaction.c | 5 +++++ arch/arm/src/armv8-m/arm_sigdeliver.c | 1 + 9 files changed, 39 insertions(+), 27 deletions(-) diff --git a/arch/arm/src/armv6-m/arm_doirq.c b/arch/arm/src/armv6-m/arm_doirq.c index 4ee54a70bcf..b1a7e39e880 100644 --- a/arch/arm/src/armv6-m/arm_doirq.c +++ b/arch/arm/src/armv6-m/arm_doirq.c @@ -85,15 +85,6 @@ uint32_t *arm_doirq(int irq, uint32_t *regs) /* Dispatch the PendSV interrupt */ irq_dispatch(irq, regs); -#endif -#ifdef CONFIG_ENABLE_ALL_SIGNALS - if (tcb->sigdeliver) - { - /* Pendsv able to access running tcb with no critical section */ - - up_schedule_sigaction(tcb); - } - #endif up_irq_save(); } @@ -104,6 +95,13 @@ uint32_t *arm_doirq(int irq, uint32_t *regs) tcb = this_task(); +#ifdef CONFIG_ENABLE_ALL_SIGNALS + if (tcb->sigdeliver) + { + up_schedule_sigaction(tcb); + } +#endif + /* Update scheduler parameters. * The arm-m architecture svc call will trigger an interrupt, * and the actual context switch is executed after doirq is completed, diff --git a/arch/arm/src/armv6-m/arm_schedulesigaction.c b/arch/arm/src/armv6-m/arm_schedulesigaction.c index 0e51f0c5bcd..69cb9bd5c73 100644 --- a/arch/arm/src/armv6-m/arm_schedulesigaction.c +++ b/arch/arm/src/armv6-m/arm_schedulesigaction.c @@ -86,6 +86,11 @@ void up_schedule_sigaction(struct tcb_s *tcb) struct tcb_s *rtcb = running_task(); uint32_t ipsr = getipsr(); + if (tcb->xcp.saved_regs != NULL) + { + return; + } + /* First, handle some special cases when the signal is * being delivered to the currently executing task. */ diff --git a/arch/arm/src/armv6-m/arm_sigdeliver.c b/arch/arm/src/armv6-m/arm_sigdeliver.c index f61d216c6e7..ce43e27fff7 100644 --- a/arch/arm/src/armv6-m/arm_sigdeliver.c +++ b/arch/arm/src/armv6-m/arm_sigdeliver.c @@ -165,6 +165,7 @@ retry: g_running_tasks[this_cpu()] = NULL; rtcb->xcp.regs = rtcb->xcp.saved_regs; + rtcb->xcp.saved_regs = NULL; arm_fullcontextrestore(); UNUSED(regs); } diff --git a/arch/arm/src/armv7-m/arm_doirq.c b/arch/arm/src/armv7-m/arm_doirq.c index 9d22058ea0a..a70e909c167 100644 --- a/arch/arm/src/armv7-m/arm_doirq.c +++ b/arch/arm/src/armv7-m/arm_doirq.c @@ -85,15 +85,6 @@ uint32_t *arm_doirq(int irq, uint32_t *regs) /* Dispatch the PendSV interrupt */ irq_dispatch(irq, regs); -#endif -#ifdef CONFIG_ENABLE_ALL_SIGNALS - if (tcb->sigdeliver) - { - /* Pendsv able to access running tcb with no critical section */ - - up_schedule_sigaction(tcb); - } - #endif up_irq_save(); @@ -105,6 +96,13 @@ uint32_t *arm_doirq(int irq, uint32_t *regs) tcb = this_task(); +#ifdef CONFIG_ENABLE_ALL_SIGNALS + if (tcb->sigdeliver) + { + up_schedule_sigaction(tcb); + } +#endif + /* Update scheduler parameters. * The arm-m architecture svc call will trigger an interrupt, * and the actual context switch is executed after doirq is completed, diff --git a/arch/arm/src/armv7-m/arm_schedulesigaction.c b/arch/arm/src/armv7-m/arm_schedulesigaction.c index db570847049..75e8783ff25 100644 --- a/arch/arm/src/armv7-m/arm_schedulesigaction.c +++ b/arch/arm/src/armv7-m/arm_schedulesigaction.c @@ -87,6 +87,11 @@ void up_schedule_sigaction(struct tcb_s *tcb) struct tcb_s *rtcb = running_task(); uint32_t ipsr = getipsr(); + if (tcb->xcp.saved_regs != NULL) + { + return; + } + /* First, handle some special cases when the signal is * being delivered to the currently executing task. */ diff --git a/arch/arm/src/armv7-m/arm_sigdeliver.c b/arch/arm/src/armv7-m/arm_sigdeliver.c index 02b17525288..2ac5aeada24 100644 --- a/arch/arm/src/armv7-m/arm_sigdeliver.c +++ b/arch/arm/src/armv7-m/arm_sigdeliver.c @@ -189,6 +189,7 @@ retry: } rtcb->xcp.regs = rtcb->xcp.saved_regs; + rtcb->xcp.saved_regs = NULL; arm_fullcontextrestore(); UNUSED(regs); } diff --git a/arch/arm/src/armv8-m/arm_doirq.c b/arch/arm/src/armv8-m/arm_doirq.c index a327eeabd7a..e5ca07a4438 100644 --- a/arch/arm/src/armv8-m/arm_doirq.c +++ b/arch/arm/src/armv8-m/arm_doirq.c @@ -97,15 +97,6 @@ uint32_t *arm_doirq(int irq, uint32_t *regs) /* Dispatch the PendSV interrupt */ irq_dispatch(irq, regs); -#endif -#ifdef CONFIG_ENABLE_ALL_SIGNALS - if (tcb->sigdeliver) - { - /* Pendsv able to access running tcb with no critical section */ - - up_schedule_sigaction(tcb); - } - #endif up_irq_save(); @@ -117,6 +108,13 @@ uint32_t *arm_doirq(int irq, uint32_t *regs) tcb = this_task(); +#ifdef CONFIG_ENABLE_ALL_SIGNALS + if (tcb->sigdeliver) + { + up_schedule_sigaction(tcb); + } +#endif + /* Update scheduler parameters. * The arm-m architecture svc call will trigger an interrupt, * and the actual context switch is executed after doirq is completed, diff --git a/arch/arm/src/armv8-m/arm_schedulesigaction.c b/arch/arm/src/armv8-m/arm_schedulesigaction.c index a24d8fb8f12..8a166eff217 100644 --- a/arch/arm/src/armv8-m/arm_schedulesigaction.c +++ b/arch/arm/src/armv8-m/arm_schedulesigaction.c @@ -87,6 +87,11 @@ void up_schedule_sigaction(struct tcb_s *tcb) struct tcb_s *rtcb = running_task(); uint32_t ipsr = getipsr(); + if (tcb->xcp.saved_regs != NULL) + { + return; + } + /* First, handle some special cases when the signal is * being delivered to the currently executing task. */ diff --git a/arch/arm/src/armv8-m/arm_sigdeliver.c b/arch/arm/src/armv8-m/arm_sigdeliver.c index 39e1b0d3be9..5904b4e028e 100644 --- a/arch/arm/src/armv8-m/arm_sigdeliver.c +++ b/arch/arm/src/armv8-m/arm_sigdeliver.c @@ -189,6 +189,7 @@ retry: } rtcb->xcp.regs = rtcb->xcp.saved_regs; + rtcb->xcp.saved_regs = NULL; arm_fullcontextrestore(); UNUSED(regs); }