From ca4bd482a0bd91787ea1c8b6b5b8123378c40557 Mon Sep 17 00:00:00 2001 From: Ville Juven Date: Tue, 10 Sep 2024 15:01:24 +0300 Subject: [PATCH] arm64/task/pthread_start: Fix rare issue with context register location There is a tiny possibility that when a process is started a trap is taken which causes a context switch. This moves the kernel stack unexpectedly and the task start logic no longer works. Fix this by recording the initial context location, and use that to trampoline into the user process with interrupts disabled. This ensures the context stays intact AND the kernel stack is fully unwound before the user process starts. --- arch/arm64/include/irq.h | 3 +++ arch/arm64/src/common/arm64_initialstate.c | 4 ++++ arch/arm64/src/common/arm64_pthread_start.c | 20 +++++++++++--------- arch/arm64/src/common/arm64_task_start.c | 20 +++++++++++--------- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/arch/arm64/include/irq.h b/arch/arm64/include/irq.h index 933ded64667..debac2522a0 100644 --- a/arch/arm64/include/irq.h +++ b/arch/arm64/include/irq.h @@ -280,6 +280,9 @@ struct xcptcontext /* task stack reg context */ uint64_t *regs; +#ifndef CONFIG_BUILD_FLAT + uint64_t *initregs; +#endif /* task context, for signal process */ diff --git a/arch/arm64/src/common/arm64_initialstate.c b/arch/arm64/src/common/arm64_initialstate.c index b61f01b3b26..6e10432a436 100644 --- a/arch/arm64/src/common/arm64_initialstate.c +++ b/arch/arm64/src/common/arm64_initialstate.c @@ -108,6 +108,10 @@ void arm64_new_task(struct tcb_s * tcb) pinitctx->tpidr_el1 = (uint64_t)tcb; tcb->xcp.regs = (uint64_t *)pinitctx; + +#ifndef CONFIG_BUILD_FLAT + tcb->xcp.initregs = tcb->xcp.regs; +#endif } /**************************************************************************** diff --git a/arch/arm64/src/common/arm64_pthread_start.c b/arch/arm64/src/common/arm64_pthread_start.c index f063c9b2848..db80db228b6 100644 --- a/arch/arm64/src/common/arm64_pthread_start.c +++ b/arch/arm64/src/common/arm64_pthread_start.c @@ -68,8 +68,11 @@ void up_pthread_start(pthread_trampoline_t startup, pthread_startroutine_t entrypt, pthread_addr_t arg) { - struct tcb_s *tcb = this_task(); - uint64_t spsr; + uint64_t *regs = this_task()->xcp.initregs; + + /* This must be performed atomically, the C-section ends upon user entry */ + + enter_critical_section(); /* Set up to enter the user-space pthread start-up function in * unprivileged mode. We need: @@ -80,11 +83,10 @@ void up_pthread_start(pthread_trampoline_t startup, * SPSR = user mode */ - tcb->xcp.regs[REG_ELR] = (uint64_t)startup; - tcb->xcp.regs[REG_X0] = (uint64_t)entrypt; - tcb->xcp.regs[REG_X1] = (uint64_t)arg; - spsr = tcb->xcp.regs[REG_SPSR] & ~SPSR_MODE_MASK; - tcb->xcp.regs[REG_SPSR] = spsr | SPSR_MODE_EL0T; + regs[REG_ELR] = (uint64_t)startup; + regs[REG_X0] = (uint64_t)entrypt; + regs[REG_X1] = (uint64_t)arg; + regs[REG_SPSR] = (regs[REG_SPSR] & ~SPSR_MODE_MASK) | SPSR_MODE_EL0T; /* Fully unwind the kernel stack and drop to user space */ @@ -94,8 +96,8 @@ void up_pthread_start(pthread_trampoline_t startup, "mov sp, x0\n" /* Stack pointer = context */ "b arm64_exit_exception\n" : - : "r" (tcb->xcp.regs) - : "memory" + : "r" (regs) + : "x0", "memory" ); PANIC(); diff --git a/arch/arm64/src/common/arm64_task_start.c b/arch/arm64/src/common/arm64_task_start.c index fd91e7b378e..5e3501eee6f 100644 --- a/arch/arm64/src/common/arm64_task_start.c +++ b/arch/arm64/src/common/arm64_task_start.c @@ -65,8 +65,11 @@ void up_task_start(main_t taskentry, int argc, char *argv[]) { - struct tcb_s *tcb = this_task(); - uint64_t spsr; + uint64_t *regs = this_task()->xcp.initregs; + + /* This must be performed atomically, the C-section ends upon user entry */ + + enter_critical_section(); /* Set up to return to the user-space _start function in * unprivileged mode. We need: @@ -77,11 +80,10 @@ void up_task_start(main_t taskentry, int argc, char *argv[]) * SPSR = user mode */ - tcb->xcp.regs[REG_ELR] = (uint64_t)taskentry; - tcb->xcp.regs[REG_X0] = (uint64_t)argc; - tcb->xcp.regs[REG_X1] = (uint64_t)argv; - spsr = tcb->xcp.regs[REG_SPSR] & ~SPSR_MODE_MASK; - tcb->xcp.regs[REG_SPSR] = spsr | SPSR_MODE_EL0T; + regs[REG_ELR] = (uint64_t)taskentry; + regs[REG_X0] = (uint64_t)argc; + regs[REG_X1] = (uint64_t)argv; + regs[REG_SPSR] = (regs[REG_SPSR] & ~SPSR_MODE_MASK) | SPSR_MODE_EL0T; /* Fully unwind the kernel stack and drop to user space */ @@ -91,8 +93,8 @@ void up_task_start(main_t taskentry, int argc, char *argv[]) "mov sp, x0\n" /* Stack pointer = context */ "b arm64_exit_exception\n" : - : "r" (tcb->xcp.regs) - : "memory" + : "r" (regs) + : "x0", "memory" ); PANIC();