This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch releases/13.0
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
The following commit(s) were added to refs/heads/releases/13.0 by this push:
new 29a125f64 testing/sig_sp_test: add test for SP context restore on
signal return
29a125f64 is described below
commit 29a125f64162affe4fa5724a160e6cbf18d2c8de
Author: Andrew Au <[email protected]>
AuthorDate: Sun Jun 21 17:26:28 2026 +0000
testing/sig_sp_test: add test for SP context restore on signal return
Add a test that verifies modifying SP (REG_R13) in the saved register
context is honored on signal return. The test pushes values onto the
stack, triggers SIGALRM, and in the handler advances SP to skip a
value, then verifies the correct value is popped after signal return.
This exercises the SP relocation fix in arm_sigdeliver.c for ARMv7-M
and ARMv8-M architectures.
Signed-off-by: Andrew Au <[email protected]>
---
testing/sig_sp_test/CMakeLists.txt | 20 ++++
testing/sig_sp_test/Kconfig | 31 +++++
testing/sig_sp_test/Make.defs | 10 ++
testing/sig_sp_test/Makefile | 17 +++
testing/sig_sp_test/sig_sp_test_main.c | 210 +++++++++++++++++++++++++++++++++
5 files changed, 288 insertions(+)
diff --git a/testing/sig_sp_test/CMakeLists.txt
b/testing/sig_sp_test/CMakeLists.txt
new file mode 100644
index 000000000..ed82f11be
--- /dev/null
+++ b/testing/sig_sp_test/CMakeLists.txt
@@ -0,0 +1,20 @@
+#
##############################################################################
+# apps/testing/sig_sp_test/CMakeLists.txt
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+#
##############################################################################
+
+if(CONFIG_TESTING_SIG_SP_TEST)
+ nuttx_add_application(
+ NAME
+ ${CONFIG_TESTING_SIG_SP_TEST_PROGNAME}
+ PRIORITY
+ ${CONFIG_TESTING_SIG_SP_TEST_PRIORITY}
+ STACKSIZE
+ ${CONFIG_TESTING_SIG_SP_TEST_STACKSIZE}
+ MODULE
+ ${CONFIG_TESTING_SIG_SP_TEST}
+ SRCS
+ sig_sp_test_main.c)
+endif()
diff --git a/testing/sig_sp_test/Kconfig b/testing/sig_sp_test/Kconfig
new file mode 100644
index 000000000..1c32a53f2
--- /dev/null
+++ b/testing/sig_sp_test/Kconfig
@@ -0,0 +1,31 @@
+#
+# For a description of the syntax of this configuration file,
+# see the file kconfig-language.txt in the NuttX tools repository.
+#
+
+config TESTING_SIG_SP_TEST
+ tristate "Signal SP restore test"
+ default n
+ depends on BUILD_FLAT && ARCH_ARM
+ ---help---
+ Test that modifying SP (REG_R13) in saved register context
+ during a signal handler is honored on exception return.
+
+ This verifies the SP context relocation in arm_sigdeliver.
+ Only available for flat builds on ARM.
+
+if TESTING_SIG_SP_TEST
+
+config TESTING_SIG_SP_TEST_PROGNAME
+ string "Program name"
+ default "sig_sp_test"
+
+config TESTING_SIG_SP_TEST_PRIORITY
+ int "Task priority"
+ default 100
+
+config TESTING_SIG_SP_TEST_STACKSIZE
+ int "Stack size"
+ default 4096
+
+endif
diff --git a/testing/sig_sp_test/Make.defs b/testing/sig_sp_test/Make.defs
new file mode 100644
index 000000000..57974a46c
--- /dev/null
+++ b/testing/sig_sp_test/Make.defs
@@ -0,0 +1,10 @@
+############################################################################
+# apps/testing/sig_sp_test/Make.defs
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+############################################################################
+
+ifneq ($(CONFIG_TESTING_SIG_SP_TEST),)
+CONFIGURED_APPS += $(APPDIR)/testing/sig_sp_test
+endif
diff --git a/testing/sig_sp_test/Makefile b/testing/sig_sp_test/Makefile
new file mode 100644
index 000000000..af73ec99e
--- /dev/null
+++ b/testing/sig_sp_test/Makefile
@@ -0,0 +1,17 @@
+############################################################################
+# apps/testing/sig_sp_test/Makefile
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+############################################################################
+
+include $(APPDIR)/Make.defs
+
+PROGNAME = $(CONFIG_TESTING_SIG_SP_TEST_PROGNAME)
+PRIORITY = $(CONFIG_TESTING_SIG_SP_TEST_PRIORITY)
+STACKSIZE = $(CONFIG_TESTING_SIG_SP_TEST_STACKSIZE)
+MODULE = $(CONFIG_TESTING_SIG_SP_TEST)
+
+MAINSRC = sig_sp_test_main.c
+
+include $(APPDIR)/Application.mk
diff --git a/testing/sig_sp_test/sig_sp_test_main.c
b/testing/sig_sp_test/sig_sp_test_main.c
new file mode 100644
index 000000000..e6df0a3bd
--- /dev/null
+++ b/testing/sig_sp_test/sig_sp_test_main.c
@@ -0,0 +1,210 @@
+/****************************************************************************
+ * apps/testing/sig_sp_test/sig_sp_test_main.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Test: Verify that modifying SP (REG_R13) in the saved register context
+ * is honored on context restore (exception return path).
+ *
+ * Scenario (simulates runtime unwinding past a trampoline):
+ * 1. Push values 1 and 2 onto the stack (simulating a trampoline push)
+ * 2. Busy-wait for a timer signal (SIGALRM)
+ * 3. In the handler, "emulate a pop" by advancing SP by 4
+ * (simulating a runtime deciding the top frame was a stub)
+ * 4. Redirect PC to resume_after_signal
+ * 5. After signal return, pop a value — it should be 1
+ *
+ * This exercises:
+ * - The SP relocation fix in arm_exception.S
+ * - The backward sliding copy (new SP > old SP, stack shrinks)
+ * - A realistic use case: unwinding past a trampoline frame
+ *
+ * Requirements:
+ * - Flat build only (accesses nxsched_self() for saved_regs)
+ * - ARM architecture (uses ARMv7-M register layout)
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <nuttx/arch.h>
+#include <nuttx/sched.h>
+#include <arch/irq.h>
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static volatile int g_result = -1;
+static volatile int g_ready = 0;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: verify_result
+ *
+ * Description:
+ * Called with the popped value in r0. Verifies it equals 1.
+ ****************************************************************************/
+
+void __attribute__((noinline, used)) verify_result(uint32_t value)
+{
+ printf("sig_sp_test: popped value = %lu (expected 1)\n",
+ (unsigned long)value);
+
+ if (value == 1)
+ {
+ printf("sig_sp_test: PASS\n");
+ g_result = 0;
+ }
+ else
+ {
+ printf("sig_sp_test: FAIL - expected 1, got %lu\n",
+ (unsigned long)value);
+ g_result = 1;
+ }
+
+ exit(g_result);
+}
+
+/****************************************************************************
+ * Name: resume_after_signal
+ *
+ * Description:
+ * We land here after the signal handler adjusts SP and PC.
+ * Stack now has [SP] = 1 (the '2' was skipped by SP += 4).
+ * Pop one value and verify it equals 1.
+ ****************************************************************************/
+
+static void __attribute__((naked, used)) resume_after_signal(void)
+{
+ __asm__ __volatile__(
+ "pop {r0}\n\t"
+ "b verify_result\n\t"
+ );
+}
+
+/****************************************************************************
+ * Name: sigalrm_handler
+ *
+ * Description:
+ * Called from the timer interrupt path (async signal delivery).
+ * Accesses saved_regs via nxsched_self() to modify the context
+ * that will be restored after signal return.
+ ****************************************************************************/
+
+static void sigalrm_handler(int signo, siginfo_t *info, void *ucontext)
+{
+ struct tcb_s *rtcb = nxsched_self();
+ uint32_t *regs;
+
+ (void)signo;
+ (void)info;
+ (void)ucontext;
+
+ regs = rtcb->xcp.saved_regs;
+ if (regs == NULL)
+ {
+ printf("sig_sp_test: ERROR - saved_regs is NULL\n");
+ exit(2);
+ }
+
+ printf("sig_sp_test: handler - PC=0x%08lx SP=0x%08lx\n",
+ (unsigned long)regs[REG_R15],
+ (unsigned long)regs[REG_R13]);
+
+ /* Only act when the main code is ready (in the asm loop) */
+
+ if (!g_ready)
+ {
+ alarm(1);
+ return;
+ }
+
+ /* Emulate a pop: advance SP by 4 (skip top-of-stack value '2') */
+
+ regs[REG_R13] += 4;
+
+ /* Redirect execution to resume_after_signal */
+
+ regs[REG_R15] = ((uint32_t)(uintptr_t)resume_after_signal) & ~1u;
+ regs[REG_XPSR] |= (1 << 24);
+
+ printf("sig_sp_test: handler - new SP=0x%08lx PC=0x%08lx\n",
+ (unsigned long)regs[REG_R13],
+ (unsigned long)regs[REG_R15]);
+}
+
+/****************************************************************************
+ * Name: wait_with_values_on_stack
+ *
+ * Description:
+ * Push 1 and 2 on the stack, set g_ready, then loop forever.
+ * The alarm handler will redirect us out of the loop.
+ * Stack after pushes: [SP] = 2, [SP+4] = 1
+ ****************************************************************************/
+
+static void __attribute__((naked, used)) wait_with_values_on_stack(void)
+{
+ __asm__ __volatile__(
+ "mov r0, #1\n\t"
+ "push {r0}\n\t"
+ "mov r0, #2\n\t"
+ "push {r0}\n\t"
+ "ldr r0, =g_ready\n\t"
+ "mov r1, #1\n\t"
+ "str r1, [r0]\n\t"
+ "1: nop\n\t"
+ "b 1b\n\t"
+ );
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * sig_sp_test_main
+ ****************************************************************************/
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+
+ (void)argc;
+ (void)argv;
+
+ printf("sig_sp_test: Signal SP restore test\n");
+ printf("sig_sp_test: push 1, push 2, alarm, handler SP+=4, pop => 1\n");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = sigalrm_handler;
+ sa.sa_flags = SA_SIGINFO;
+ sigemptyset(&sa.sa_mask);
+
+ if (sigaction(SIGALRM, &sa, NULL) < 0)
+ {
+ printf("sig_sp_test: ERROR sigaction failed\n");
+ return 1;
+ }
+
+ alarm(1);
+ wait_with_values_on_stack();
+
+ /* Should never reach here */
+
+ printf("sig_sp_test: ERROR - returned from wait\n");
+ return 1;
+}