From: NIIBE Y. <gn...@m1...> - 2002-08-28 06:04:59
|
I've found two (long-standing) bugs, when reviewing gUSA implementation. (1) There was a problem on restarting system call. If the arguments of system call is more than four, the fifth argument will be clobbered on restarting. Because register r0 is clobbered by return value of system call. (2) If the process is traced, fork returns bogus value for child process. Here're fixes. Will do for 2.5. 2002-08-28 NIIBE Yutaka <gn...@m1...> Bug fixes of system call restart, fork on traced process. * arch/sh/kernel/signal.c: Change the arguments. (sys_sigsuspend, sys_rt_sigsuspend): Follow the change. (restore_sigcontext): Remove handling of r0. (do_sigreturn): New function (was: sys_sigreturn). (do_rt_sigreturn): New function (was: sys_rt_sigreturn). (sys_sigreturn, sys_rt_sigreturn): Syscall entries. Calling do_XXX, and go back to ret_from_syscall without setting r0. (handle_signal): Added from_syscall and orig_r0 arguments. Restore regs->regs[0] on ERESTARTSYS/ERESTARTNOINTR (BUG Fix). (do_signal): Rearrange arguments. Restore regs.regs[0] on ERESTARTSYS/ERESTARTNOINTR (BUG Fix). * arch/sh/kernel/entry.S (OFF_TRA): Removed. (system_call): Don't set regs->tra (as it's removed). (ret_from_fork): Set return value (r0) as 0, before going to syscall_ret or syscall_ret_trace (BUG Fix). (syscall_ret_trace, syscall_ret): Save original value of r0 into r9. Set r8 as 1 indicating it's from syscall. (ret_from_exception): Set r8 as 0. (signal_return): Follow the change of arguments of do_signal. (restore_all, handle_exception): Remove handling of tra. * include/asm-sh/ptrace.h (struct pt_regs): Removed the member tra. Index: arch/sh/kernel/entry.S =================================================================== RCS file: /cvsroot/linuxsh/linux/arch/sh/kernel/entry.S,v retrieving revision 1.1.1.1.2.8 diff -u -3 -p -r1.1.1.1.2.8 entry.S --- arch/sh/kernel/entry.S 15 Aug 2002 14:17:56 -0000 1.1.1.1.2.8 +++ arch/sh/kernel/entry.S 28 Aug 2002 05:57:02 -0000 @@ -32,6 +32,12 @@ * * jmp @k0 ! control-transfer instruction * ldc k1, ssr ! delay slot + */ + +/* + * ABI at ret_from_syscall: + * r8: from_syscall (0 or 1) indicating it's from syscall or not + * r9: original r0 (to restart system call) * * Stack layout in 'ret_from_syscall': * ptrace needs to have all regs on the stack. @@ -47,7 +53,6 @@ * gbr * mach * macl - * syscall # * */ @@ -96,8 +101,6 @@ OFF_R7 = 28 /* New ABI: ar OFF_SP = (15*4) OFF_PC = (16*4) OFF_SR = (16*4+8) -OFF_TRA = (16*4+6*4) - #define k0 r0 #define k1 r1 @@ -331,7 +334,8 @@ ENTRY(ret_from_fork) mov.l @(tsk_ptrace,r0), r0 ! Is current PTRACE_SYSCALL'd? mov #PT_TRACESYS, r1 tst r1, r0 - bt ret_from_syscall + bt/s syscall_ret + mov #0, r0 bra syscall_ret_trace nop @@ -377,11 +381,7 @@ system_call: cmp/hi r9, r8 bt debug_trap ! - mov #OFF_TRA, r14 - add r15, r14 - ! ! New Syscall ABI - mov.l r8, @r14 ! set tra STI() ! stc k_current, r11 @@ -434,6 +434,8 @@ system_call: ! In case of trace syscall_ret_trace: + mov.l @(OFF_R0,r15), r9 ! save original value of r0 + mov #1, r8 ! indicate it's syscall return mov.l r0, @(OFF_R0,r15) ! save the return value mov.l __syscall_trace, r1 mova SYMBOL_NAME(ret_from_syscall), r0 @@ -488,10 +490,12 @@ ret_from_exception: bt restore_all ! Yes, it's from kernel, go back soon ! bra ret_from_syscall - nop + mov #0, r8 ! indicate it's *not* syscall return .align 2 syscall_ret: + mov.l @(OFF_R0,r15), r9 ! save original value of r0 + mov #1, r8 ! indicate it's syscall return mov.l r0, @(OFF_R0,r15) ! save the return value /* fall through */ @@ -509,9 +513,10 @@ ENTRY(ret_from_syscall) tst r0, r0 bt restore_all signal_return: - mov r15, r4 - mov #0, r5 mov.l __do_signal, r1 + mov #0, r4 + mov r8, r5 + mov r9, r6 mova restore_all, r0 jmp @r1 lds r0, pr @@ -551,7 +556,6 @@ restore_all: ldc.l @r15+, gbr lds.l @r15+, mach lds.l @r15+, macl - add #4, r15 ! Skip syscall number ! ! Calculate new SR value mov k3, k2 ! original SR value @@ -665,11 +669,9 @@ handle_exception: add current, k1 mov k1, r15 ! change to kernel stack ! -9: mov #-1, k4 - mov.l 3f, k1 - ! Save the user registers on the stack. - mov.l k4, @-r15 ! Set tra (default: -1) +9: mov.l 3f, k1 ! + ! Save the user registers on the stack. sts.l macl, @-r15 sts.l mach, @-r15 stc.l gbr, @-r15 Index: arch/sh/kernel/signal.c =================================================================== RCS file: /cvsroot/linuxsh/linux/arch/sh/kernel/signal.c,v retrieving revision 1.1.1.1.2.5 diff -u -3 -p -r1.1.1.1.2.5 signal.c --- arch/sh/kernel/signal.c 20 Aug 2002 01:15:48 -0000 1.1.1.1.2.5 +++ arch/sh/kernel/signal.c 28 Aug 2002 05:57:02 -0000 @@ -30,7 +30,9 @@ #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) -asmlinkage int do_signal(struct pt_regs *regs, sigset_t *oldset); +asmlinkage int do_signal(sigset_t *oldset, int from_syscall, + unsigned long orig_r0, unsigned long r7, + struct pt_regs regs); int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) { @@ -88,7 +90,7 @@ sys_sigsuspend(old_sigset_t mask, while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(®s, &saveset)) + if (do_signal(&saveset, 1, 0, 0, regs)) return -EINTR; } } @@ -117,7 +119,7 @@ sys_rt_sigsuspend(sigset_t *unewset, siz while (1) { current->state = TASK_INTERRUPTIBLE; schedule(); - if (do_signal(®s, &saveset)) + if (do_signal(&saveset, 1, 0, 0, regs)) return -EINTR; } } @@ -217,7 +219,7 @@ static inline int save_sigcontext_fpu(st #endif static int -restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc, int *r0_p) +restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) { unsigned int err = 0; @@ -248,19 +250,15 @@ restore_sigcontext(struct pt_regs *regs, err |= restore_sigcontext_fpu(sc); } #endif - - regs->tra = -1; /* disable syscall checks */ - err |= __get_user(*r0_p, &sc->sc_regs[0]); return err; } -asmlinkage int sys_sigreturn(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs regs) +static void do_sigreturn(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs regs) { struct sigframe *frame = (struct sigframe *)regs.regs[15]; sigset_t set; - int r0; if (verify_area(VERIFY_READ, frame, sizeof(*frame))) goto badframe; @@ -278,23 +276,35 @@ asmlinkage int sys_sigreturn(unsigned lo recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - if (restore_sigcontext(®s, &frame->sc, &r0)) + if (restore_sigcontext(®s, &frame->sc)) goto badframe; - return r0; + return; badframe: force_sig(SIGSEGV, current); - return 0; } -asmlinkage int sys_rt_sigreturn(unsigned long r4, unsigned long r5, - unsigned long r6, unsigned long r7, - struct pt_regs regs) +asm volatile ("" + ".align 2\n\t" + ".globl sys_sigreturn\n" +"sys_sigreturn:\n\t" + "mov.l 1f, r0\n\t" + "mov.l 2f, r1\n\t" + "mov #0, r8 ! indicate it's *not* from syscall\n\t" + "jmp @r0\n\t" + " lds r1, pr ! override the return address\n\t" + ".align 2\n" +"1: do_sigreturn\n\t" +"2: ret_from_syscall" +); + +static void do_rt_sigreturn(unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7, + struct pt_regs regs) { struct rt_sigframe *frame = (struct rt_sigframe *)regs.regs[15]; sigset_t set; stack_t st; - int r0; if (verify_area(VERIFY_READ, frame, sizeof(*frame))) goto badframe; @@ -308,7 +318,7 @@ asmlinkage int sys_rt_sigreturn(unsigned recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); - if (restore_sigcontext(®s, &frame->uc.uc_mcontext, &r0)) + if (restore_sigcontext(®s, &frame->uc.uc_mcontext)) goto badframe; if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) @@ -316,13 +326,25 @@ asmlinkage int sys_rt_sigreturn(unsigned /* It is more difficult to avoid calling this function than to call it and ignore errors. */ do_sigaltstack(&st, NULL, regs.regs[15]); - - return r0; + return; badframe: force_sig(SIGSEGV, current); - return 0; -} +} + +asm volatile ("" + ".align 2\n\t" + ".globl sys_sigreturn\n" +"sys_rt_sigreturn:\n\t" + "mov.l 1f, r0\n\t" + "mov.l 2f, r1\n\t" + "mov #0, r8 ! indicate it's *not* from syscall\n\t" + "jmp @r0\n\t" + " lds r1, pr ! override the return address\n\t" + ".align 2\n" +"1: do_rt_sigreturn\n\t" +"2: ret_from_syscall" +); /* * Set up a signal frame. @@ -507,10 +529,11 @@ give_sigsegv: static void handle_signal(unsigned long sig, struct k_sigaction *ka, - siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) + siginfo_t *info, sigset_t *oldset, struct pt_regs *regs, + int from_syscall, unsigned long orig_r0) { /* Are we from a system call? */ - if (regs->tra >= 0) { + if (from_syscall) { /* If so, check system call restarting.. */ switch (regs->regs[0]) { case -ERESTARTNOHAND: @@ -524,6 +547,7 @@ handle_signal(unsigned long sig, struct } /* fallthrough */ case -ERESTARTNOINTR: + regs->regs[0] = orig_r0; regs->pc -= 2; } } else { @@ -566,7 +590,8 @@ handle_signal(unsigned long sig, struct * the kernel can handle, and then we build all the user-level signal handling * stack-frames in one go after that. */ -int do_signal(struct pt_regs *regs, sigset_t *oldset) +int do_signal(sigset_t *oldset, int from_syscall, unsigned long orig_r0, + unsigned long r7, struct pt_regs regs) { siginfo_t info; struct k_sigaction *ka; @@ -577,7 +602,7 @@ int do_signal(struct pt_regs *regs, sigs * kernel mode. Just return without doing anything * if so. */ - if (!user_mode(regs)) + if (!user_mode(®s)) return 1; if (!oldset) @@ -662,7 +687,7 @@ int do_signal(struct pt_regs *regs, sigs case SIGQUIT: case SIGILL: case SIGTRAP: case SIGABRT: case SIGFPE: case SIGSEGV: case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: - if (do_coredump(signr, regs)) + if (do_coredump(signr, ®s)) exit_code |= 0x80; /* FALLTHRU */ @@ -676,17 +701,19 @@ int do_signal(struct pt_regs *regs, sigs } /* Whee! Actually deliver the signal. */ - handle_signal(signr, ka, &info, oldset, regs); + handle_signal(signr, ka, &info, oldset, ®s, + from_syscall, orig_r0); return 1; } /* Did we come from a system call? */ - if (regs->tra >= 0) { + if (from_syscall) { /* Restart the system call - no handlers present */ - if (regs->regs[0] == -ERESTARTNOHAND || - regs->regs[0] == -ERESTARTSYS || - regs->regs[0] == -ERESTARTNOINTR) { - regs->pc -= 2; + if (regs.regs[0] == -ERESTARTNOHAND || + regs.regs[0] == -ERESTARTSYS || + regs.regs[0] == -ERESTARTNOINTR) { + regs.regs[0] = orig_r0; + regs.pc -= 2; } } return 0; Index: include/asm-sh/ptrace.h =================================================================== RCS file: /cvsroot/linuxsh/linux/include/asm-sh/ptrace.h,v retrieving revision 1.1.1.1.2.2 diff -u -3 -p -r1.1.1.1.2.2 ptrace.h --- include/asm-sh/ptrace.h 24 May 2002 14:41:19 -0000 1.1.1.1.2.2 +++ include/asm-sh/ptrace.h 28 Aug 2002 05:57:02 -0000 @@ -62,7 +62,6 @@ struct pt_regs { unsigned long gbr; unsigned long mach; unsigned long macl; - long tra; }; #ifdef __KERNEL__ |