]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - kernel/ptrace.c
futex: Provide distinct return value when owner is exiting
[mirror_ubuntu-bionic-kernel.git] / kernel / ptrace.c
index 84b1367935e410f79770c06efadc20deb51346b3..09fb3f58a838ef39d558fd2a1c777979857eceaa 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/hw_breakpoint.h>
 #include <linux/cn_proc.h>
 #include <linux/compat.h>
+#include <linux/sched/signal.h>
 
 /*
  * Access another process' address space via ptrace.
@@ -77,9 +78,7 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent,
  */
 static void ptrace_link(struct task_struct *child, struct task_struct *new_parent)
 {
-       rcu_read_lock();
-       __ptrace_link(child, new_parent, __task_cred(new_parent));
-       rcu_read_unlock();
+       __ptrace_link(child, new_parent, current_cred());
 }
 
 /**
@@ -322,6 +321,16 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
        return -EPERM;
 ok:
        rcu_read_unlock();
+       /*
+        * If a task drops privileges and becomes nondumpable (through a syscall
+        * like setresuid()) while we are trying to access it, we must ensure
+        * that the dumpability is read after the credentials; otherwise,
+        * we may be able to attach to a task that we shouldn't be able to
+        * attach to (as if the task had dropped privileges without becoming
+        * nondumpable).
+        * Pairs with a write barrier in commit_creds().
+        */
+       smp_rmb();
        mm = task->mm;
        if (mm &&
            ((get_dumpable(mm) != SUID_DUMP_USER) &&
@@ -703,6 +712,10 @@ static int ptrace_peek_siginfo(struct task_struct *child,
        if (arg.nr < 0)
                return -EINVAL;
 
+       /* Ensure arg.off fits in an unsigned long */
+       if (arg.off > ULONG_MAX)
+               return 0;
+
        if (arg.flags & PTRACE_PEEKSIGINFO_SHARED)
                pending = &child->signal->shared_pending;
        else
@@ -710,18 +723,20 @@ static int ptrace_peek_siginfo(struct task_struct *child,
 
        for (i = 0; i < arg.nr; ) {
                siginfo_t info;
-               s32 off = arg.off + i;
+               unsigned long off = arg.off + i;
+               bool found = false;
 
                spin_lock_irq(&child->sighand->siglock);
                list_for_each_entry(q, &pending->list, list) {
                        if (!off--) {
+                               found = true;
                                copy_siginfo(&info, &q->info);
                                break;
                        }
                }
                spin_unlock_irq(&child->sighand->siglock);
 
-               if (off >= 0) /* beyond the end of the list */
+               if (!found) /* beyond the end of the list */
                        break;
 
 #ifdef CONFIG_COMPAT
@@ -925,18 +940,26 @@ int ptrace_request(struct task_struct *child, long request,
                        ret = ptrace_setsiginfo(child, &siginfo);
                break;
 
-       case PTRACE_GETSIGMASK:
+       case PTRACE_GETSIGMASK: {
+               sigset_t *mask;
+
                if (addr != sizeof(sigset_t)) {
                        ret = -EINVAL;
                        break;
                }
 
-               if (copy_to_user(datavp, &child->blocked, sizeof(sigset_t)))
+               if (test_tsk_restore_sigmask(child))
+                       mask = &child->saved_sigmask;
+               else
+                       mask = &child->blocked;
+
+               if (copy_to_user(datavp, mask, sizeof(sigset_t)))
                        ret = -EFAULT;
                else
                        ret = 0;
 
                break;
+       }
 
        case PTRACE_SETSIGMASK: {
                sigset_t new_set;
@@ -962,6 +985,8 @@ int ptrace_request(struct task_struct *child, long request,
                child->blocked = new_set;
                spin_unlock_irq(&child->sighand->siglock);
 
+               clear_tsk_restore_sigmask(child);
+
                ret = 0;
                break;
        }