fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / s390 / kernel / compat_signal.c
index 3886a27..2bbe78a 100644 (file)
@@ -1,8 +1,7 @@
 /*
- *  arch/s390/kernel/signal32.c
+ *  arch/s390/kernel/compat_signal.c
  *
- *  S390 version
- *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Copyright (C) IBM Corp. 2000,2006
  *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
  *               Gerhard Tonn (ton@de.ibm.com)                  
  *
@@ -11,7 +10,6 @@
  *  1997-11-28  Modified for POSIX.1b signals by Richard Henderson
  */
 
-#include <linux/config.h>
 #include <linux/compat.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -30,6 +28,7 @@
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include <asm/lowcore.h>
+#include <linux/tracehook.h>
 #include "compat_linux.h"
 #include "compat_ptrace.h"
 
@@ -40,6 +39,7 @@ typedef struct
        __u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
        struct sigcontext32 sc;
        _sigregs32 sregs;
+       int signo;
        __u8 retcode[S390_SYSCALL_SIZE];
 } sigframe32;
 
@@ -47,17 +47,15 @@ typedef struct
 {
        __u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
        __u8 retcode[S390_SYSCALL_SIZE];
-       struct siginfo32 info;
+       compat_siginfo_t info;
        struct ucontext32 uc;
 } rt_sigframe32;
 
-asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
-
-int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
+int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
 {
        int err;
 
-       if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t32)))
+       if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
                return -EFAULT;
 
        /* If you change siginfo_t structure, please be sure
@@ -94,10 +92,14 @@ int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
                                          &to->si_addr);
                        break;
                case __SI_POLL >> 16:
-               case __SI_TIMER >> 16:
                        err |= __put_user(from->si_band, &to->si_band);
                        err |= __put_user(from->si_fd, &to->si_fd);
                        break;
+               case __SI_TIMER >> 16:
+                       err |= __put_user(from->si_tid, &to->si_tid);
+                       err |= __put_user(from->si_overrun, &to->si_overrun);
+                       err |= __put_user(from->si_int, &to->si_int);
+                       break;
                default:
                        break;
                }
@@ -105,105 +107,100 @@ int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from)
        return err;
 }
 
-/*
- * Atomically swap in the new signal mask, and wait for a signal.
- */
-asmlinkage int
-sys32_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t mask)
+int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
 {
-       sigset_t saveset;
-
-       mask &= _BLOCKABLE;
-       spin_lock_irq(&current->sighand->siglock);
-       saveset = current->blocked;
-       siginitset(&current->blocked, mask);
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-       regs->gprs[2] = -EINTR;
-
-       while (1) {
-               set_current_state(TASK_INTERRUPTIBLE);
-               schedule();
-               if (do_signal(regs, &saveset))
-                       return -EINTR;
-       }
-}
+       int err;
+       u32 tmp;
 
