aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh
diff options
context:
space:
mode:
authorBobby Bingham <koorogi@koorogi.info>2014-04-03 17:46:39 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 19:20:52 -0400
commitabafe5d9b04648a2f699202e9ae2d15ffe44c3a3 (patch)
tree7fffd52fa35230fe65a5e4925b3c734635dfbf0a /arch/sh
parentd0df04f7cc2b31ae1eb3b70f9333b9f91a963f9b (diff)
sh: push extra copy of r0-r2 for syscall parameters
When invoking syscall handlers on sh32, the saved userspace registers are at the top of the stack. This seems to have been intentional, as it is an easy way to pass r0, r1, ... to the handler as parameters 5, 6, ... It causes problems, however, because the compiler is allowed to generate code for a function which clobbers that function's own parameters. For example, gcc generates the following code for clone: <SyS_clone>: mov.l 8c020714 <SyS_clone+0xc>,r1 ! 8c020540 <do_fork> mov.l r7,@r15 mov r6,r7 jmp @r1 mov #0,r6 nop .word 0x0540 .word 0x8c02 The `mov.l r7,@r15` clobbers the saved value of r0 passed from userspace. For most system calls, this might not be a problem, because we'll be overwriting r0 with the return value anyway. But in the case of clone, copy_thread will need the original value of r0 if the CLONE_SETTLS flag was specified. The first patch in this series fixes this issue for system calls by pushing to the stack and extra copy of r0-r2 before invoking the handler. We discard this copy before restoring the userspace registers, so it is not a problem if they are clobbered. Exception handlers also receive the userspace register values in a similar manner, and may hit the same problem. The second patch removes the do_fpu_error handler, which looks susceptible to this problem and which, as far as I can tell, has not been used in some time. The third patch addresses other exception handlers. This patch (of 3): The userspace registers are stored at the top of the stack when the syscall handler is invoked, which allows r0-r2 to act as parameters 5-7. Parameters passed on the stack may be clobbered by the syscall handler. The solution is to push an extra copy of the registers which might be used as syscall parameters to the stack, so that the authoritative set of saved register values does not get clobbered. A few system call handlers are also updated to get the userspace registers using current_pt_regs() instead of from the stack. Signed-off-by: Bobby Bingham <koorogi@koorogi.info> Cc: Paul Mundt <paul.mundt@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/sh')
-rw-r--r--arch/sh/include/asm/syscalls_32.h12
-rw-r--r--arch/sh/kernel/entry-common.S15
-rw-r--r--arch/sh/kernel/signal_32.c12
-rw-r--r--arch/sh/kernel/sys_sh32.c7
4 files changed, 20 insertions, 26 deletions
diff --git a/arch/sh/include/asm/syscalls_32.h b/arch/sh/include/asm/syscalls_32.h
index 4f97df87d7d5..4f643aa718e3 100644
--- a/arch/sh/include/asm/syscalls_32.h
+++ b/arch/sh/include/asm/syscalls_32.h
@@ -9,15 +9,9 @@
9 9
10struct pt_regs; 10struct pt_regs;
11 11
12asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, 12asmlinkage int sys_sigreturn(void);
13 unsigned long r6, unsigned long r7, 13asmlinkage int sys_rt_sigreturn(void);
14 struct pt_regs __regs); 14asmlinkage int sys_sh_pipe(void);
15asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
16 unsigned long r6, unsigned long r7,
17 struct pt_regs __regs);
18asmlinkage int sys_sh_pipe(unsigned long r4, unsigned long r5,
19 unsigned long r6, unsigned long r7,
20 struct pt_regs __regs);
21asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char __user *buf, 15asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char __user *buf,
22 size_t count, long dummy, loff_t pos); 16 size_t count, long dummy, loff_t pos);
23asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char __user *buf, 17asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char __user *buf,
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S
index ca46834294b7..13047a4facd2 100644
--- a/arch/sh/kernel/entry-common.S
+++ b/arch/sh/kernel/entry-common.S
@@ -193,10 +193,10 @@ syscall_trace_entry:
193 ! Reload R0-R4 from kernel stack, where the 193 ! Reload R0-R4 from kernel stack, where the
194 ! parent may have modified them using 194 ! parent may have modified them using
195 ! ptrace(POKEUSR). (Note that R0-R2 are 195 ! ptrace(POKEUSR). (Note that R0-R2 are
196 ! used by the system call handler directly 196 ! reloaded from the kernel stack by syscall_call
197 ! from the kernel stack anyway, so don't need 197 ! below, so don't need to be reloaded here.)
198 ! to be reloaded here.) This allows the parent 198 ! This allows the parent to rewrite system calls
199 ! to rewrite system calls and args on the fly. 199 ! and args on the fly.
200 mov.l @(OFF_R4,r15), r4 ! arg0 200 mov.l @(OFF_R4,r15), r4 ! arg0
201 mov.l @(OFF_R5,r15), r5 201 mov.l @(OFF_R5,r15), r5
202 mov.l @(OFF_R6,r15), r6 202 mov.l @(OFF_R6,r15), r6
@@ -357,8 +357,15 @@ syscall_call:
357 mov.l 3f, r8 ! Load the address of sys_call_table 357 mov.l 3f, r8 ! Load the address of sys_call_table
358 add r8, r3 358 add r8, r3
359 mov.l @r3, r8 359 mov.l @r3, r8
360 mov.l @(OFF_R2,r15), r2
361 mov.l @(OFF_R1,r15), r1
362 mov.l @(OFF_R0,r15), r0
363 mov.l r2, @-r15
364 mov.l r1, @-r15
365 mov.l r0, @-r15
360 jsr @r8 ! jump to specific syscall handler 366 jsr @r8 ! jump to specific syscall handler
361 nop 367 nop
368 add #12, r15
362 mov.l @(OFF_R0,r15), r12 ! save r0 369 mov.l @(OFF_R0,r15), r12 ! save r0
363 mov.l r0, @(OFF_R0,r15) ! save the return value 370 mov.l r0, @(OFF_R0,r15) ! save the return value
364 ! 371 !
diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c
index 6af6e7c5cac8..594cd371aa28 100644
--- a/arch/sh/kernel/signal_32.c
+++ b/arch/sh/kernel/signal_32.c
@@ -148,11 +148,9 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *r0_p
148 return err; 148 return err;
149} 149}
150 150
151asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, 151asmlinkage int sys_sigreturn(void)
152 unsigned long r6, unsigned long r7,
153 struct pt_regs __regs)
154{ 152{
155 struct pt_regs *regs = RELOC_HIDE(&__regs, 0); 153 struct pt_regs *regs = current_pt_regs();
156 struct sigframe __user *frame = (struct sigframe __user *)regs->regs[15]; 154 struct sigframe __user *frame = (struct sigframe __user *)regs->regs[15];
157 sigset_t set; 155 sigset_t set;
158 int r0; 156 int r0;
@@ -180,11 +178,9 @@ badframe:
180 return 0; 178 return 0;
181} 179}
182 180
183asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, 181asmlinkage int sys_rt_sigreturn(void)
184 unsigned long r6, unsigned long r7,
185 struct pt_regs __regs)
186{ 182{
187 struct pt_regs *regs = RELOC_HIDE(&__regs, 0); 183 struct pt_regs *regs = current_pt_regs();
188 struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15]; 184 struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15];
189 sigset_t set; 185 sigset_t set;
190 int r0; 186 int r0;
diff --git a/arch/sh/kernel/sys_sh32.c b/arch/sh/kernel/sys_sh32.c
index 497bab3a0401..b66d1c62eb19 100644
--- a/arch/sh/kernel/sys_sh32.c
+++ b/arch/sh/kernel/sys_sh32.c
@@ -21,17 +21,14 @@
21 * sys_pipe() is the normal C calling standard for creating 21 * sys_pipe() is the normal C calling standard for creating
22 * a pipe. It's not the way Unix traditionally does this, though. 22 * a pipe. It's not the way Unix traditionally does this, though.
23 */ 23 */
24asmlinkage int sys_sh_pipe(unsigned long r4, unsigned long r5, 24asmlinkage int sys_sh_pipe(void)
25 unsigned long r6, unsigned long r7,
26 struct pt_regs __regs)
27{ 25{
28 struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
29 int fd[2]; 26 int fd[2];
30 int error; 27 int error;
31 28
32 error = do_pipe_flags(fd, 0); 29 error = do_pipe_flags(fd, 0);
33 if (!error) { 30 if (!error) {
34 regs->regs[1] = fd[1]; 31 current_pt_regs()->regs[1] = fd[1];
35 return fd[0]; 32 return fd[0];
36 } 33 }
37 return error; 34 return error;