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

commit dcca9a4735be9527eae4d3e8545e8479601c5e05
Author: raiden00pl <[email protected]>
AuthorDate: Tue Sep 1 13:27:39 2026 +0200

    arch/intel64: don't touch the outgoing stack after releasing the csection
    
    up_switch_context() and up_exit() released the critical section and then
    kept using the outgoing task's stack: a call/ret through
    nxsched_switch_context() and the call into x86_64_fullcontextrestore().
    Once the lock is released the outgoing task can be woken and run by
    another CPU on that same stack, so those accesses race with it.
    
    Release the critical section as the last step and enter
    x86_64_fullcontextrestore() with a jmp so nothing is read from or
    written to the outgoing stack after the release.
    
    Assisted-by: Claude Code
    Signed-off-by: raiden00pl <[email protected]>
---
 arch/x86_64/src/common/x86_64_exit.c          | 18 +++++++-----------
 arch/x86_64/src/common/x86_64_switchcontext.c | 17 +++++++++++------
 2 files changed, 18 insertions(+), 17 deletions(-)

diff --git a/arch/x86_64/src/common/x86_64_exit.c 
b/arch/x86_64/src/common/x86_64_exit.c
index 70b210a83c7..6070cc82a60 100644
--- a/arch/x86_64/src/common/x86_64_exit.c
+++ b/arch/x86_64/src/common/x86_64_exit.c
@@ -87,23 +87,19 @@ void up_exit(int status)
 
   x86_64_restore_auxstate(tcb);
 
-  /* Restore the cpu lock */
-
-  restore_critical_section(tcb, this_cpu());
-
 #ifdef CONFIG_ARCH_KERNEL_STACK
   /* Update kernel stack top pointer */
 
   x86_64_set_ktopstk(tcb->xcp.ktopstk);
 #endif
 
-  /* Then switch contexts */
-
-  x86_64_fullcontextrestore(tcb->xcp.regs);
-
-  /* x86_64_fullcontextrestore() should not return but could if the software
-   * interrupts are disabled.
+  /* Restore the cpu lock.  This must come last and the final jump must
+   * not touch the stack (see up_switch_context()).
    */
 
-  PANIC();
+  restore_critical_section(tcb, this_cpu());
+
+  __asm__ volatile ("jmp x86_64_fullcontextrestore"
+                    :: "D" (tcb->xcp.regs) : "memory");
+  __builtin_unreachable();
 }
diff --git a/arch/x86_64/src/common/x86_64_switchcontext.c 
b/arch/x86_64/src/common/x86_64_switchcontext.c
index 9ddf056f37a..721d005f40d 100644
--- a/arch/x86_64/src/common/x86_64_switchcontext.c
+++ b/arch/x86_64/src/common/x86_64_switchcontext.c
@@ -86,6 +86,7 @@ void up_switch_context(struct tcb_s *tcb, struct tcb_s *rtcb)
   else if (!up_saveusercontext(rtcb->xcp.regs))
     {
       struct tcb_s **running_task;
+
       cpu = this_cpu();
 
       x86_64_restore_auxstate(tcb);
@@ -101,10 +102,6 @@ void up_switch_context(struct tcb_s *tcb, struct tcb_s 
*rtcb)
       tcb = this_task();
 #endif
 
-      /* Restore the cpu lock */
-
-      restore_critical_section(tcb, cpu);
-
       /* Update scheduler parameters */
 
       running_task = &g_running_tasks[cpu];
@@ -117,8 +114,16 @@ void up_switch_context(struct tcb_s *tcb, struct tcb_s 
*rtcb)
 
       *running_task = tcb;
 
-      /* Then switch contexts */
+      /* Restore the cpu lock.  This makes the outgoing task wakeable by
+       * other CPUs while this CPU still runs on the outgoing task's
+       * stack, so it must come last and the final jump must not touch
+       * the stack (a call would push the return address onto it).
+       */
+
+      restore_critical_section(tcb, cpu);
 
-      x86_64_fullcontextrestore(tcb->xcp.regs);
+      __asm__ volatile ("jmp x86_64_fullcontextrestore"
+                        :: "D" (tcb->xcp.regs) : "memory");
+      __builtin_unreachable();
     }
 }

Reply via email to