--- /dev/null
+From nobody Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@tv-sign.ru>
+Date: Wed Feb 15 22:50:10 2006 +0300
+Subject: [PATCH] fix zap_thread's ptrace related problems
+
+1. The tracee can go from ptrace_stop() to do_signal_stop()
+ after __ptrace_unlink(p).
+
+2. It is unsafe to __ptrace_unlink(p) while p->parent may wait
+ for tasklist_lock in ptrace_detach().
+
+Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
+Cc: Roland McGrath <roland@redhat.com>
+Cc: Ingo Molnar <mingo@elte.hu>
+Cc: Christoph Hellwig <hch@lst.de>
+Cc: Eric W. Biederman <ebiederm@xmission.com>
+Signed-off-by: Linus Torvalds <torvalds@osdl.org>
+Signed-off-by: Chris Wright <chrisw@sous-sol.org>
+---
+
+ fs/exec.c | 2 +-
+ include/linux/ptrace.h | 1 +
+ kernel/ptrace.c | 25 +++++++++++++++----------
+ 3 files changed, 17 insertions(+), 11 deletions(-)
+
+--- linux-2.6.15.4.orig/fs/exec.c
++++ linux-2.6.15.4/fs/exec.c
+@@ -1403,7 +1403,7 @@ static void zap_threads (struct mm_struc
+ do_each_thread(g,p) {
+ if (mm == p->mm && p != tsk &&
+ p->ptrace && p->parent->mm == mm) {
+- __ptrace_unlink(p);
++ __ptrace_detach(p, 0);
+ }
+ } while_each_thread(g,p);
+ write_unlock_irq(&tasklist_lock);
+--- linux-2.6.15.4.orig/include/linux/ptrace.h
++++ linux-2.6.15.4/include/linux/ptrace.h
+@@ -84,6 +84,7 @@ extern int ptrace_readdata(struct task_s
+ extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len);
+ extern int ptrace_attach(struct task_struct *tsk);
+ extern int ptrace_detach(struct task_struct *, unsigned int);
++extern void __ptrace_detach(struct task_struct *, unsigned int);
+ extern void ptrace_disable(struct task_struct *);
+ extern int ptrace_check_attach(struct task_struct *task, int kill);
+ extern int ptrace_request(struct task_struct *child, long request, long addr, long data);
+--- linux-2.6.15.4.orig/kernel/ptrace.c
++++ linux-2.6.15.4/kernel/ptrace.c
+@@ -71,8 +71,8 @@ void ptrace_untrace(task_t *child)
+ */
+ void __ptrace_unlink(task_t *child)
+ {
+- if (!child->ptrace)
+- BUG();
++ BUG_ON(!child->ptrace);
++
+ child->ptrace = 0;
+ if (!list_empty(&child->ptrace_list)) {
+ list_del_init(&child->ptrace_list);
+@@ -183,22 +183,27 @@ bad:
+ return retval;
+ }
+
++void __ptrace_detach(struct task_struct *child, unsigned int data)
++{
++ child->exit_code = data;
++ /* .. re-parent .. */
++ __ptrace_unlink(child);
++ /* .. and wake it up. */
++ if (child->exit_state != EXIT_ZOMBIE)
++ wake_up_process(child);
++}
++
+ int ptrace_detach(struct task_struct *child, unsigned int data)
+ {
+ if (!valid_signal(data))
+- return -EIO;
++ return -EIO;
+
+ /* Architecture-specific hardware disable .. */
+ ptrace_disable(child);
+
+- /* .. re-parent .. */
+- child->exit_code = data;
+-
+ write_lock_irq(&tasklist_lock);
+- __ptrace_unlink(child);
+- /* .. and wake it up. */
+- if (child->exit_state != EXIT_ZOMBIE)
+- wake_up_process(child);
++ if (child->ptrace)
++ __ptrace_detach(child, data);
+ write_unlock_irq(&tasklist_lock);
+
+ return 0;