diff options
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/exit.c | 20 | ||||
| -rw-r--r-- | kernel/ptrace.c | 5 |
2 files changed, 21 insertions, 4 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 426d3ae722ba..ca9a9e21c444 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1280,6 +1280,22 @@ static int wait_task_continued(task_t *p, int noreap, } +static inline int my_ptrace_child(struct task_struct *p) +{ + if (!(p->ptrace & PT_PTRACED)) + return 0; + if (!(p->ptrace & PT_ATTACHED)) + return 1; + /* + * This child was PTRACE_ATTACH'd. We should be seeing it only if + * we are the attacher. If we are the real parent, this is a race + * inside ptrace_attach. It is waiting for the tasklist_lock, + * which we have to switch the parent links, but has already set + * the flags in p->ptrace. + */ + return (p->parent != p->real_parent); +} + static long do_wait(pid_t pid, int options, struct siginfo __user *infop, int __user *stat_addr, struct rusage __user *ru) { @@ -1308,12 +1324,12 @@ repeat: switch (p->state) { case TASK_TRACED: - if (!(p->ptrace & PT_PTRACED)) + if (!my_ptrace_child(p)) continue; /*FALLTHROUGH*/ case TASK_STOPPED: if (!(options & WUNTRACED) && - !(p->ptrace & PT_PTRACED)) + !my_ptrace_child(p)) continue; retval = wait_task_stopped(p, ret == 2, (options & WNOWAIT), diff --git a/kernel/ptrace.c b/kernel/ptrace.c index b14b4a467729..09ba057222c3 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -82,7 +82,8 @@ int ptrace_check_attach(struct task_struct *child, int kill) */ read_lock(&tasklist_lock); if ((child->ptrace & PT_PTRACED) && child->parent == current && - child->signal != NULL) { + (!(child->ptrace & PT_ATTACHED) || child->real_parent != current) + && child->signal != NULL) { ret = 0; spin_lock_irq(&child->sighand->siglock); if (child->state == TASK_STOPPED) { @@ -131,7 +132,7 @@ int ptrace_attach(struct task_struct *task) goto bad; /* Go */ - task->ptrace |= PT_PTRACED; + task->ptrace |= PT_PTRACED | PT_ATTACHED; if (capable(CAP_SYS_PTRACE)) task->ptrace |= PT_PTRACE_CAP; task_unlock(task); |
