aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Gauthier <marc@tensilica.com>2012-10-14 19:55:35 -0400
committerChris Zankel <chris@zankel.net>2012-10-16 00:42:27 -0400
commit84ed30538b5d9a29a9612b93dd0a45d561624f82 (patch)
treeb1f0001a732c3518076b8d63cbfe0081f80dafb4
parenteae8a416afe140df4b054c448476654db0d46bde (diff)
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: Marc Gauthier <marc@tensilica.com> Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> Signed-off-by: Chris Zankel <chris@zankel.net>
-rw-r--r--arch/xtensa/kernel/process.c24
1 files changed, 20 insertions, 4 deletions
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index bc020825cce5..7901ee76b9be 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -173,6 +173,16 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
173 * 173 *
174 * Note: This is a pristine frame, so we don't need any spill region on top of 174 * Note: This is a pristine frame, so we don't need any spill region on top of
175 * childregs. 175 * childregs.
176 *
177 * The fun part: if we're keeping the same VM (i.e. cloning a thread,
178 * not an entire process), we're normally given a new usp, and we CANNOT share
179 * any live address register windows. If we just copy those live frames over,
180 * the two threads (parent and child) will overflow the same frames onto the
181 * parent stack at different times, likely corrupting the parent stack (esp.
182 * if the parent returns from functions that called clone() and calls new
183 * ones, before the child overflows its now old copies of its parent windows).
184 * One solution is to spill windows to the parent stack, but that's fairly
185 * involved. Much simpler to just not copy those live frames across.
176 */ 186 */
177 187
178int copy_thread(unsigned long clone_flags, unsigned long usp, 188int copy_thread(unsigned long clone_flags, unsigned long usp,
@@ -191,13 +201,14 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
191 else 201 else
192 childregs = (struct pt_regs*)tos - 1; 202 childregs = (struct pt_regs*)tos - 1;
193 203
204 /* This does not copy all the regs. In a bout of brilliance or madness,
205 ARs beyond a0-a15 exist past the end of the struct. */
194 *childregs = *regs; 206 *childregs = *regs;
195 207
196 /* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */ 208 /* Create a call4 dummy-frame: a0 = 0, a1 = childregs. */
197 *((int*)childregs - 3) = (unsigned long)childregs; 209 *((int*)childregs - 3) = (unsigned long)childregs;
198 *((int*)childregs - 4) = 0; 210 *((int*)childregs - 4) = 0;
199 211
200 childregs->areg[1] = tos;
201 childregs->areg[2] = 0; 212 childregs->areg[2] = 0;
202 p->set_child_tid = p->clear_child_tid = NULL; 213 p->set_child_tid = p->clear_child_tid = NULL;
203 p->thread.ra = MAKE_RA_FOR_CALL((unsigned long)ret_from_fork, 0x1); 214 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 216
206 if (user_mode(regs)) { 217 if (user_mode(regs)) {
207 218
208 int len = childregs->wmask & ~0xf;
209 childregs->areg[1] = usp; 219 childregs->areg[1] = usp;
210 memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4], 220 if (clone_flags & CLONE_VM) {
211 &regs->areg[XCHAL_NUM_AREGS - len/4], len); 221 childregs->wmask = 1; /* can't share live windows */
222 } else {
223 int len = childregs->wmask & ~0xf;
224 memcpy(&childregs->areg[XCHAL_NUM_AREGS - len/4],
225 &regs->areg[XCHAL_NUM_AREGS - len/4], len);
226 }
212// FIXME: we need to set THREADPTR in thread_info... 227// FIXME: we need to set THREADPTR in thread_info...
213 if (clone_flags & CLONE_SETTLS) 228 if (clone_flags & CLONE_SETTLS)
214 childregs->areg[2] = childregs->areg[6]; 229 childregs->areg[2] = childregs->areg[6];
@@ -216,6 +231,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp,
216 } else { 231 } else {
217 /* In kernel space, we start a new thread with a new stack. */ 232 /* In kernel space, we start a new thread with a new stack. */
218 childregs->wmask = 1; 233 childregs->wmask = 1;
234 childregs->areg[1] = tos;
219 } 235 }
220 236
221#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS) 237#if (XTENSA_HAVE_COPROCESSORS || XTENSA_HAVE_IO_PORTS)