Commit 84ed3053 authored by Marc Gauthier's avatar Marc Gauthier Committed by Chris Zankel

xtensa: copy_thread with CLONE_VM must not copy live parent AR windows

When doing a fork (new VM), the new task has a mirror image of the
parent's stack, so keeps the same live register windows etc.
However when doing a clone with CLONE_VM, keeping the same VM
(eg. when creating a new thread), the child starts afresh on a new
stack -- it cannot share any part of the parent stack.  It
especially cannot have the same live AR windows as the parent,
otherwise it will overwrite the parent stack on overflow, likely
causing corruption.  (and so it did...)

Effectively, the register windows need to be spilled.
Turns out it's much easier to simply not copy parent register
windows when CLONE_VM is set.
Signed-off-by: default avatarMarc Gauthier <marc@tensilica.com>
Signed-off-by: default avatarMax Filippov <jcmvbkbc@gmail.com>
Signed-off-by: default avatarChris Zankel <chris@zankel.net>
parent eae8a416
...@@ -173,6 +173,16 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) ...@@ -173,6 +173,16 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
* *
* Note: This is a pristine frame, so we don't need any spill region on top of * Note: This is a pristine frame, so we don't need any spill region on top of
* childregs. * childregs.
*
* The fun part: if we're keeping the same VM (i.e. cloning a thread,
* not an entire process), we're normally given a new usp, and we CANNOT share
* any live address register windows. If we just copy those live frames over,
* the two threads (parent and child) will overflow the same frames onto the
* parent stack at different times, likely corrupting the parent stack (esp.
* if the parent returns from functions that called clone() and calls new
* ones, before the child overflows its now old copies of its parent windows).
* One solution is to spill windows to the parent stack, but that's fairly
* involved. Much simpler to just not copy those live frames across.
*/ */
int copy_thread(unsigned long clone_flags, unsigned long usp, int copy_thread(unsigned long clone_flags, unsigned long usp,
...@@ -191,13 +201,14 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -191,13 +201,14 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
else else
childregs = (struct pt_regs*)tos - 1; childregs = (struct pt_regs*)tos - 1;
/* This does not copy all the regs. In a bout of brilliance or madness,
ARs beyond a0-a15 exist past the end of the struct. */
*childregs = *regs; *childregs = *regs;
/* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */ /* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
*((int*)childregs - 3) = (unsigned long)childregs; *((int*)childregs - 3) = (unsigned long)childregs;
*((int*)childregs - 4) = 0; *((int*)childregs - 4) = 0;
childregs->areg[1] = tos;
childregs->areg[2] = 0; childregs->areg[2] = 0;
p->set_child_tid = p->clear_child_tid = NULL; p->set_child_tid = p->clear_child_tid = NULL;
p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1); p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1);
...@@ -205,10 +216,14 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -205,10 +216,14 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
if (user_mode(regs)) { if (user_mode(regs)) {
int len = childregs->wmask & ~0xf;
childregs->areg[1] = usp; childregs->areg[1] = usp;
if (clone_flags & CLONE_VM) {
childregs->wmask = 1; /* can't share live windows */
} else {
int len = childregs->wmask & ~0xf;
memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
&regs->areg[XCHAL_NUM_AREGS - len/4], len); &regs->areg[XCHAL_NUM_AREGS - len/4], len);
}
// FIXME: we need to set THREADPTR in thread_info... // FIXME: we need to set THREADPTR in thread_info...
if (clone_flags & CLONE_SETTLS) if (clone_flags & CLONE_SETTLS)
childregs->areg[2] = childregs->areg[6]; childregs->areg[2] = childregs->areg[6];
...@@ -216,6 +231,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, ...@@ -216,6 +231,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
} else { } else {
/* In kernel space, we start a new thread with a new stack. */ /* In kernel space, we start a new thread with a new stack. */
childregs->wmask = 1; childregs->wmask = 1;
childregs->areg[1] = tos;
} }
#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS) #if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment