mirror of
https://github.com/apache/nuttx.git
synced 2026-10-07 14:25:25 +00:00
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 <raiden00@railab.me>
This commit is contained in:
parent
a5bf9ad9c9
commit
dcca9a4735
2 changed files with 18 additions and 17 deletions
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
*/
|
||||
|
||||
x86_64_fullcontextrestore(tcb->xcp.regs);
|
||||
restore_critical_section(tcb, cpu);
|
||||
|
||||
__asm__ volatile ("jmp x86_64_fullcontextrestore"
|
||||
:: "D" (tcb->xcp.regs) : "memory");
|
||||
__builtin_unreachable();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue