diff options
author | David Daney <ddaney@caviumnetworks.com> | 2009-07-08 10:07:50 -0700 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2009-08-03 17:52:41 +0100 |
commit | 484889fc85a223ef56edc31828b86751b2296b7c (patch) | |
tree | f511d75b729c331260bc544a0f03b7d5bea26d9a /arch/mips/kernel/process.c | |
parent | 1de010a2702eb21d90883b83bf8c737d5e69d234 (diff) |
MIPS: Avoid clobbering struct pt_regs in kthreads
The resume() implementation octeon_switch.S examines the saved cp0_status
register. We were clobbering the entire pt_regs structure in kernel
threads leading to random crashes.
When switching away from a kernel thread, the saved cp0_status is examined
and if bit 30 is set it is cleared and the CP2 state saved into the pt_regs
structure. Since the kernel thread stack overlaid the pt_regs structure
this resulted in a corrupt stack. When the kthread with the corrupt stack
was resumed, it could crash if it used any of the data in the stack that was
clobbered.
We fix it by moving the kernel thread stack down so it doesn't overlay
pt_regs.
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/process.c')
-rw-r--r-- | arch/mips/kernel/process.c | 4 |
1 files changed, 3 insertions, 1 deletions
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index c09d681b718..f3d73e1831c 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -115,7 +115,7 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, { struct thread_info *ti = task_thread_info(p); struct pt_regs *childregs; - long childksp; + unsigned long childksp; p->set_child_tid = p->clear_child_tid = NULL; childksp = (unsigned long)task_stack_page(p) + THREAD_SIZE - 32; @@ -132,6 +132,8 @@ int copy_thread(unsigned long clone_flags, unsigned long usp, /* set up new TSS. */ childregs = (struct pt_regs *) childksp - 1; + /* Put the stack after the struct pt_regs. */ + childksp = (unsigned long) childregs; *childregs = *regs; childregs->regs[7] = 0; /* Clear error flag */ |