Skip to content

Commit

Permalink
riscv_fork.c: Fix vfork() for kernel mode + SMP
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pussuw authored and jlaitine committed Sep 30, 2024
1 parent f19371d commit 326f7ce
Showing 1 changed file with 24 additions and 10 deletions.
34 changes: 24 additions & 10 deletions arch/risc-v/src/common/riscv_fork.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,33 +133,47 @@ 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 */

/* Save FPU */
stacktop = (uintptr_t)child->cmn.xcp.ktopstk;
}
else
#endif
{
/* Set context to user stack */

riscv_savefpu(child->cmn.xcp.regs, riscv_fpuregs(&child->cmn));
stacktop = newsp;
}

/* Copy the parent stack contents */
/* Set the new register restore area to the new stack top */

memcpy((void *)newsp, (const void *)parent->xcp.regs[REG_SP], stackutil);
child->cmn.xcp.regs = (void *)(stacktop - XCPTCONTEXT_SIZE);

/* Set the new register restore area to the new stack top */
/* Copy the parent integer context (overwrites child's SP and TP) */

child->cmn.xcp.regs = (void *)(newsp - XCPTCONTEXT_SIZE);
memcpy(child->cmn.xcp.regs, parent->xcp.regs, XCPTCONTEXT_SIZE);

/* Save FPU */

riscv_savefpu(child->cmn.xcp.regs, riscv_fpuregs(&child->cmn));

/* Return 0 to child */

Expand Down

0 comments on commit 326f7ce

Please sign in to comment.