Commit 5ecfbae093f0c37311e89b29bfc0c9d586eace87

Authored by Oleg Nesterov
Committed by Linus Torvalds
1 parent dadac81b1b

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

Showing 3 changed files with 17 additions and 11 deletions Side-by-side Diff

... ... @@ -1403,7 +1403,7 @@
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);
include/linux/ptrace.h
... ... @@ -84,6 +84,7 @@
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);
... ... @@ -72,8 +72,8 @@
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);
80 80  
81 81  
82 82  
... ... @@ -184,22 +184,27 @@
184 184 return retval;
185 185 }
186 186  
  187 +void __ptrace_detach(struct task_struct *child, unsigned int data)
  188 +{
  189 + child->exit_code = data;
  190 + /* .. re-parent .. */
  191 + __ptrace_unlink(child);
  192 + /* .. and wake it up. */
  193 + if (child->exit_state != EXIT_ZOMBIE)
  194 + wake_up_process(child);
  195 +}
  196 +
187 197 int ptrace_detach(struct task_struct *child, unsigned int data)
188 198 {
189 199 if (!valid_signal(data))
190   - return -EIO;
  200 + return -EIO;
191 201  
192 202 /* Architecture-specific hardware disable .. */
193 203 ptrace_disable(child);
194 204  
195   - /* .. re-parent .. */
196   - child->exit_code = data;
197   -
198 205 write_lock_irq(&tasklist_lock);
199   - __ptrace_unlink(child);
200   - /* .. and wake it up. */
201   - if (child->exit_state != EXIT_ZOMBIE)
202   - wake_up_process(child);
  206 + if (child->ptrace)
  207 + __ptrace_detach(child, data);
203 208 write_unlock_irq(&tasklist_lock);
204 209  
205 210 return 0;