Commit f470021a authored by Roland McGrath's avatar Roland McGrath

ptrace children revamp

ptrace no longer fiddles with the children/sibling links, and the
old ptrace_children list is gone.  Now ptrace, whether of one's own
children or another's via PTRACE_ATTACH, just uses the new ptraced
list instead.

There should be no user-visible difference that matters.  The only
change is the order in which do_wait() sees multiple stopped
children and stopped ptrace attachees.  Since wait_task_stopped()
was changed earlier so it no longer reorders the children list, we
already know this won't cause any new problems.
Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
parent 98abed02
...@@ -140,8 +140,8 @@ extern struct group_info init_groups; ...@@ -140,8 +140,8 @@ extern struct group_info init_groups;
.nr_cpus_allowed = NR_CPUS, \ .nr_cpus_allowed = NR_CPUS, \
}, \ }, \
.tasks = LIST_HEAD_INIT(tsk.tasks), \ .tasks = LIST_HEAD_INIT(tsk.tasks), \
.ptrace_children= LIST_HEAD_INIT(tsk.ptrace_children), \ .ptraced = LIST_HEAD_INIT(tsk.ptraced), \
.ptrace_list = LIST_HEAD_INIT(tsk.ptrace_list), \ .ptrace_entry = LIST_HEAD_INIT(tsk.ptrace_entry), \
.real_parent = &tsk, \ .real_parent = &tsk, \
.parent = &tsk, \ .parent = &tsk, \
.children = LIST_HEAD_INIT(tsk.children), \ .children = LIST_HEAD_INIT(tsk.children), \
......
...@@ -1062,12 +1062,6 @@ struct task_struct { ...@@ -1062,12 +1062,6 @@ struct task_struct {
#endif #endif
struct list_head tasks; struct list_head tasks;
/*
* ptrace_list/ptrace_children forms the list of my children
* that were stolen by a ptracer.
*/
struct list_head ptrace_children;
struct list_head ptrace_list;
struct mm_struct *mm, *active_mm; struct mm_struct *mm, *active_mm;
...@@ -1089,18 +1083,25 @@ struct task_struct { ...@@ -1089,18 +1083,25 @@ struct task_struct {
/* /*
* pointers to (original) parent process, youngest child, younger sibling, * pointers to (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with * older sibling, respectively. (p->father can be replaced with
* p->parent->pid) * p->real_parent->pid)
*/ */
struct task_struct *real_parent; /* real parent process (when being debugged) */ struct task_struct *real_parent; /* real parent process */
struct task_struct *parent; /* parent process */ struct task_struct *parent; /* recipient of SIGCHLD, wait4() reports */
/* /*
* children/sibling forms the list of my children plus the * children/sibling forms the list of my natural children
* tasks I'm ptracing.
*/ */
struct list_head children; /* list of my children */ struct list_head children; /* list of my children */
struct list_head sibling; /* linkage in my parent's children list */ struct list_head sibling; /* linkage in my parent's children list */
struct task_struct *group_leader; /* threadgroup leader */ struct task_struct *group_leader; /* threadgroup leader */
/*
* ptraced is the list of tasks this task is using ptrace on.
* This includes both natural children and PTRACE_ATTACH targets.
* p->ptrace_entry is p's link on the p->parent->ptraced list.
*/
struct list_head ptraced;
struct list_head ptrace_entry;
/* PID/PID hash table linkage. */ /* PID/PID hash table linkage. */
struct pid_link pids[PIDTYPE_MAX]; struct pid_link pids[PIDTYPE_MAX];
struct list_head thread_group; struct list_head thread_group;
...@@ -1876,9 +1877,6 @@ extern void wait_task_inactive(struct task_struct * p); ...@@ -1876,9 +1877,6 @@ extern void wait_task_inactive(struct task_struct * p);
#define wait_task_inactive(p) do { } while (0) #define wait_task_inactive(p) do { } while (0)
#endif #endif
#define remove_parent(p) list_del_init(&(p)->sibling)
#define add_parent(p) list_add_tail(&(p)->sibling,&(p)->parent->children)
#define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks) #define next_task(p) list_entry(rcu_dereference((p)->tasks.next), struct task_struct, tasks)
#define for_each_process(p) \ #define for_each_process(p) \
......
This diff is collapsed.
...@@ -1125,8 +1125,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1125,8 +1125,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
*/ */
p->group_leader = p; p->group_leader = p;
INIT_LIST_HEAD(&p->thread_group); INIT_LIST_HEAD(&p->thread_group);
INIT_LIST_HEAD(&p->ptrace_children); INIT_LIST_HEAD(&p->ptrace_entry);
INIT_LIST_HEAD(&p->ptrace_list); INIT_LIST_HEAD(&p->ptraced);
/* Now that the task is set up, run cgroup callbacks if /* Now that the task is set up, run cgroup callbacks if
* necessary. We need to run them before the task is visible * necessary. We need to run them before the task is visible
...@@ -1198,7 +1198,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1198,7 +1198,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
} }
if (likely(p->pid)) { if (likely(p->pid)) {
add_parent(p); list_add_tail(&p->sibling, &p->real_parent->children);
if (unlikely(p->ptrace & PT_PTRACED)) if (unlikely(p->ptrace & PT_PTRACED))
__ptrace_link(p, current->parent); __ptrace_link(p, current->parent);
......
...@@ -33,13 +33,9 @@ ...@@ -33,13 +33,9 @@
*/ */
void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
{ {
BUG_ON(!list_empty(&child->ptrace_list)); BUG_ON(!list_empty(&child->ptrace_entry));
if (child->parent == new_parent) list_add(&child->ptrace_entry, &new_parent->ptraced);
return;
list_add(&child->ptrace_list, &child->parent->ptrace_children);
remove_parent(child);
child->parent = new_parent; child->parent = new_parent;
add_parent(child);
} }
/* /*
...@@ -73,12 +69,8 @@ void __ptrace_unlink(struct task_struct *child) ...@@ -73,12 +69,8 @@ void __ptrace_unlink(struct task_struct *child)
BUG_ON(!child->ptrace); BUG_ON(!child->ptrace);
child->ptrace = 0; child->ptrace = 0;
if (ptrace_reparented(child)) {
list_del_init(&child->ptrace_list);
remove_parent(child);
child->parent = child->real_parent; child->parent = child->real_parent;
add_parent(child); list_del_init(&child->ptrace_entry);
}
if (task_is_traced(child)) if (task_is_traced(child))
ptrace_untrace(child); ptrace_untrace(child);
...@@ -492,15 +484,34 @@ int ptrace_traceme(void) ...@@ -492,15 +484,34 @@ int ptrace_traceme(void)
/* /*
* Are we already being traced? * Are we already being traced?
*/ */
repeat:
task_lock(current); task_lock(current);
if (!(current->ptrace & PT_PTRACED)) { if (!(current->ptrace & PT_PTRACED)) {
/*
* See ptrace_attach() comments about the locking here.
*/
unsigned long flags;
if (!write_trylock_irqsave(&tasklist_lock, flags)) {
task_unlock(current);
do {
cpu_relax();
} while (!write_can_lock(&tasklist_lock));
goto repeat;
}
ret = security_ptrace(current->parent, current, ret = security_ptrace(current->parent, current,
PTRACE_MODE_ATTACH); PTRACE_MODE_ATTACH);
/* /*
* Set the ptrace bit in the process ptrace flags. * Set the ptrace bit in the process ptrace flags.
* Then link us on our parent's ptraced list.
*/ */
if (!ret) if (!ret) {
current->ptrace |= PT_PTRACED; current->ptrace |= PT_PTRACED;
__ptrace_link(current, current->real_parent);
}
write_unlock_irqrestore(&tasklist_lock, flags);
} }
task_unlock(current); task_unlock(current);
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment