Merge tag 'upstream-3.7-rc1' of git://git.infradead.org/linux-ubifs
[linux-3.10.git] / kernel / ptrace.c
index c890ac9..a232bb5 100644 (file)
@@ -8,7 +8,7 @@
  */
 
 #include <linux/capability.h>
-#include <linux/module.h>
+#include <linux/export.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
@@ -96,9 +96,20 @@ void __ptrace_unlink(struct task_struct *child)
         */
        if (!(child->flags & PF_EXITING) &&
            (child->signal->flags & SIGNAL_STOP_STOPPED ||
-            child->signal->group_stop_count))
+            child->signal->group_stop_count)) {
                child->jobctl |= JOBCTL_STOP_PENDING;
 
+               /*
+                * This is only possible if this thread was cloned by the
+                * traced task running in the stopped group, set the signal
+                * for the future reports.
+                * FIXME: we should change ptrace_init_task() to handle this
+                * case.
+                */
+               if (!(child->jobctl & JOBCTL_STOP_SIGMASK))
+                       child->jobctl |= SIGSTOP;
+       }
+
        /*
         * If transition to TASK_STOPPED is pending or in TASK_TRACED, kick
         * @child in the butt.  Note that @resume should be used iff @child
@@ -187,15 +198,14 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
                return 0;
        rcu_read_lock();
        tcred = __task_cred(task);
-       if (cred->user->user_ns == tcred->user->user_ns &&
-           (cred->uid == tcred->euid &&
-            cred->uid == tcred->suid &&
-            cred->uid == tcred->uid  &&
-            cred->gid == tcred->egid &&
-            cred->gid == tcred->sgid &&
-            cred->gid == tcred->gid))
+       if (uid_eq(cred->uid, tcred->euid) &&
+           uid_eq(cred->uid, tcred->suid) &&
+           uid_eq(cred->uid, tcred->uid)  &&
+           gid_eq(cred->gid, tcred->egid) &&
+           gid_eq(cred->gid, tcred->sgid) &&
+           gid_eq(cred->gid, tcred->gid))
                goto ok;
-       if (ptrace_has_cap(tcred->user->user_ns, mode))
+       if (ptrace_has_cap(tcred->user_ns, mode))
                goto ok;
        rcu_read_unlock();
        return -EPERM;
@@ -220,26 +230,22 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode)
 }
 
 static int ptrace_attach(struct task_struct *task, long request,
+                        unsigned long addr,
                         unsigned long flags)
 {
        bool seize = (request == PTRACE_SEIZE);
        int retval;
 
-       /*
-        * SEIZE will enable new ptrace behaviors which will be implemented
-        * gradually.  SEIZE_DEVEL is used to prevent applications
-        * expecting full SEIZE behaviors trapping on kernel commits which
-        * are still in the process of implementing them.
-        *
-        * Only test programs for new ptrace behaviors being implemented
-        * should set SEIZE_DEVEL.  If unset, SEIZE will fail with -EIO.
-        *
-        * Once SEIZE behaviors are completely implemented, this flag and
-        * the following test will be removed.
-        */
        retval = -EIO;
-       if (seize && !(flags & PTRACE_SEIZE_DEVEL))
-               goto out;
+       if (seize) {
+               if (addr != 0)
+                       goto out;
+               if (flags & ~(unsigned long)PTRACE_O_MASK)
+                       goto out;
+               flags = PT_PTRACED | PT_SEIZED | (flags << PT_OPT_FLAG_SHIFT);
+       } else {
+               flags = PT_PTRACED;
+       }
 
        audit_ptrace(task);
 
@@ -251,7 +257,7 @@ static int ptrace_attach(struct task_struct *task, long request,
 
        /*
         * Protect exec's credential calculations against our interference;
-        * interference; SUID, SGID and LSM creds get determined differently
+        * SUID, SGID and LSM creds get determined differently
         * under ptrace.
         */
        retval = -ERESTARTNOINTR;
@@ -271,11 +277,11 @@ static int ptrace_attach(struct task_struct *task, long request,
        if (task->ptrace)
                goto unlock_tasklist;
 
-       task->ptrace = PT_PTRACED;
        if (seize)
-               task->ptrace |= PT_SEIZED;
+               flags |= PT_SEIZED;
        if (ns_capable(task_user_ns(task), CAP_SYS_PTRACE))
-               task->ptrace |= PT_PTRACE_CAP;
+               flags |= PT_PTRACE_CAP;
+       task->ptrace = flags;
 
        __ptrace_link(task, current);
 
@@ -517,30 +523,18 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds
 
 static int ptrace_setoptions(struct task_struct *child, unsigned long data)
 {
-       child->ptrace &= ~PT_TRACE_MASK;
-
-       if (data & PTRACE_O_TRACESYSGOOD)
-               child->ptrace |= PT_TRACESYSGOOD;
-
-       if (data & PTRACE_O_TRACEFORK)
-               child->ptrace |= PT_TRACE_FORK;
-
-       if (data & PTRACE_O_TRACEVFORK)
-               child->ptrace |= PT_TRACE_VFORK;
+       unsigned flags;
 
-       if (data & PTRACE_O_TRACECLONE)
-               child->ptrace |= PT_TRACE_CLONE;
-
-       if (data & PTRACE_O_TRACEEXEC)
-               child->ptrace |= PT_TRACE_EXEC;
-
-       if (data & PTRACE_O_TRACEVFORKDONE)
-               child->ptrace |= PT_TRACE_VFORK_DONE;
+       if (data & ~(unsigned long)PTRACE_O_MASK)
+               return -EINVAL;
 
-       if (data & PTRACE_O_TRACEEXIT)
-               child->ptrace |= PT_TRACE_EXIT;
+       /* Avoid intermediate state when all opts are cleared */
+       flags = child->ptrace;
+       flags &= ~(PTRACE_O_MASK << PT_OPT_FLAG_SHIFT);
+       flags |= (data << PT_OPT_FLAG_SHIFT);
+       child->ptrace = flags;
 
-       return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
+       return 0;
 }
 
 static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
@@ -880,7 +874,7 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
        }
 
        if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
-               ret = ptrace_attach(child, request, data);
+               ret = ptrace_attach(child, request, addr, data);
                /*
                 * Some architectures need to do book-keeping after
                 * a ptrace attach.
@@ -1023,7 +1017,7 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
        }
 
        if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) {
-               ret = ptrace_attach(child, request, data);
+               ret = ptrace_attach(child, request, addr, data);
                /*
                 * Some architectures need to do book-keeping after
                 * a ptrace attach.