Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / um / kernel / signal_kern.c
index 23aefcb..da17b75 100644 (file)
@@ -22,7 +22,6 @@
 #include "asm/ucontext.h"
 #include "kern_util.h"
 #include "signal_kern.h"
-#include "signal_user.h"
 #include "kern.h"
 #include "frame_kern.h"
 #include "sigcontext.h"
@@ -38,115 +37,108 @@ EXPORT_SYMBOL(unblock_signals);
 /*
  * OK, we're invoking a handler
  */    
-static int handle_signal(struct pt_regs *regs, unsigned long signr, 
-                        struct k_sigaction *ka, siginfo_t *info, 
-                        sigset_t *oldset, int error)
+static int handle_signal(struct pt_regs *regs, unsigned long signr,
+                        struct k_sigaction *ka, siginfo_t *info,
+                        sigset_t *oldset)
 {
-        __sighandler_t handler;
-       void (*restorer)(void);
        unsigned long sp;
-       sigset_t save;
-       int err, ret;
+       int err;
 
-       ret = 0;
        /* Always make any pending restarted system calls return -EINTR */
        current_thread_info()->restart_block.fn = do_no_restart_syscall;
-       switch(error){
-       case -ERESTART_RESTARTBLOCK:
-       case -ERESTARTNOHAND:
-               ret = -EINTR;
-               break;
 
-       case -ERESTARTSYS:
-               if (!(ka->sa.sa_flags & SA_RESTART)) {
-                       ret = -EINTR;
+       /* Did we come from a system call? */
+       if(PT_REGS_SYSCALL_NR(regs) >= 0){
+               /* If so, check system call restarting.. */
+               switch(PT_REGS_SYSCALL_RET(regs)){
+               case -ERESTART_RESTARTBLOCK:
+               case -ERESTARTNOHAND:
+                       PT_REGS_SYSCALL_RET(regs) = -EINTR;
                        break;
-               }
-               /* fallthrough */
-       case -ERESTARTNOINTR:
-               PT_REGS_RESTART_SYSCALL(regs);
-               PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
 
-               /* This is because of the UM_SET_SYSCALL_RETURN and the fact
-                * that on i386 the system call number and return value are
-                * in the same register.  When the system call restarts, %eax
-                * had better have the system call number in it.  Since the
-                * return value doesn't matter (except that it shouldn't be
-                * -ERESTART*), we'll stick the system call number there.
-                */
-               ret = PT_REGS_SYSCALL_NR(regs);
-               break;
+               case -ERESTARTSYS:
+                       if (!(ka->sa.sa_flags & SA_RESTART)) {
+                               PT_REGS_SYSCALL_RET(regs) = -EINTR;
+                               break;
+                       }
+               /* fallthrough */
+               case -ERESTARTNOINTR:
+                       PT_REGS_RESTART_SYSCALL(regs);
+                       PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
+                       break;
+               }
        }
 
-       handler = ka->sa.sa_handler;
-       save = *oldset;
+       sp = PT_REGS_SP(regs);
+       if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
+               sp = current->sas_ss_sp + current->sas_ss_size;
 
-       if (ka->sa.sa_flags & SA_ONESHOT)
-               ka->sa.sa_handler = SIG_DFL;
+#ifdef CONFIG_ARCH_HAS_SC_SIGNALS
+       if(!(ka->sa.sa_flags & SA_SIGINFO))
+               err = setup_signal_stack_sc(sp, signr, ka, regs, oldset);
+       else
+#endif
+               err = setup_signal_stack_si(sp, signr, ka, regs, info, oldset);
 
-       if (!(ka->sa.sa_flags & SA_NODEFER)) {
+       if(err){
+               spin_lock_irq(&current->sighand->siglock);
+               current->blocked = *oldset;
+               recalc_sigpending();
+               spin_unlock_irq(&current->sighand->siglock);
+               force_sigsegv(signr, current);
+       } else {
                spin_lock_irq(&current->sighand->siglock);
                sigorsets(&current->blocked, &current->blocked, 
                          &ka->sa.sa_mask);
-               sigaddset(&current->blocked, signr);
+                if(!(ka->sa.sa_flags & SA_NODEFER))
+                       sigaddset(&current->blocked, signr);
                recalc_sigpending();
                spin_unlock_irq(&current->sighand->siglock);
        }
 
-       sp = PT_REGS_SP(regs);
-
-       if((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0))
-               sp = current->sas_ss_sp + current->sas_ss_size;
-       
-       if(error != 0) PT_REGS_SET_SYSCALL_RETURN(regs, ret);
-
-       if (ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer;
-       else restorer = NULL;
-
-       if(ka->sa.sa_flags & SA_SIGINFO)
-               err = setup_signal_stack_si(sp, signr, (unsigned long) handler,
-                                           restorer, regs, info, &save);
-       else
-               err = setup_signal_stack_sc(sp, signr, (unsigned long) handler,
-                                           restorer, regs, &save);
-       if(err) goto segv;
-
-       return(0);
- segv:
-       force_sigsegv(signr, current);
-       return(1);
+       return err;
 }
 
-static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset, int error)
+static int kern_do_signal(struct pt_regs *regs)
 {
        struct k_sigaction ka_copy;
        siginfo_t info;
-       int err, sig;
+       sigset_t *oldset;
+       int sig, handled_sig = 0;
 
-       if (!oldset)
+       if (test_thread_flag(TIF_RESTORE_SIGMASK))
+               oldset = &current->saved_sigmask;
+       else
                oldset = &current->blocked;
 
-       sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL);
-       if(sig == 0)
-               return(0);
-
-       /* Whee!  Actually deliver the signal.  */
-       err = handle_signal(regs, sig, &ka_copy, &info, oldset, error);
-       if(!err)
-               return(1);
+       while((sig = get_signal_to_deliver(&info, &ka_copy, regs, NULL)) > 0){
+               handled_sig = 1;
+               /* Whee!  Actually deliver the signal.  */
+               if(!handle_signal(regs, sig, &ka_copy, &info, oldset)){
+                       /* 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);
+                       break;
+               }
+       }
 
        /* Did we come from a system call? */
-       if(PT_REGS_SYSCALL_NR(regs) >= 0){
+       if(!handled_sig && (PT_REGS_SYSCALL_NR(regs) >= 0)){
                /* Restart the system call - no handlers present */
-               if(PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOHAND ||
-                  PT_REGS_SYSCALL_RET(regs) == -ERESTARTSYS ||
-                  PT_REGS_SYSCALL_RET(regs) == -ERESTARTNOINTR){
+               switch(PT_REGS_SYSCALL_RET(regs)){
+               case -ERESTARTNOHAND:
+               case -ERESTARTSYS:
+               case -ERESTARTNOINTR:
                        PT_REGS_ORIG_SYSCALL(regs) = PT_REGS_SYSCALL_NR(regs);
                        PT_REGS_RESTART_SYSCALL(regs);
-               }
-               else if(PT_REGS_SYSCALL_RET(regs) == -ERESTART_RESTARTBLOCK){
+                       break;
+               case -ERESTART_RESTARTBLOCK:
                        PT_REGS_SYSCALL_RET(regs) = __NR_restart_syscall;
                        PT_REGS_RESTART_SYSCALL(regs);
+                       break;
                }
        }
 
@@ -157,155 +149,43 @@ static int kern_do_signal(struct pt_regs *regs, sigset_t *oldset, int error)
         * on the host.  The tracing thread will check this flag and 
         * PTRACE_SYSCALL if necessary.
         */
-       if((current->ptrace & PT_DTRACE) && 
-          is_syscall(PT_REGS_IP(&current->thread.regs)))
-               (void) CHOOSE_MODE(current->thread.mode.tt.singlestep_syscall = 1, 0);
-       return(0);
+       if(current->ptrace & PT_DTRACE)
+               current->thread.singlestep_syscall =
+                       is_syscall(PT_REGS_IP(&current->thread.regs));
+
+       /* if there's no signal to deliver, we just put the saved sigmask
+        * back */
+       if (!handled_sig && test_thread_flag(TIF_RESTORE_SIGMASK)) {
+               clear_thread_flag(TIF_RESTORE_SIGMASK);
+               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+       }
+       return(handled_sig);
 }
 
-int do_signal(int error)
+int do_signal(void)
 {
-       return(kern_do_signal(&current->thread.regs, NULL, error));
+       return(kern_do_signal(&current->thread.regs));
 }
 
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
-int sys_sigsuspend(int history0, int history1, old_sigset_t mask)
+long sys_sigsuspend(int history0, int history1, old_sigset_t mask)
 {
-       sigset_t saveset;
-
        mask &= _BLOCKABLE;
        spin_lock_irq(&current->sighand->siglock);
-       saveset = current->blocked;
+       current->saved_sigmask = current->blocked;
        siginitset(&current->blocked, mask);
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if(kern_do_signal(&current->thread.regs, &saveset, -EINTR))
-                       return(-EINTR);
-       }
-}
-
-int sys_rt_sigsuspend(sigset_t __user *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);
-
-       spin_lock_irq(&current->sighand->siglock);
-       saveset = current->blocked;
-       current->blocked = newset;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-
-       while (1) {
-               current->state = TASK_INTERRUPTIBLE;
-               schedule();
-               if (kern_do_signal(&current->thread.regs, &saveset, -EINTR))
-                       return(-EINTR);
-       }
-}
-
-int 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)) ||
-                   __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
-                   __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
-                       return -EFAULT;
-               __get_user(new_ka.sa.sa_flags, &act->sa_flags);
-               __get_user(mask, &act->sa_mask);
-               siginitset(&new_ka.sa.sa_mask, mask);
-       }
-
-       ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
-
-       if (!ret && oact) {
-               if (verify_area(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;
-               __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
-               __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask);
-       }
-
-       return ret;
+       current->state = TASK_INTERRUPTIBLE;
+       schedule();
+       set_thread_flag(TIF_RESTORE_SIGMASK);
+       return -ERESTARTNOHAND;
 }
 
