ipmr/ip6mr: Initialize the last assert time of mfc entries.
[pandora-kernel.git] / kernel / ptrace.c
index a70d2a5..f07c144 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
@@ -106,11 +117,45 @@ void __ptrace_unlink(struct task_struct *child)
         * TASK_KILLABLE sleeps.
         */
        if (child->jobctl & JOBCTL_STOP_PENDING || task_is_traced(child))
-               signal_wake_up(child, task_is_traced(child));
+               ptrace_signal_wake_up(child, true);
 
        spin_unlock(&child->sighand->siglock);
 }
 
+/* Ensure that nothing can wake it up, even SIGKILL */
+static bool ptrace_freeze_traced(struct task_struct *task)
+{
+       bool ret = false;
+
+       /* Lockless, nobody but us can set this flag */
+       if (task->jobctl & JOBCTL_LISTENING)
+               return ret;
+
+       spin_lock_irq(&task->sighand->siglock);
+       if (task_is_traced(task) && !__fatal_signal_pending(task)) {
+               task->state = __TASK_TRACED;
+               ret = true;
+       }
+       spin_unlock_irq(&task->sighand->siglock);
+
+       return ret;
+}
+
+static void ptrace_unfreeze_traced(struct task_struct *task)
+{
+       if (task->state != __TASK_TRACED)
+               return;
+
+       WARN_ON(!task->ptrace || task->parent != current);
+
+       spin_lock_irq(&task->sighand->siglock);
+       if (__fatal_signal_pending(task))
+               wake_up_state(task, __TASK_TRACED);
+       else
+               task->state = TASK_TRACED;
+       spin_unlock_irq(&task->sighand->siglock);
+}
+
 /**
  * ptrace_check_attach - check whether ptracee is ready for ptrace operation
  * @child: ptracee to check for
@@ -140,24 +185,29 @@ int ptrace_check_attach(struct task_struct *child, bool ignore_state)
         * be changed by us so it's not changing right after this.
         */
        read_lock(&tasklist_lock);
-       if ((child->ptrace & PT_PTRACED) && child->parent == current) {
+       if (child->ptrace && child->parent == current) {
+               WARN_ON(child->state == __TASK_TRACED);
                /*
                 * child->sighand can't be NULL, release_task()
                 * does ptrace_unlink() before __exit_signal().
                 */
-               spin_lock_irq(&child->sighand->siglock);
-               WARN_ON_ONCE(task_is_stopped(child));
-               if (ignore_state || (task_is_traced(child) &&
-                                    !(child->jobctl & JOBCTL_LISTENING)))
+               if (ignore_state || ptrace_freeze_traced(child))
                        ret = 0;
-               spin_unlock_irq(&child->sighand->siglock);
        }
        read_unlock(&tasklist_lock);
 
-       if (!ret && !ignore_state)
-               ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
+       if (!ret && !ignore_state) {
+               if (!wait_task_inactive(child, __TASK_TRACED)) {
+                       /*
+                        * This can only happen if may_ptrace_stop() fails and
+                        * ptrace_stop() changes ->state back to TASK_RUNNING,
+                        * so we should not worry about leaking __TASK_TRACED.
+                        */
+                       WARN_ON(child->state == __TASK_TRACED);
+                       ret = -ESRCH;
+               }
+       }
 
-       /* All systems go.. */
        return ret;
 }
 
@@ -175,7 +225,7 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
         */
        int dumpable = 0;
        /* Don't let security modules deny introspection */
-       if (task == current)
+       if (same_thread_group(task, current))
                return 0;
        rcu_read_lock();
        tcred = __task_cred(task);
@@ -196,7 +246,8 @@ ok:
        smp_rmb();
        if (task->mm)
                dumpable = get_dumpable(task->mm);
-       if (!dumpable && !task_ns_capable(task, CAP_SYS_PTRACE))
+       if (dumpable != SUID_DUMP_USER &&
+           !task_ns_capable(task, CAP_SYS_PTRACE))
                return -EPERM;
 
        return security_ptrace_access_check(task, mode);
@@ -296,7 +347,7 @@ static int ptrace_attach(struct task_struct *task, long request,
         */
        if (task_is_stopped(task) &&
            task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))
-               signal_wake_up(task, 1);
+               signal_wake_up_state(task, __TASK_STOPPED);
 
        spin_unlock(&task->sighand->siglock);
 
@@ -589,6 +640,8 @@ static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
 static int ptrace_resume(struct task_struct *child, long request,
                         unsigned long data)
 {
+       bool need_siglock;
+
        if (!valid_signal(data))
                return -EIO;
 
@@ -616,8 +669,26 @@ static int ptrace_resume(struct task_struct *child, long request,
                user_disable_single_step(child);
        }
 
+       /*
+        * Change ->exit_code and ->state under siglock to avoid the race
+        * with wait_task_stopped() in between; a non-zero ->exit_code will
+        * wrongly look like another report from tracee.
+        *
+        * Note that we need siglock even if ->exit_code == data and/or this
+        * status was not reported yet, the new status must not be cleared by
+        * wait_task_stopped() after resume.
+        *
+        * If data == 0 we do not care if wait_task_stopped() reports the old
+        * status and clears the code too; this can't race with the tracee, it
+        * takes siglock after resume.
+        */
+       need_siglock = data && !thread_group_empty(current);
+       if (need_siglock)
+               spin_lock_irq(&child->sighand->siglock);
        child->exit_code = data;
        wake_up_state(child, __TASK_TRACED);
+       if (need_siglock)
+               spin_unlock_irq(&child->sighand->siglock);
 
        return 0;
 }
@@ -725,7 +796,7 @@ int ptrace_request(struct task_struct *child, long request,
                 * tracee into STOP.
                 */
                if (likely(task_set_jobctl_pending(child, JOBCTL_TRAP_STOP)))
-                       signal_wake_up(child, child->jobctl & JOBCTL_LISTENING);
+                       ptrace_signal_wake_up(child, child->jobctl & JOBCTL_LISTENING);
 
                unlock_task_sighand(child, &flags);
                ret = 0;
@@ -751,7 +822,7 @@ int ptrace_request(struct task_struct *child, long request,
                         * start of this trap and now.  Trigger re-trap.
                         */
                        if (child->jobctl & JOBCTL_TRAP_NOTIFY)
-                               signal_wake_up(child, true);
+                               ptrace_signal_wake_up(child, true);
                        ret = 0;
                }
                unlock_task_sighand(child, &flags);
@@ -888,6 +959,8 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr,
                goto out_put_task_struct;
 
        ret = arch_ptrace(child, request, addr, data);
+       if (ret || request != PTRACE_DETACH)
+               ptrace_unfreeze_traced(child);
 
  out_put_task_struct:
        put_task_struct(child);
@@ -1027,8 +1100,11 @@ asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
 
        ret = ptrace_check_attach(child, request == PTRACE_KILL ||
                                  request == PTRACE_INTERRUPT);
-       if (!ret)
+       if (!ret) {
                ret = compat_arch_ptrace(child, request, addr, data);
+               if (ret || request != PTRACE_DETACH)
+                       ptrace_unfreeze_traced(child);
+       }
 
  out_put_task_struct:
        put_task_struct(child);