-asmlinkage int
-sys32_rt_sigsuspend(struct pt_regs * regs,compat_sigset_t *unewset, size_t sigsetsize)
-{
-       sigset_t saveset, newset;
-       compat_sigset_t set32;
+       if (!access_ok (VERIFY_READ, from, sizeof(compat_siginfo_t)))
+               return -EFAULT;
 
-       /* XXX: Don't preclude handling different sized sigset_t's.  */
-       if (sigsetsize != sizeof(sigset_t))
-               return -EINVAL;
+       err = __get_user(to->si_signo, &from->si_signo);
+       err |= __get_user(to->si_errno, &from->si_errno);
+       err |= __get_user(to->si_code, &from->si_code);
 
-       if (copy_from_user(&set32, unewset, sizeof(set32)))
-               return -EFAULT;
-       switch (_NSIG_WORDS) {
-       case 4: newset.sig[3] = set32.sig[6] + (((long)set32.sig[7]) << 32);
-       case 3: newset.sig[2] = set32.sig[4] + (((long)set32.sig[5]) << 32);
-       case 2: newset.sig[1] = set32.sig[2] + (((long)set32.sig[3]) << 32);
-       case 1: newset.sig[0] = set32.sig[0] + (((long)set32.sig[1]) << 32);
+       if (to->si_code < 0)
+               err |= __copy_from_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+       else {
+               switch (to->si_code >> 16) {
+               case __SI_RT >> 16: /* This is not generated by the kernel as of now.  */
+               case __SI_MESGQ >> 16:
+                       err |= __get_user(to->si_int, &from->si_int);
+                       /* fallthrough */
+               case __SI_KILL >> 16:
+                       err |= __get_user(to->si_pid, &from->si_pid);
+                       err |= __get_user(to->si_uid, &from->si_uid);
+                       break;
+               case __SI_CHLD >> 16:
+                       err |= __get_user(to->si_pid, &from->si_pid);
+                       err |= __get_user(to->si_uid, &from->si_uid);
+                       err |= __get_user(to->si_utime, &from->si_utime);
+                       err |= __get_user(to->si_stime, &from->si_stime);
+                       err |= __get_user(to->si_status, &from->si_status);
+                       break;
+               case __SI_FAULT >> 16:
+                       err |= __get_user(tmp, &from->si_addr);
+                       to->si_addr = (void __user *)(u64) (tmp & PSW32_ADDR_INSN);
+                       break;
+               case __SI_POLL >> 16:
+                       err |= __get_user(to->si_band, &from->si_band);
+                       err |= __get_user(to->si_fd, &from->si_fd);
+                       break;
+               case __SI_TIMER >> 16:
+                       err |= __get_user(to->si_tid, &from->si_tid);
+                       err |= __get_user(to->si_overrun, &from->si_overrun);
+                       err |= __get_user(to->si_int, &from->si_int);
+                       break;
+               default:
+                       break;
+               }
        }
-        sigdelsetmask(&newset, ~_BLOCKABLE);
-
-        spin_lock_irq(&current->sighand->siglock);
-        saveset = current->blocked;
-        current->blocked = newset;
-        recalc_sigpending();
-        spin_unlock_irq(&current->sighand->siglock);
-        regs->gprs[2] = -EINTR;
-
-        while (1) {
-                set_current_state(TASK_INTERRUPTIBLE);
-                schedule();
-                if (do_signal(regs, &saveset))
-                        return -EINTR;
-        }
-}                                                         
+       return err;
+}
 
 asmlinkage long
-sys32_sigaction(int sig, const struct old_sigaction32 *act,
-                struct old_sigaction32 *oact)
+sys32_sigaction(int sig, const struct old_sigaction32 __user *act,
+                struct old_sigaction32 __user *oact)
 {
         struct k_sigaction new_ka, old_ka;
+       unsigned long sa_handler, sa_restorer;
         int ret;
 
         if (act) {
                compat_old_sigset_t mask;
-               if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
-                   __get_user((unsigned long)new_ka.sa.sa_handler, &act->sa_handler) ||
-                   __get_user((unsigned long)new_ka.sa.sa_restorer, &act->sa_restorer))
+               if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+                   __get_user(sa_handler, &act->sa_handler) ||
+                   __get_user(sa_restorer, &act->sa_restorer) ||
+                   __get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
+                   __get_user(mask, &act->sa_mask))
                        return -EFAULT;
-               __get_user(new_ka.sa.sa_flags, &act->sa_flags);
-               __get_user(mask, &act->sa_mask);
+               new_ka.sa.sa_handler = (__sighandler_t) sa_handler;
+               new_ka.sa.sa_restorer = (void (*)(void)) sa_restorer;
                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((unsigned long)old_ka.sa.sa_handler, &oact->sa_handler) ||
-                   __put_user((unsigned long)old_ka.sa.sa_restorer, &oact->sa_restorer))
+               sa_handler = (unsigned long) old_ka.sa.sa_handler;
+               sa_restorer = (unsigned long) old_ka.sa.sa_restorer;
+               if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+                   __put_user(sa_handler, &oact->sa_handler) ||
+                   __put_user(sa_restorer, &oact->sa_restorer) ||
+                   __put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
+                   __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
                        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;
 }
 
