do_sigaltstack(): lift copying to/from userland into callers
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 27 May 2017 04:29:34 +0000 (00:29 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 27 May 2017 19:38:17 +0000 (15:38 -0400)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
kernel/signal.c

index ca92bcfeb322f3f836031ec8b3ab21867f39adf5..d1eed0d7ca6499d82f51afbbb77b94be830b73cb 100644 (file)
@@ -3113,78 +3113,68 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
 }
 
 static int
-do_sigaltstack (const stack_t __user *uss, stack_t __user *uoss, unsigned long sp)
+do_sigaltstack (const stack_t *ss, stack_t *oss, unsigned long sp)
 {
-       stack_t oss;
-       int error;
+       struct task_struct *t = current;
 
-       oss.ss_sp = (void __user *) current->sas_ss_sp;
-       oss.ss_size = current->sas_ss_size;
-       oss.ss_flags = sas_ss_flags(sp) |
-               (current->sas_ss_flags & SS_FLAG_BITS);
+       if (oss) {
+               memset(oss, 0, sizeof(stack_t));
+               oss->ss_sp = (void __user *) t->sas_ss_sp;
+               oss->ss_size = t->sas_ss_size;
+               oss->ss_flags = sas_ss_flags(sp) |
+                       (current->sas_ss_flags & SS_FLAG_BITS);
+       }
 
-       if (uss) {
-               void __user *ss_sp;
-               size_t ss_size;
-               unsigned ss_flags;
+       if (ss) {
+               void __user *ss_sp = ss->ss_sp;
+               size_t ss_size = ss->ss_size;
+               unsigned ss_flags = ss->ss_flags;
                int ss_mode;
 
-               error = -EFAULT;
-               if (!access_ok(VERIFY_READ, uss, sizeof(*uss)))
-                       goto out;
-               error = __get_user(ss_sp, &uss->ss_sp) |
-                       __get_user(ss_flags, &uss->ss_flags) |
-                       __get_user(ss_size, &uss->ss_size);
-               if (error)
-                       goto out;
-
-               error = -EPERM;
-               if (on_sig_stack(sp))
-                       goto out;
+               if (unlikely(on_sig_stack(sp)))
+                       return -EPERM;
 
                ss_mode = ss_flags & ~SS_FLAG_BITS;
-               error = -EINVAL;
-               if (ss_mode != SS_DISABLE && ss_mode != SS_ONSTACK &&
-                               ss_mode != 0)
-                       goto out;
+               if (unlikely(ss_mode != SS_DISABLE && ss_mode != SS_ONSTACK &&
+                               ss_mode != 0))
+                       return -EINVAL;
 
                if (ss_mode == SS_DISABLE) {
                        ss_size = 0;
                        ss_sp = NULL;
                } else {
-                       error = -ENOMEM;
-                       if (ss_size < MINSIGSTKSZ)
-                               goto out;
+                       if (unlikely(ss_size < MINSIGSTKSZ))
+                               return -ENOMEM;
                }
 
-               current->sas_ss_sp = (unsigned long) ss_sp;
-               current->sas_ss_size = ss_size;
-               current->sas_ss_flags = ss_flags;
-       }
-
-       error = 0;
-       if (uoss) {
-               error = -EFAULT;
-               if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss)))
-                       goto out;
-               error = __put_user(oss.ss_sp, &uoss->ss_sp) |
-                       __put_user(oss.ss_size, &uoss->ss_size) |
-                       __put_user(oss.ss_flags, &uoss->ss_flags);
+               t->sas_ss_sp = (unsigned long) ss_sp;
+               t->sas_ss_size = ss_size;
+               t->sas_ss_flags = ss_flags;
        }
-
-out:
-       return error;
+       return 0;
 }
+
 SYSCALL_DEFINE2(sigaltstack,const stack_t __user *,uss, stack_t __user *,uoss)
 {
-       return do_sigaltstack(uss, uoss, current_user_stack_pointer());
+       stack_t new, old;
+       int err;
+       if (uss && copy_from_user(&new, uss, sizeof(stack_t)))
+               return -EFAULT;
+       err = do_sigaltstack(uss ? &new : NULL, uoss ? &old : NULL,
+                             current_user_stack_pointer());
+       if (!err && uoss && copy_to_user(uoss, &old, sizeof(stack_t)))
+               err = -EFAULT;
+       return err;
 }
 
 int restore_altstack(const stack_t __user *uss)
 {
-       int err = do_sigaltstack(uss, NULL, current_user_stack_pointer());
+       stack_t new;
+       if (copy_from_user(&new, uss, sizeof(stack_t)))
+               return -EFAULT;
+       (void)do_sigaltstack(&new, NULL, current_user_stack_pointer());
        /* squash all but EFAULT for now */
-       return err == -EFAULT ? err : 0;
+       return 0;
 }
 
 int __save_altstack(stack_t __user *uss, unsigned long sp)
@@ -3207,29 +3197,24 @@ COMPAT_SYSCALL_DEFINE2(sigaltstack,
 {
        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,
+       ret = do_sigaltstack(uss_ptr ? &uss : NULL, &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))
+               compat_stack_t old;
+               memset(&old, 0, sizeof(old));
+               old.ss_sp = ptr_to_compat(uoss.ss_sp);
+               old.ss_flags = uoss.ss_flags;
+               old.ss_size = uoss.ss_size;
+               if (copy_to_user(uoss_ptr, &old, sizeof(compat_stack_t)))
                        ret = -EFAULT;
        }
        return ret;