generic compat_sys_rt_sigprocmask()
[firefly-linux-kernel-4.4.55.git] / kernel / signal.c
index 580a91e634710b6dbbc75f328c3bbef549b999cb..bce1222b73159ec5d7877e7394849f5ee8a5d002 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/nsproxy.h>
 #include <linux/user_namespace.h>
 #include <linux/uprobes.h>
+#include <linux/compat.h>
 #define CREATE_TRACE_POINTS
 #include <trace/events/signal.h>
 
@@ -2527,11 +2528,8 @@ static void __set_task_blocked(struct task_struct *tsk, const sigset_t *newset)
  */
 void set_current_blocked(sigset_t *newset)
 {
-       struct task_struct *tsk = current;
        sigdelsetmask(newset, sigmask(SIGKILL) | sigmask(SIGSTOP));
-       spin_lock_irq(&tsk->sighand->siglock);
-       __set_task_blocked(tsk, newset);
-       spin_unlock_irq(&tsk->sighand->siglock);
+       __set_current_blocked(newset);
 }
 
 void __set_current_blocked(const sigset_t *newset)
@@ -2615,6 +2613,47 @@ SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset,
        return 0;
 }
 
+#ifdef CONFIG_COMPAT
+#ifdef CONFIG_GENERIC_COMPAT_RT_SIGPROCMASK
+COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset,
+               compat_sigset_t __user *, oset, compat_size_t, sigsetsize)
+{
+#ifdef __BIG_ENDIAN
+       sigset_t old_set = current->blocked;
+
+       /* XXX: Don't preclude handling different sized sigset_t's.  */
+       if (sigsetsize != sizeof(sigset_t))
+               return -EINVAL;
+
+       if (nset) {
+               compat_sigset_t new32;
+               sigset_t new_set;
+               int error;
+               if (copy_from_user(&new32, nset, sizeof(compat_sigset_t)))
+                       return -EFAULT;
+
+               sigset_from_compat(&new_set, &new32);
+               sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP));
+
+               error = sigprocmask(how, &new_set, NULL);
+               if (error)
+                       return error;
+       }
+       if (oset) {
+               compat_sigset_t old32;
+               sigset_to_compat(&old32, &old_set);
+               if (copy_to_user(oset, &old_set, sizeof(sigset_t)))
+                       return -EFAULT;
+       }
+       return 0;
+#else
+       return sys_rt_sigprocmask(how, (sigset_t __user *)nset,
+                                 (sigset_t __user *)oset, sigsetsize);
+#endif
+}
+#endif
+#endif
+
 long do_sigpending(void __user *set, unsigned long sigsetsize)
 {
        long error = -EINVAL;
@@ -3094,6 +3133,80 @@ do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long s
 out:
        return error;
 }
+#ifdef CONFIG_GENERIC_SIGALTSTACK
+SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss)
+{
+       return do_sigaltstack(uss, uoss, current_user_stack_pointer());
+}
+#endif
+
+int restore_altstack(const stack_t __user *uss)
+{
+       int err = do_sigaltstack(uss, NULL, current_user_stack_pointer());
+       /* squash all but EFAULT for now */
+       return err == -EFAULT ? err : 0;
+}
+
+int __save_altstack(stack_t __user *uss, unsigned long sp)
+{
+       struct task_struct *t = current;
+       return  __put_user((void __user *)t->sas_ss_sp, &uss->ss_sp) |
+               __put_user(sas_ss_flags(sp), &uss->ss_flags) |
+               __put_user(t->sas_ss_size, &uss->ss_size);
+}
+
+#ifdef CONFIG_COMPAT
+#ifdef CONFIG_GENERIC_SIGALTSTACK
+COMPAT_SYSCALL_DEFINE2(sigaltstack,
+                       const compat_stack_t __user *, uss_ptr,
+                       compat_stack_t __user *, uoss_ptr)
+{
+       stack_t uss, uoss;
+       int ret;
+       mm_segment_t seg;
+
+       if (uss_ptr) {
+               compat_stack_t uss32;
+
+               memset(&uss, 0, sizeof(stack_t));
+               if (copy_from_user(&uss32, uss_ptr, sizeof(compat_stack_t)))
+                       return -EFAULT;
+               uss.ss_sp = compat_ptr(uss32.ss_sp);
+               uss.ss_flags = uss32.ss_flags;
+               uss.ss_size = uss32.ss_size;
+       }
+       seg = get_fs();
+       set_fs(KERNEL_DS);
+       ret = do_sigaltstack((stack_t __force __user *) (uss_ptr ? &uss : NULL),
+                            (stack_t __force __user *) &uoss,
+                            compat_user_stack_pointer());
+       set_fs(seg);
+       if (ret >= 0 && uoss_ptr)  {
+               if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(compat_stack_t)) ||
+                   __put_user(ptr_to_compat(uoss.ss_sp), &uoss_ptr->ss_sp) ||
+                   __put_user(uoss.ss_flags, &uoss_ptr->ss_flags) ||
+                   __put_user(uoss.ss_size, &uoss_ptr->ss_size))
+                       ret = -EFAULT;
+       }
+       return ret;
+}
+
+int compat_restore_altstack(const compat_stack_t __user *uss)
+{
+       int err = compat_sys_sigaltstack(uss, NULL);
+       /* squash all but -EFAULT for now */
+       return err == -EFAULT ? err : 0;
+}
+
+int __compat_save_altstack(compat_stack_t __user *uss, unsigned long sp)
+{
+       struct task_struct *t = current;
+       return  __put_user(ptr_to_compat((void __user *)t->sas_ss_sp), &uss->ss_sp) |
+               __put_user(sas_ss_flags(sp), &uss->ss_flags) |
+               __put_user(t->sas_ss_size, &uss->ss_size);
+}
+#endif
+#endif
 
 #ifdef __ARCH_WANT_SYS_SIGPENDING
 