-int
-do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact);
-
-asmlinkage long 
-sys32_rt_sigaction(int sig, const struct sigaction32 *act,
-          struct sigaction32 *oact,  size_t sigsetsize)
+asmlinkage long
+sys32_rt_sigaction(int sig, const struct sigaction32 __user *act,
+          struct sigaction32 __user *oact,  size_t sigsetsize)
 {
        struct k_sigaction new_ka, old_ka;
+       unsigned long sa_handler;
        int ret;
        compat_sigset_t set32;
 
@@ -212,7 +209,7 @@ sys32_rt_sigaction(int sig, const struct sigaction32 *act,
                return -EINVAL;
 
        if (act) {
-               ret = get_user((unsigned long)new_ka.sa.sa_handler, &act->sa_handler);
+               ret = get_user(sa_handler, &act->sa_handler);
                ret |= __copy_from_user(&set32, &act->sa_mask,
                                        sizeof(compat_sigset_t));
                switch (_NSIG_WORDS) {
@@ -229,6 +226,7 @@ sys32_rt_sigaction(int sig, const struct sigaction32 *act,
                
                if (ret)
                        return -EFAULT;
+               new_ka.sa.sa_handler = (__sighandler_t) sa_handler;
        }
 
        ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
@@ -258,30 +256,36 @@ sys32_rt_sigaction(int sig, const struct sigaction32 *act,
 }
 
 asmlinkage long
-sys32_sigaltstack(const stack_t32 *uss, stack_t32 *uoss, struct pt_regs *regs)
+sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss,
+                                                       struct pt_regs *regs)
 {
        stack_t kss, koss;
+       unsigned long ss_sp;
        int ret, err = 0;
        mm_segment_t old_fs = get_fs();
 
        if (uss) {
                if (!access_ok(VERIFY_READ, uss, sizeof(*uss)))
                        return -EFAULT;
-               err |= __get_user((unsigned long) kss.ss_sp, &uss->ss_sp);
+               err |= __get_user(ss_sp, &uss->ss_sp);
                err |= __get_user(kss.ss_size, &uss->ss_size);
                err |= __get_user(kss.ss_flags, &uss->ss_flags);
                if (err)
                        return -EFAULT;
+               kss.ss_sp = (void __user *) ss_sp;
        }
 
        set_fs (KERNEL_DS);
-       ret = do_sigaltstack(uss ? &kss : NULL , uoss ? &koss : NULL, regs->gprs[15]);
+       ret = do_sigaltstack((stack_t __user *) (uss ? &kss : NULL),
+                            (stack_t __user *) (uoss ? &koss : NULL),
+                            regs->gprs[15]);
        set_fs (old_fs);
 
        if (!ret && uoss) {
                if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss)))
                        return -EFAULT;
-               err |= __put_user((unsigned long) koss.ss_sp, &uoss->ss_sp);
+               ss_sp = (unsigned long) koss.ss_sp;
+               err |= __put_user(ss_sp, &uoss->ss_sp);
                err |= __put_user(koss.ss_size, &uoss->ss_size);
                err |= __put_user(koss.ss_flags, &uoss->ss_flags);
                if (err)
@@ -290,7 +294,7 @@ sys32_sigaltstack(const stack_t32 *uss, stack_t32 *uoss, struct pt_regs *regs)
        return ret;
 }
 
-static int save_sigregs32(struct pt_regs *regs,_sigregs32 *sregs)
+static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)
 {
        _s390_regs_common32 regs32;
        int err, i;
@@ -311,7 +315,7 @@ static int save_sigregs32(struct pt_regs *regs,_sigregs32 *sregs)
                              sizeof(_s390_fp_regs32));
 }
 
-static int restore_sigregs32(struct pt_regs *regs,_sigregs32 *sregs)
+static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
 {
        _s390_regs_common32 regs32;
        int err, i;
@@ -343,10 +347,10 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 *sregs)
 
 asmlinkage long sys32_sigreturn(struct pt_regs *regs)
 {
-       sigframe32 *frame = (sigframe32 *)regs->gprs[15];
+       sigframe32 __user *frame = (sigframe32 __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_SIZE32))
                goto badframe;
@@ -365,18 +369,18 @@ asmlinkage long sys32_sigreturn(struct pt_regs *regs)
 badframe:
        force_sig(SIGSEGV, current);
        return 0;
-}      
+}
 
 asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
 {
-       rt_sigframe32 *frame = (rt_sigframe32 *)regs->gprs[15];
+       rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15];
        sigset_t set;
        stack_t st;
        __u32 ss_sp;
        int err;
        mm_segment_t old_fs = get_fs();
 
-       if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+       if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
                goto badframe;
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
@@ -391,16 +395,14 @@ asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs)
                goto badframe;
 
        err = __get_user(ss_sp, &frame->uc.uc_stack.ss_sp);
