Skip to content

Commit abafe5d

Browse files
Bobby Binghamtorvalds
authored andcommitted
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 <[email protected]> Cc: Paul Mundt <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent d0df04f commit abafe5d

File tree

4 files changed

+20
-26
lines changed

4 files changed

+20
-26
lines changed

arch/sh/include/asm/syscalls_32.h

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,9 @@
99

1010
struct pt_regs;
1111

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);
2115
asmlinkage ssize_t sys_pread_wrapper(unsigned int fd, char __user *buf,
2216
size_t count, long dummy, loff_t pos);
2317
asmlinkage ssize_t sys_pwrite_wrapper(unsigned int fd, const char __user *buf,

arch/sh/kernel/entry-common.S

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,10 @@ syscall_trace_entry:
193193
! Reload R0-R4 from kernel stack, where the
194194
! parent may have modified them using
195195
! 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.
200200
mov.l @(OFF_R4,r15), r4 ! arg0
201201
mov.l @(OFF_R5,r15), r5
202202
mov.l @(OFF_R6,r15), r6
@@ -357,8 +357,15 @@ syscall_call:
357357
mov.l 3f, r8 ! Load the address of sys_call_table
358358
add r8, r3
359359
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
360366
jsr @r8 ! jump to specific syscall handler
361367
nop
368+
add #12, r15
362369
mov.l @(OFF_R0,r15), r12 ! save r0
363370
mov.l r0, @(OFF_R0,r15) ! save the return value
364371
!

arch/sh/kernel/signal_32.c

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,9 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc, int *r0_p
148148
return err;
149149
}
150150

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)
154152
{
155-
struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
153+
struct pt_regs *regs = current_pt_regs();
156154
struct sigframe __user *frame = (struct sigframe __user *)regs->regs[15];
157155
sigset_t set;
158156
int r0;
@@ -180,11 +178,9 @@ asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5,
180178
return 0;
181179
}
182180

183-
asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5,
184-
unsigned long r6, unsigned long r7,
185-
struct pt_regs __regs)
181+
asmlinkage int sys_rt_sigreturn(void)
186182
{
187-
struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
183+
struct pt_regs *regs = current_pt_regs();
188184
struct rt_sigframe __user *frame = (struct rt_sigframe __user *)regs->regs[15];
189185
sigset_t set;
190186
int r0;

arch/sh/kernel/sys_sh32.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,14 @@
2121
* sys_pipe() is the normal C calling standard for creating
2222
* a pipe. It's not the way Unix traditionally does this, though.
2323
*/
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)
2725
{
28-
struct pt_regs *regs = RELOC_HIDE(&__regs, 0);
2926
int fd[2];
3027
int error;
3128

3229
error = do_pipe_flags(fd, 0);
3330
if (!error) {
34-
regs->regs[1] = fd[1];
31+
current_pt_regs()->regs[1] = fd[1];
3532
return fd[0];
3633
}
3734
return error;

0 commit comments

Comments
 (0)