REMOVE_LINKS(child);
child->parent = child->real_parent;
SET_LINKS(child);
+
+ if (child->state == TASK_TRACED) {
+ /*
+ * Turn a tracing stop into a normal stop now,
+ * since with no tracer there would be no way
+ * to wake it up with SIGCONT or SIGKILL.
+ */
+ child->state = TASK_STOPPED;
+ }
}
/*
*/
int ptrace_check_attach(struct task_struct *child, int kill)
{
- if (!(child->ptrace & PT_PTRACED))
- return -ESRCH;
+ int ret = -ESRCH;
- if (child->parent != current)
- return -ESRCH;
+ /*
+ * We take the read lock around doing both checks to close a
+ * possible race where someone else was tracing our child and
+ * detached between these two checks. After this locked check,
+ * we are sure that this is our traced child and that can only
+ * be changed by us so it's not changing right after this.
+ */
+ read_lock(&tasklist_lock);
+ if ((child->ptrace & PT_PTRACED) && child->parent == current &&
+ child->signal != NULL) {
+ ret = 0;
+ spin_lock_irq(&child->sighand->siglock);
+ if (child->state == TASK_STOPPED) {
+ child->state = TASK_TRACED;
+ } else if (child->state != TASK_TRACED && !kill) {
+ ret = -ESRCH;
+ }
+ spin_unlock_irq(&child->sighand->siglock);
+ }
+ read_unlock(&tasklist_lock);
- if (!kill) {
- if (child->state != TASK_STOPPED)
- return -ESRCH;
+ if (!ret && !kill) {
wait_task_inactive(child);
}
/* All systems go.. */
- return 0;
+ return ret;
}
int ptrace_attach(struct task_struct *task)
if (bytes > PAGE_SIZE-offset)
bytes = PAGE_SIZE-offset;
- flush_cache_page(vma, addr);
-
maddr = kmap(page);
if (write) {
copy_to_user_page(vma, page, addr,
return ret;
}
-
-void ptrace_notify(int exit_code)
-{
- BUG_ON (!(current->ptrace & PT_PTRACED));
-
- /* Let the debugger run. */
- current->exit_code = exit_code;
- set_current_state(TASK_STOPPED);
- notify_parent(current, SIGCHLD);
- schedule();
-
- /*
- * Signals sent while we were stopped might set TIF_SIGPENDING.
- */
-
- spin_lock_irq(¤t->sighand->siglock);
- recalc_sigpending();
- spin_unlock_irq(¤t->sighand->siglock);
-}
-
-EXPORT_SYMBOL(ptrace_notify);