X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;ds=sidebyside;f=arch%2Fs390%2Fkernel%2Fsignal.c;h=ae1927e48cfb8fbef2fe082885245d3ed9c17e2a;hb=76828883507a47dae78837ab5dec5a5b4513c667;hp=2379b673450bab161eca7f74eefec43f2f3bd36a;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 2379b6734..ae1927e48 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -1,8 +1,7 @@ /* * arch/s390/kernel/signal.c * - * S390 version - * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) IBM Corp. 1999,2006 * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) * * Based on Intel version @@ -39,6 +38,7 @@ typedef struct __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; struct sigcontext sc; _sigregs sregs; + int signo; __u8 retcode[S390_SYSCALL_SIZE]; } sigframe; @@ -50,71 +50,36 @@ typedef struct struct ucontext uc; } rt_sigframe; -int do_signal(struct pt_regs *regs, sigset_t *oldset); - /* * Atomically swap in the new signal mask, and wait for a signal. */ asmlinkage int -sys_sigsuspend(struct pt_regs * regs, int history0, int history1, - old_sigset_t mask) +sys_sigsuspend(int history0, int history1, old_sigset_t mask) { - sigset_t saveset; - mask &= _BLOCKABLE; spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; + current->saved_sigmask = current->blocked; siginitset(¤t->blocked, mask); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); - regs->gprs[2] = -EINTR; - - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - if (do_signal(regs, &saveset)) - return -EINTR; - } -} - -asmlinkage int -sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t sigsetsize) -{ - sigset_t saveset, newset; - - /* XXX: Don't preclude handling different sized sigset_t's. */ - if (sigsetsize != sizeof(sigset_t)) - return -EINVAL; - if (copy_from_user(&newset, unewset, sizeof(newset))) - return -EFAULT; - sigdelsetmask(&newset, ~_BLOCKABLE); + current->state = TASK_INTERRUPTIBLE; + schedule(); + set_thread_flag(TIF_RESTORE_SIGMASK); - spin_lock_irq(¤t->sighand->siglock); - saveset = current->blocked; - current->blocked = newset; - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - regs->gprs[2] = -EINTR; - - while (1) { - set_current_state(TASK_INTERRUPTIBLE); - schedule(); - if (do_signal(regs, &saveset)) - return -EINTR; - } + return -ERESTARTNOHAND; } -asmlinkage int -sys_sigaction(int sig, const struct old_sigaction *act, - struct old_sigaction *oact) +asmlinkage long +sys_sigaction(int sig, const struct old_sigaction __user *act, + struct old_sigaction __user *oact) { struct k_sigaction new_ka, old_ka; int ret; if (act) { old_sigset_t mask; - if (verify_area(VERIFY_READ, act, sizeof(*act)) || + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || __get_user(new_ka.sa.sa_handler, &act->sa_handler) || __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) return -EFAULT; @@ -126,7 +91,7 @@ sys_sigaction(int sig, const struct old_sigaction *act, ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { - if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) return -EFAULT; @@ -137,8 +102,9 @@ sys_sigaction(int sig, const struct old_sigaction *act, return ret; } -asmlinkage int -sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs *regs) +asmlinkage long +sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, + struct pt_regs *regs) { return do_sigaltstack(uss, uoss, regs->gprs[15]); } @@ -146,7 +112,7 @@ sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs *regs) /* Returns non-zero on fault. */ -static int save_sigregs(struct pt_regs *regs, _sigregs *sregs) +static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) { unsigned long old_mask = regs->psw.mask; int err; @@ -175,7 +141,7 @@ static int save_sigregs(struct pt_regs *regs, _sigregs *sregs) } /* Returns positive number on error */ -static int restore_sigregs(struct pt_regs *regs, _sigregs *sregs) +static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) { unsigned long old_mask = regs->psw.mask; int err; @@ -208,10 +174,10 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs *sregs) asmlinkage long sys_sigreturn(struct pt_regs *regs) { - sigframe *frame = (sigframe *)regs->gprs[15]; + sigframe __user *frame = (sigframe __user *)regs->gprs[15]; sigset_t set; - if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) goto badframe; if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) goto badframe; @@ -234,10 +200,10 @@ badframe: asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) { - rt_sigframe *frame = (rt_sigframe *)regs->gprs[15]; + rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; sigset_t set; - if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) goto badframe; if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) goto badframe; @@ -251,9 +217,9 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) if (restore_sigregs(regs, &frame->uc.uc_mcontext)) goto badframe; - /* It is more difficult to avoid calling this function than to - call it and ignore errors. */ - do_sigaltstack(&frame->uc.uc_stack, NULL, regs->gprs[15]); + if (do_sigaltstack(&frame->uc.uc_stack, NULL, + regs->gprs[15]) == -EFAULT) + goto badframe; return regs->gprs[2]; badframe: @@ -269,7 +235,7 @@ badframe: /* * Determine which stack to use.. */ -static inline void * +static inline void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) { unsigned long sp; @@ -279,7 +245,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) /* This is the X/Open sanctioned signal stack switching. */ if (ka->sa.sa_flags & SA_ONSTACK) { - if (! on_sig_stack(sp)) + if (! sas_ss_flags(sp)) sp = current->sas_ss_sp + current->sas_ss_size; } @@ -290,7 +256,7 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) sp = (unsigned long) ka->sa.sa_restorer; } - return (void *)((sp - frame_size) & -8ul); + return (void __user *)((sp - frame_size) & -8ul); } static inline int map_signal(int sig) @@ -303,10 +269,12 @@ static inline int map_signal(int sig) return sig; } -static void setup_frame(int sig, struct k_sigaction *ka, - sigset_t *set, struct pt_regs * regs) +static int setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) { - sigframe *frame = get_sigframe(ka, regs, sizeof(sigframe)); + sigframe __user *frame; + + frame = get_sigframe(ka, regs, sizeof(sigframe)); if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe))) goto give_sigsegv; @@ -326,13 +294,13 @@ static void setup_frame(int sig, struct k_sigaction *ka, } else { regs->gprs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE; - if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, - (u16 *)(frame->retcode))) + if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, + (u16 __user *)(frame->retcode))) goto give_sigsegv; } /* Set up backchain. */ - if (__put_user(regs->gprs[15], (addr_t *) frame)) + if (__put_user(regs->gprs[15], (addr_t __user *) frame)) goto give_sigsegv; /* Set up registers for signal handler */ @@ -346,19 +314,24 @@ static void setup_frame(int sig, struct k_sigaction *ka, To avoid breaking binary compatibility, they are passed as args. */ regs->gprs[4] = current->thread.trap_no; regs->gprs[5] = current->thread.prot_addr; - return; + + /* Place signal number on stack to allow backtrace from handler. */ + if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) + goto give_sigsegv; + return 0; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); + return -EFAULT; } -static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, +static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs * regs) { int err = 0; - rt_sigframe *frame = get_sigframe(ka, regs, sizeof(rt_sigframe)); + rt_sigframe __user *frame; + + frame = get_sigframe(ka, regs, sizeof(rt_sigframe)); if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe))) goto give_sigsegv; @@ -367,8 +340,8 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); - err |= __put_user(0, &frame->uc.uc_link); - err |= __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(NULL, &frame->uc.uc_link); + err |= __put_user((void __user *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); err |= __put_user(sas_ss_flags(regs->gprs[15]), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); @@ -385,12 +358,12 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, } else { regs->gprs[14] = (unsigned long) frame->retcode | PSW_ADDR_AMODE; - err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, - (u16 *)(frame->retcode)); + err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, + (u16 __user *)(frame->retcode)); } /* Set up backchain. */ - if (__put_user(regs->gprs[15], (addr_t *) frame)) + if (__put_user(regs->gprs[15], (addr_t __user *) frame)) goto give_sigsegv; /* Set up registers for signal handler */ @@ -400,40 +373,39 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->gprs[2] = map_signal(sig); regs->gprs[3] = (unsigned long) &frame->info; regs->gprs[4] = (unsigned long) &frame->uc; - return; + return 0; give_sigsegv: - if (sig == SIGSEGV) - ka->sa.sa_handler = SIG_DFL; - force_sig(SIGSEGV, current); + force_sigsegv(sig, current); + return -EFAULT; } /* * OK, we're invoking a handler */ -static void -handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, - struct pt_regs * regs) +static int +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) { - struct k_sigaction *ka = ¤t->sighand->action[sig-1]; + int ret; /* Set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) - setup_rt_frame(sig, ka, info, oldset, regs); + ret = setup_rt_frame(sig, ka, info, oldset, regs); else - setup_frame(sig, ka, oldset, regs); + ret = setup_frame(sig, ka, oldset, regs); - if (ka->sa.sa_flags & SA_ONESHOT) - ka->sa.sa_handler = SIG_DFL; - - if (!(ka->sa.sa_flags & SA_NODEFER)) { + if (ret == 0) { spin_lock_irq(¤t->sighand->siglock); sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); - sigaddset(¤t->blocked,sig); + if (!(ka->sa.sa_flags & SA_NODEFER)) + sigaddset(¤t->blocked,sig); recalc_sigpending(); spin_unlock_irq(¤t->sighand->siglock); } + + return ret; } /* @@ -445,11 +417,13 @@ handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset, * 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) +void do_signal(struct pt_regs *regs) { unsigned long retval = 0, continue_addr = 0, restart_addr = 0; siginfo_t info; int signr; + struct k_sigaction ka; + sigset_t *oldset; /* * We want the common case to go fast, which @@ -458,9 +432,11 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) * if so. */ if (!user_mode(regs)) - return 1; + return; - if (!oldset) + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + oldset = ¤t->saved_sigmask; + else oldset = ¤t->blocked; /* Are we from a system call? */ @@ -471,19 +447,21 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) /* Prepare for system call restart. We do this here so that a debugger will see the already changed PSW. */ - if (retval == -ERESTARTNOHAND || - retval == -ERESTARTSYS || - retval == -ERESTARTNOINTR) { + switch (retval) { + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: regs->gprs[2] = regs->orig_gpr2; regs->psw.addr = restart_addr; - } else if (retval == -ERESTART_RESTARTBLOCK) { + break; + case -ERESTART_RESTARTBLOCK: regs->gprs[2] = -EINTR; } } /* Get signal to deliver. When running under ptrace, at this point the debugger may change all our registers ... */ - signr = get_signal_to_deliver(&info, regs, NULL); + signr = get_signal_to_deliver(&info, &ka, regs, NULL); /* Depending on the signal settings we may need to revert the decision to restart the system call. */ @@ -499,18 +477,40 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) if (signr > 0) { /* Whee! Actually deliver the signal. */ -#ifdef CONFIG_S390_SUPPORT +#ifdef CONFIG_COMPAT if (test_thread_flag(TIF_31BIT)) { - extern void handle_signal32(unsigned long sig, - siginfo_t *info, - sigset_t *oldset, - struct pt_regs *regs); - handle_signal32(signr, &info, oldset, regs); - return 1; + extern int handle_signal32(unsigned long sig, + struct k_sigaction *ka, + siginfo_t *info, + sigset_t *oldset, + struct pt_regs *regs); + if (handle_signal32( + signr, &ka, &info, oldset, regs) == 0) { + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + return; } #endif - handle_signal(signr, &info, oldset, regs); - return 1; + if (handle_signal(signr, &ka, &info, oldset, regs) == 0) { + /* + * A signal was successfully delivered; the saved + * sigmask will have been stored in the signal frame, + * and will be restored by sigreturn, so we can simply + * clear the TIF_RESTORE_SIGMASK flag. + */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + return; + } + + /* + * If there's no signal to deliver, we just put the saved sigmask back. + */ + if (test_thread_flag(TIF_RESTORE_SIGMASK)) { + clear_thread_flag(TIF_RESTORE_SIGMASK); + sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); } /* Restart a different system call. */ @@ -519,5 +519,4 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) regs->gprs[2] = __NR_restart_syscall; set_thread_flag(TIF_RESTART_SVC); } - return 0; }