[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>

authored by Oleg Nesterov and committed by Linus Torvalds 5ecfbae0 dadac81b

+19 -13
+1 -1
fs/exec.c
··· 1403 1403 do_each_thread(g,p) { 1404 1404 if (mm == p->mm && p != tsk && 1405 1405 p->ptrace && p->parent->mm == mm) { 1406 - __ptrace_unlink(p); 1406 + __ptrace_detach(p, 0); 1407 1407 } 1408 1408 } while_each_thread(g,p); 1409 1409 write_unlock_irq(&tasklist_lock);
+1
include/linux/ptrace.h
··· 84 84 extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len); 85 85 extern int ptrace_attach(struct task_struct *tsk); 86 86 extern int ptrace_detach(struct task_struct *, unsigned int); 87 + extern void __ptrace_detach(struct task_struct *, unsigned int); 87 88 extern void ptrace_disable(struct task_struct *); 88 89 extern int ptrace_check_attach(struct task_struct *task, int kill); 89 90 extern int ptrace_request(struct task_struct *child, long request, long addr, long data);
+17 -12
kernel/ptrace.c
··· 72 72 */ 73 73 void __ptrace_unlink(task_t *child) 74 74 { 75 - if (!child->ptrace) 76 - BUG(); 75 + BUG_ON(!child->ptrace); 76 + 77 77 child->ptrace = 0; 78 78 if (!list_empty(&child->ptrace_list)) { 79 79 list_del_init(&child->ptrace_list); ··· 184 184 return retval; 185 185 } 186 186 187 - int ptrace_detach(struct task_struct *child, unsigned int data) 187 + void __ptrace_detach(struct task_struct *child, unsigned int data) 188 188 { 189 - if (!valid_signal(data)) 190 - return -EIO; 191 - 192 - /* Architecture-specific hardware disable .. */ 193 - ptrace_disable(child); 194 - 195 - /* .. re-parent .. */ 196 189 child->exit_code = data; 197 - 198 - write_lock_irq(&tasklist_lock); 190 + /* .. re-parent .. */ 199 191 __ptrace_unlink(child); 200 192 /* .. and wake it up. */ 201 193 if (child->exit_state != EXIT_ZOMBIE) 202 194 wake_up_process(child); 195 + } 196 + 197 + int ptrace_detach(struct task_struct *child, unsigned int data) 198 + { 199 + if (!valid_signal(data)) 200 + return -EIO; 201 + 202 + /* Architecture-specific hardware disable .. */ 203 + ptrace_disable(child); 204 + 205 + write_lock_irq(&tasklist_lock); 206 + if (child->ptrace) 207 + __ptrace_detach(child, data); 203 208 write_unlock_irq(&tasklist_lock); 204 209 205 210 return 0;