@@ -3130,7 +3243,6 @@ SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset,
        if (nset) {
                if (copy_from_user(&new_set, nset, sizeof(*nset)))
                        return -EFAULT;
-               new_set &= ~(sigmask(SIGKILL) | sigmask(SIGSTOP));
 
                new_blocked = current->blocked;
 
@@ -3148,7 +3260,7 @@ SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset,
                        return -EINVAL;
                }
 
-               __set_current_blocked(&new_blocked);
+               set_current_blocked(&new_blocked);
        }
 
        if (oset) {
@@ -3160,7 +3272,7 @@ SYSCALL_DEFINE3(sigprocmask, int, how, old_sigset_t __user *, nset,
 }
 #endif /* __ARCH_WANT_SYS_SIGPROCMASK */
 
-#ifdef __ARCH_WANT_SYS_RT_SIGACTION
+#ifndef CONFIG_ODD_RT_SIGACTION
 /**
  *  sys_rt_sigaction - alter an action taken by a process
  *  @sig: signal to be sent
@@ -3194,7 +3306,7 @@ SYSCALL_DEFINE4(rt_sigaction, int, sig,
 out:
        return ret;
 }
-#endif /* __ARCH_WANT_SYS_RT_SIGACTION */
+#endif /* !CONFIG_ODD_RT_SIGACTION */
 
 #ifdef __ARCH_WANT_SYS_SGETMASK
 
@@ -3212,6 +3324,7 @@ SYSCALL_DEFINE1(ssetmask, int, newmask)
        int old = current->blocked.sig[0];
        sigset_t newset;
 
+       siginitset(&newset, newmask);
        set_current_blocked(&newset);
 
        return old;
@@ -3280,7 +3393,29 @@ SYSCALL_DEFINE2(rt_sigsuspend, sigset_t __user *, unewset, size_t, sigsetsize)
                return -EFAULT;
        return sigsuspend(&newset);
 }
-#endif /* __ARCH_WANT_SYS_RT_SIGSUSPEND */
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE2(rt_sigsuspend, compat_sigset_t __user *, unewset, compat_size_t, sigsetsize)
+{
+#ifdef __BIG_ENDIAN
+       sigset_t newset;
+       compat_sigset_t newset32;
+
+       /* XXX: Don't preclude handling different sized sigset_t's.  */
+       if (sigsetsize != sizeof(sigset_t))
+               return -EINVAL;
+
+       if (copy_from_user(&newset32, unewset, sizeof(compat_sigset_t)))
+               return -EFAULT;
+       sigset_from_compat(&newset, &newset32);
+       return sigsuspend(&newset);
+#else
+       /* on little-endian bitmaps don't care about granularity */
+       return sys_rt_sigsuspend((sigset_t __user *)unewset, sigsetsize);
+#endif
+}
+#endif
+#endif
 
 __attribute__((weak)) const char *arch_vma_name(struct vm_area_struct *vma)
 {