Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Bobby Bingham and committed by
Linus Torvalds
abafe5d9 d0df04f7

+20 -26
+3 -9
arch/sh/include/asm/syscalls_32.h
··· 9 9 10 10 struct pt_regs; 11 11 12 - asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, 13 - unsigned long r6, unsigned long r7, 14 - struct pt_regs __regs); 15 - asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, 16 - unsigned long r6, unsigned long r7, 17 - struct pt_regs __regs); 18 - asmlinkage int sys_sh_pipe(unsigned long r4, unsigned long r5, 19 - unsigned long r6, unsigned long r7, 20 - struct pt_regs __regs); 12 + asmlinkage int sys_sigreturn(void); 13 + asmlinkage int sys_rt_sigreturn(void); 14 + asmlinkage int sys_sh_pipe(void); 21 15 asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char __user *buf, 22 16 size_t count, long dummy, loff_t pos); 23 17 asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char __user *buf,
+11 -4
arch/sh/kernel/entry-common.S
··· 193 193 ! Reload R0-R4 from kernel stack, where the 194 194 ! parent may have modified them using 195 195 ! ptrace(POKEUSR). (Note that R0-R2 are 196 - ! used by the system call handler directly 197 - ! from the kernel stack anyway, so don't need 198 - ! to be reloaded here.) This allows the parent 199 - ! to rewrite system calls and args on the fly. 196 + ! reloaded from the kernel stack by syscall_call 197 + ! below, so don't need to be reloaded here.) 198 + ! This allows the parent to rewrite system calls 199 + ! and args on the fly. 200 200 mov.l @(OFF_R4,r15), r4 ! arg0 201 201 mov.l @(OFF_R5,r15), r5 202 202 mov.l @(OFF_R6,r15), r6 ··· 357 357 mov.l 3f, r8 ! Load the address of sys_call_table 358 358 add r8, r3 359 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 366 jsr @r8 ! jump to specific syscall handler 361 367 nop 368 + add #12, r15 362 369 mov.l @(OFF_R0,r15), r12 ! save r0 363 370 mov.l r0, @(OFF_R0,r15) ! save the return value 364 371 !
+4 -8
arch/sh/kernel/signal_32.c
··· 148 148 return err; 149 149 } 150 150 151 - asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, 152 - unsigned long r6, unsigned long r7, 153 - struct pt_regs __regs) 151 + asmlinkage int sys_sigreturn(void) 154 152 { 155 - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); 153 + struct pt_regs *regs = current_pt_regs(); 156 154 struct sigframe __user *frame = (struct sigframe __user *)regs->regs[15]; 157 155 sigset_t set; 158 156 int r0; ··· 178 180 return 0; 179 181 } 180 182 181 - asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, 182 - unsigned long r6, unsigned long r7, 183 - struct pt_regs __regs) 183 + asmlinkage int sys_rt_sigreturn(void) 184 184 { 185 - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); 185 + struct pt_regs *regs = current_pt_regs(); 186 186 struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15]; 187 187 sigset_t set; 188 188 int r0;
+2 -5
arch/sh/kernel/sys_sh32.c
··· 21 21 * sys_pipe() is the normal C calling standard for creating 22 22 * a pipe. It's not the way Unix traditionally does this, though. 23 23 */ 24 - asmlinkage int sys_sh_pipe(unsigned long r4, unsigned long r5, 25 - unsigned long r6, unsigned long r7, 26 - struct pt_regs __regs) 24 + asmlinkage int sys_sh_pipe(void) 27 25 { 28 - struct pt_regs *regs = RELOC_HIDE(&__regs, 0); 29 26 int fd[2]; 30 27 int error; 31 28 32 29 error = do_pipe_flags(fd, 0); 33 30 if (!error) { 34 - regs->regs[1] = fd[1]; 31 + current_pt_regs()->regs[1] = fd[1]; 35 32 return fd[0]; 36 33 } 37 34 return error;