riscv_fork.c: Fix vfork() for kernel mode + SMP

There was an error in the fork() routine when system calls are in use:
the child context is saved on the child's user stack, which is incorrect,
the context must be saved on the kernel stack instead.

The result is a full system crash if (when) the child executes on a
different CPU which does not have the same MMU mappings active.
This commit is contained in:
Ville Juven 2024-09-26 11:57:09 +03:00 committed by Xiang Xiao
parent 887c6c8716
commit 172d2a8491
1 changed files with 26 additions and 12 deletions

View File

@ -133,34 +133,48 @@ pid_t riscv_fork(const struct fork_s *context)
DEBUGASSERT(stacktop > parent->xcp.regs[REG_SP]);
stackutil = stacktop - parent->xcp.regs[REG_SP];
/* Copy the parent stack contents (overwrites child's SP and TP) */
/* Copy goes to child's user stack top */
newtop = (uintptr_t)child->cmn.stack_base_ptr + child->cmn.adj_stack_size;
newsp = newtop - stackutil;
memcpy((void *)newsp, (const void *)parent->xcp.regs[REG_SP], stackutil);
#ifdef CONFIG_SCHED_THREAD_LOCAL
/* Save child's thread pointer */
tp = child->cmn.xcp.regs[REG_TP];
#endif
/* Set up frame for context and copy the parent's user context there */
/* Determine the integer context save area */
memcpy((void *)(newsp - XCPTCONTEXT_SIZE),
parent->xcp.regs, XCPTCONTEXT_SIZE);
#ifdef CONFIG_ARCH_KERNEL_STACK
if (child->cmn.xcp.kstack)
{
/* Set context to kernel stack */
stacktop = (uintptr_t)child->cmn.xcp.ktopstk;
}
else
#endif
{
/* Set context to user stack */
stacktop = newsp;
}
/* Set the new register restore area to the new stack top */
child->cmn.xcp.regs = (void *)(stacktop - XCPTCONTEXT_SIZE);
/* Copy the parent integer context (overwrites child's SP and TP) */
memcpy(child->cmn.xcp.regs, parent->xcp.regs, XCPTCONTEXT_SIZE);
/* Save FPU */
riscv_savefpu(child->cmn.xcp.regs, riscv_fpuregs(&child->cmn));
/* Copy the parent stack contents */
memcpy((void *)newsp, (const void *)parent->xcp.regs[REG_SP], stackutil);
/* Set the new register restore area to the new stack top */
child->cmn.xcp.regs = (void *)(newsp - XCPTCONTEXT_SIZE);
/* Return 0 to child */
child->cmn.xcp.regs[REG_A0] = 0;