-       st.ss_sp = (void *) A((unsigned long)ss_sp);
+       st.ss_sp = compat_ptr(ss_sp);
        err |= __get_user(st.ss_size, &frame->uc.uc_stack.ss_size);
        err |= __get_user(st.ss_flags, &frame->uc.uc_stack.ss_flags);
        if (err)
                goto badframe; 
 
-       /* It is more difficult to avoid calling this function than to
-          call it and ignore errors.  */
-       set_fs (KERNEL_DS);   
-       do_sigaltstack(&st, NULL, regs->gprs[15]);
+       set_fs (KERNEL_DS);
+       do_sigaltstack((stack_t __user *)&st, NULL, regs->gprs[15]);
        set_fs (old_fs);
 
        return regs->gprs[2];
@@ -418,7 +420,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;
@@ -428,7 +430,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;
        }
 
@@ -439,7 +441,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)
@@ -452,10 +454,10 @@ static inline int map_signal(int sig)
                return sig;
 }
 
-static void setup_frame32(int sig, struct k_sigaction *ka,
+static int setup_frame32(int sig, struct k_sigaction *ka,
                        sigset_t *set, struct pt_regs * regs)
 {
-       sigframe32 *frame = get_sigframe(ka, regs, sizeof(sigframe32));
+       sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(sigframe32));
        if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe32)))
                goto give_sigsegv;
 
@@ -474,12 +476,12 @@ static void setup_frame32(int sig, struct k_sigaction *ka,
        } else {
                regs->gprs[14] = (__u64) frame->retcode;
                if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn,
-                              (u16 *)(frame->retcode)))
+                              (u16 __user *)(frame->retcode)))
                        goto give_sigsegv;
         }
 
        /* Set up backchain. */
-       if (__put_user(regs->gprs[15], (unsigned int *) frame))
+       if (__put_user(regs->gprs[15], (unsigned int __user *) frame))
                goto give_sigsegv;
 
        /* Set up registers for signal handler */
@@ -493,19 +495,22 @@ static void setup_frame32(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_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,
+static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,
                           sigset_t *set, struct pt_regs * regs)
 {
        int err = 0;
-       rt_sigframe32 *frame = get_sigframe(ka, regs, sizeof(rt_sigframe32));
+       rt_sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(rt_sigframe32));
        if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe32)))
                goto give_sigsegv;
 
@@ -531,11 +536,11 @@ static void setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,
        } else {
                regs->gprs[14] = (__u64) frame->retcode;
                err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn,
-                                 (u16 *)(frame->retcode));
+                                 (u16 __user *)(frame->retcode));
        }
 
        /* Set up backchain. */
-       if (__put_user(regs->gprs[15], (unsigned int *) frame))
+       if (__put_user(regs->gprs[15], (unsigned int __user *) frame))
                goto give_sigsegv;
 
        /* Set up registers for signal handler */
@@ -545,39 +550,39 @@ static void setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,
        regs->gprs[2] = map_signal(sig);
        regs->gprs[3] = (__u64) &frame->info;
        regs->gprs[4] = (__u64) &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
  */    
 
-void
-handle_signal32(unsigned long sig, siginfo_t *info, sigset_t *oldset,
-       struct pt_regs * regs)
+int
+handle_signal32(unsigned long sig, struct k_sigaction *ka,
+               siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
 {
-       struct k_sigaction *ka = &current->sighand->action[sig-1];
+       int ret;
 
        /* Set up the stack frame */
        if (ka->sa.sa_flags & SA_SIGINFO)
-               setup_rt_frame32(sig, ka, info, oldset, regs);
+               ret = setup_rt_frame32(sig, ka, info, oldset, regs);
        else
-               setup_frame32(sig, ka, oldset, regs);
+               ret = setup_frame32(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(&current->sighand->siglock);
                sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
-               sigaddset(&current->blocked,sig);
+               if (!(ka->sa.sa_flags & SA_NODEFER))
+                       sigaddset(&current->blocked,sig);
                recalc_sigpending();
                spin_unlock_irq(&current->sighand->siglock);
+
+               tracehook_report_handle_signal(sig, ka, oldset, regs);
        }
-}
 
+       return ret;
+}