]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - kernel/ptrace.c
Merge branch 'core-cleanups-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-omap-h63xx.git] / kernel / ptrace.c
index 137955913e7e74bfea149c480e74fb7fd6ca63d5..aaad0ec341948690d8d8cad7ad22ee0600035fad 100644 (file)
@@ -60,11 +60,15 @@ static void ptrace_untrace(struct task_struct *child)
 {
        spin_lock(&child->sighand->siglock);
        if (task_is_traced(child)) {
-               if (child->signal->flags & SIGNAL_STOP_STOPPED) {
+               /*
+                * If the group stop is completed or in progress,
+                * this thread was already counted as stopped.
+                */
+               if (child->signal->flags & SIGNAL_STOP_STOPPED ||
+                   child->signal->group_stop_count)
                        __set_task_state(child, TASK_STOPPED);
-               } else {
+               else
                        signal_wake_up(child, 1);
-               }
        }
        spin_unlock(&child->sighand->siglock);
 }
@@ -235,18 +239,58 @@ out:
        return retval;
 }
 
-static inline void __ptrace_detach(struct task_struct *child, unsigned int data)
+/*
+ * Called with irqs disabled, returns true if childs should reap themselves.
+ */
+static int ignoring_children(struct sighand_struct *sigh)
 {
-       child->exit_code = data;
-       /* .. re-parent .. */
-       __ptrace_unlink(child);
-       /* .. and wake it up. */
-       if (child->exit_state != EXIT_ZOMBIE)
-               wake_up_process(child);
+       int ret;
+       spin_lock(&sigh->siglock);
+       ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
+             (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
+       spin_unlock(&sigh->siglock);
+       return ret;
+}
+
+/*
+ * Called with tasklist_lock held for writing.
+ * Unlink a traced task, and clean it up if it was a traced zombie.
+ * Return true if it needs to be reaped with release_task().
+ * (We can't call release_task() here because we already hold tasklist_lock.)
+ *
+ * If it's a zombie, our attachedness prevented normal parent notification
+ * or self-reaping.  Do notification now if it would have happened earlier.
+ * If it should reap itself, return true.
+ *
+ * If it's our own child, there is no notification to do.
+ * But if our normal children self-reap, then this child
+ * was prevented by ptrace and we must reap it now.
+ */
+static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
+{
+       __ptrace_unlink(p);
+
+       if (p->exit_state == EXIT_ZOMBIE) {
+               if (!task_detached(p) && thread_group_empty(p)) {
+                       if (!same_thread_group(p->real_parent, tracer))
+                               do_notify_parent(p, p->exit_signal);
+                       else if (ignoring_children(tracer->sighand))
+                               p->exit_signal = -1;
+               }
+               if (task_detached(p)) {
+                       /* Mark it as in the process of being reaped. */
+                       p->exit_state = EXIT_DEAD;
+                       return true;
+               }
+       }
+
+       return false;
 }
 
 int ptrace_detach(struct task_struct *child, unsigned int data)
 {
+       bool dead = false;
+
        if (!valid_signal(data))
                return -EIO;
 
@@ -255,14 +299,45 @@ int ptrace_detach(struct task_struct *child, unsigned int data)
        clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 
        write_lock_irq(&tasklist_lock);
-       /* protect against de_thread()->release_task() */
-       if (child->ptrace)
-               __ptrace_detach(child, data);
+       /*
+        * This child can be already killed. Make sure de_thread() or
+        * our sub-thread doing do_wait() didn't do release_task() yet.
+        */
+       if (child->ptrace) {
+               child->exit_code = data;
+               dead = __ptrace_detach(current, child);
+       }
        write_unlock_irq(&tasklist_lock);
 
+       if (unlikely(dead))
+               release_task(child);
+
        return 0;
 }
 
+/*
+ * Detach all tasks we were using ptrace on.
+ */
+void exit_ptrace(struct task_struct *tracer)
+{
+       struct task_struct *p, *n;
+       LIST_HEAD(ptrace_dead);
+
+       write_lock_irq(&tasklist_lock);
+       list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) {
+               if (__ptrace_detach(tracer, p))
+                       list_add(&p->ptrace_entry, &ptrace_dead);
+       }
+       write_unlock_irq(&tasklist_lock);
+
+       BUG_ON(!list_empty(&tracer->ptraced));
+
+       list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) {
+               list_del_init(&p->ptrace_entry);
+               release_task(p);
+       }
+}
+
 int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
 {
        int copied = 0;