-int sys_sigaltstack(const stack_t *uss, stack_t *uoss)
+long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss)
 {
        return(do_sigaltstack(uss, uoss, PT_REGS_SP(&current->thread.regs)));
 }
-
-extern int userspace_pid[];
-
-static int copy_sc_from_user(struct pt_regs *to, void *from, 
-                            struct arch_frame_data *arch)
-{
-       int ret;
-
-       ret = CHOOSE_MODE(copy_sc_from_user_tt(UPT_SC(&to->regs), from, arch),
-                         copy_sc_from_user_skas(userspace_pid[0],
-                                                &to->regs, from));
-       return(ret);
-}
-
-int sys_sigreturn(struct pt_regs regs)
-{
-       void __user *sc = sp_to_sc(PT_REGS_SP(&current->thread.regs));
-       void __user *mask = sp_to_mask(PT_REGS_SP(&current->thread.regs));
-       int sig_size = (_NSIG_WORDS - 1) * sizeof(unsigned long);
-
-       spin_lock_irq(&current->sighand->siglock);
-       copy_from_user(&current->blocked.sig[0], sc_sigmask(sc), 
-                      sizeof(current->blocked.sig[0]));
-       copy_from_user(&current->blocked.sig[1], mask, sig_size);
-       sigdelsetmask(&current->blocked, ~_BLOCKABLE);
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-       copy_sc_from_user(&current->thread.regs, sc, 
-                         &signal_frame_sc.common.arch);
-       return(PT_REGS_SYSCALL_RET(&current->thread.regs));
-}
-
-int sys_rt_sigreturn(struct pt_regs regs)
-{
-       unsigned long sp = PT_REGS_SP(&current->thread.regs);
-       struct ucontext __user *uc = sp_to_uc(sp);
-       int sig_size = _NSIG_WORDS * sizeof(unsigned long);
-
-       spin_lock_irq(&current->sighand->siglock);
-       copy_from_user(&current->blocked, &uc->uc_sigmask, sig_size);
-       sigdelsetmask(&current->blocked, ~_BLOCKABLE);
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-       copy_sc_from_user(&current->thread.regs, &uc->uc_mcontext,
-                         &signal_frame_si.common.arch);
-       return(PT_REGS_SYSCALL_RET(&current->thread.regs));
-}
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */