diff options
author | Roland McGrath <roland@redhat.com> | 2008-03-24 21:36:23 -0400 |
---|---|---|
committer | Roland McGrath <roland@redhat.com> | 2008-07-16 21:02:33 -0400 |
commit | f470021adb9190819c03d6d8c5c860a17480aa6d (patch) | |
tree | 9e5c2808138624e272b562a502cfd035ae59c268 /kernel | |
parent | 98abed02007b19bbfd68b6d06a5485afc3eeb01b (diff) |
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: Roland McGrath <roland@redhat.com>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/exit.c | 226 | ||||
-rw-r--r-- | kernel/fork.c | 6 | ||||
-rw-r--r-- | kernel/ptrace.c | 37 |
3 files changed, 146 insertions, 123 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index 7453356a961f..1e909826a804 100644 --- a/kernel/exit.c +++ b/kernel/exit.c | |||
@@ -71,7 +71,7 @@ static void __unhash_process(struct task_struct *p) | |||
71 | __get_cpu_var(process_counts)--; | 71 | __get_cpu_var(process_counts)--; |
72 | } | 72 | } |
73 | list_del_rcu(&p->thread_group); | 73 | list_del_rcu(&p->thread_group); |
74 | remove_parent(p); | 74 | list_del_init(&p->sibling); |
75 | } | 75 | } |
76 | 76 | ||
77 | /* | 77 | /* |
@@ -152,6 +152,18 @@ static void delayed_put_task_struct(struct rcu_head *rhp) | |||
152 | put_task_struct(container_of(rhp, struct task_struct, rcu)); | 152 | put_task_struct(container_of(rhp, struct task_struct, rcu)); |
153 | } | 153 | } |
154 | 154 | ||
155 | /* | ||
156 | * Do final ptrace-related cleanup of a zombie being reaped. | ||
157 | * | ||
158 | * Called with write_lock(&tasklist_lock) held. | ||
159 | */ | ||
160 | static void ptrace_release_task(struct task_struct *p) | ||
161 | { | ||
162 | BUG_ON(!list_empty(&p->ptraced)); | ||
163 | ptrace_unlink(p); | ||
164 | BUG_ON(!list_empty(&p->ptrace_entry)); | ||
165 | } | ||
166 | |||
155 | void release_task(struct task_struct * p) | 167 | void release_task(struct task_struct * p) |
156 | { | 168 | { |
157 | struct task_struct *leader; | 169 | struct task_struct *leader; |
@@ -160,8 +172,7 @@ repeat: | |||
160 | atomic_dec(&p->user->processes); | 172 | atomic_dec(&p->user->processes); |
161 | proc_flush_task(p); | 173 | proc_flush_task(p); |
162 | write_lock_irq(&tasklist_lock); | 174 | write_lock_irq(&tasklist_lock); |
163 | ptrace_unlink(p); | 175 | ptrace_release_task(p); |
164 | BUG_ON(!list_empty(&p->ptrace_list) || !list_empty(&p->ptrace_children)); | ||
165 | __exit_signal(p); | 176 | __exit_signal(p); |
166 | 177 | ||
167 | /* | 178 | /* |
@@ -315,9 +326,8 @@ static void reparent_to_kthreadd(void) | |||
315 | 326 | ||
316 | ptrace_unlink(current); | 327 | ptrace_unlink(current); |
317 | /* Reparent to init */ | 328 | /* Reparent to init */ |
318 | remove_parent(current); | ||
319 | current->real_parent = current->parent = kthreadd_task; | 329 | current->real_parent = current->parent = kthreadd_task; |
320 | add_parent(current); | 330 | list_move_tail(¤t->sibling, ¤t->real_parent->children); |
321 | 331 | ||
322 | /* Set the exit signal to SIGCHLD so we signal init on exit */ | 332 | /* Set the exit signal to SIGCHLD so we signal init on exit */ |
323 | current->exit_signal = SIGCHLD; | 333 | current->exit_signal = SIGCHLD; |
@@ -692,37 +702,71 @@ static void exit_mm(struct task_struct * tsk) | |||
692 | mmput(mm); | 702 | mmput(mm); |
693 | } | 703 | } |
694 | 704 | ||
695 | static void | 705 | /* |
696 | reparent_thread(struct task_struct *p, struct task_struct *father, int traced) | 706 | * Detach all tasks we were using ptrace on. |
707 | * Any that need to be release_task'd are put on the @dead list. | ||
708 | * | ||
709 | * Called with write_lock(&tasklist_lock) held. | ||
710 | */ | ||
711 | static void ptrace_exit(struct task_struct *parent, struct list_head *dead) | ||
697 | { | 712 | { |
698 | if (p->pdeath_signal) | 713 | struct task_struct *p, *n; |
699 | /* We already hold the tasklist_lock here. */ | ||
700 | group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); | ||
701 | 714 | ||
702 | /* Move the child from its dying parent to the new one. */ | 715 | list_for_each_entry_safe(p, n, &parent->ptraced, ptrace_entry) { |
703 | if (unlikely(traced)) { | 716 | __ptrace_unlink(p); |
704 | /* Preserve ptrace links if someone else is tracing this child. */ | 717 | |
705 | list_del_init(&p->ptrace_list); | 718 | if (p->exit_state != EXIT_ZOMBIE) |
706 | if (ptrace_reparented(p)) | 719 | continue; |
707 | list_add(&p->ptrace_list, &p->real_parent->ptrace_children); | 720 | |
708 | } else { | 721 | /* |
709 | /* If this child is being traced, then we're the one tracing it | 722 | * If it's a zombie, our attachedness prevented normal |
710 | * anyway, so let go of it. | 723 | * parent notification or self-reaping. Do notification |
724 | * now if it would have happened earlier. If it should | ||
725 | * reap itself, add it to the @dead list. We can't call | ||
726 | * release_task() here because we already hold tasklist_lock. | ||
727 | * | ||
728 | * If it's our own child, there is no notification to do. | ||
711 | */ | 729 | */ |
712 | p->ptrace = 0; | 730 | if (!task_detached(p) && thread_group_empty(p)) { |
713 | remove_parent(p); | 731 | if (!same_thread_group(p->real_parent, parent)) |
714 | p->parent = p->real_parent; | 732 | do_notify_parent(p, p->exit_signal); |
715 | add_parent(p); | 733 | } |
716 | 734 | ||
717 | if (task_is_traced(p)) { | 735 | if (task_detached(p)) { |
718 | /* | 736 | /* |
719 | * If it was at a trace stop, turn it into | 737 | * Mark it as in the process of being reaped. |
720 | * a normal stop since it's no longer being | ||
721 | * traced. | ||
722 | */ | 738 | */ |
723 | ptrace_untrace(p); | 739 | p->exit_state = EXIT_DEAD; |
740 | list_add(&p->ptrace_entry, dead); | ||
724 | } | 741 | } |
725 | } | 742 | } |
743 | } | ||
744 | |||
745 | /* | ||
746 | * Finish up exit-time ptrace cleanup. | ||
747 | * | ||
748 | * Called without locks. | ||
749 | */ | ||
750 | static void ptrace_exit_finish(struct task_struct *parent, | ||
751 | struct list_head *dead) | ||
752 | { | ||
753 | struct task_struct *p, *n; | ||
754 | |||
755 | BUG_ON(!list_empty(&parent->ptraced)); | ||
756 | |||
757 | list_for_each_entry_safe(p, n, dead, ptrace_entry) { | ||
758 | list_del_init(&p->ptrace_entry); | ||
759 | release_task(p); | ||
760 | } | ||
761 | } | ||
762 | |||
763 | static void reparent_thread(struct task_struct *p, struct task_struct *father) | ||
764 | { | ||
765 | if (p->pdeath_signal) | ||
766 | /* We already hold the tasklist_lock here. */ | ||
767 | group_send_sig_info(p->pdeath_signal, SEND_SIG_NOINFO, p); | ||
768 | |||
769 | list_move_tail(&p->sibling, &p->real_parent->children); | ||
726 | 770 | ||
727 | /* If this is a threaded reparent there is no need to | 771 | /* If this is a threaded reparent there is no need to |
728 | * notify anyone anything has happened. | 772 | * notify anyone anything has happened. |
@@ -737,7 +781,8 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced) | |||
737 | /* If we'd notified the old parent about this child's death, | 781 | /* If we'd notified the old parent about this child's death, |
738 | * also notify the new parent. | 782 | * also notify the new parent. |
739 | */ | 783 | */ |
740 | if (!traced && p->exit_state == EXIT_ZOMBIE && | 784 | if (!ptrace_reparented(p) && |
785 | p->exit_state == EXIT_ZOMBIE && | ||
741 | !task_detached(p) && thread_group_empty(p)) | 786 | !task_detached(p) && thread_group_empty(p)) |
742 | do_notify_parent(p, p->exit_signal); | 787 | do_notify_parent(p, p->exit_signal); |
743 | 788 | ||
@@ -754,12 +799,15 @@ reparent_thread(struct task_struct *p, struct task_struct *father, int traced) | |||
754 | static void forget_original_parent(struct task_struct *father) | 799 | static void forget_original_parent(struct task_struct *father) |
755 | { | 800 | { |
756 | struct task_struct *p, *n, *reaper = father; | 801 | struct task_struct *p, *n, *reaper = father; |
757 | struct list_head ptrace_dead; | 802 | LIST_HEAD(ptrace_dead); |
758 | |||
759 | INIT_LIST_HEAD(&ptrace_dead); | ||
760 | 803 | ||
761 | write_lock_irq(&tasklist_lock); | 804 | write_lock_irq(&tasklist_lock); |
762 | 805 | ||
806 | /* | ||
807 | * First clean up ptrace if we were using it. | ||
808 | */ | ||
809 | ptrace_exit(father, &ptrace_dead); | ||
810 | |||
763 | do { | 811 | do { |
764 | reaper = next_thread(reaper); | 812 | reaper = next_thread(reaper); |
765 | if (reaper == father) { | 813 | if (reaper == father) { |
@@ -768,58 +816,19 @@ static void forget_original_parent(struct task_struct *father) | |||
768 | } | 816 | } |
769 | } while (reaper->flags & PF_EXITING); | 817 | } while (reaper->flags & PF_EXITING); |
770 | 818 | ||
771 | /* | ||
772 | * There are only two places where our children can be: | ||
773 | * | ||
774 | * - in our child list | ||
775 | * - in our ptraced child list | ||
776 | * | ||
777 | * Search them and reparent children. | ||
778 | */ | ||
779 | list_for_each_entry_safe(p, n, &father->children, sibling) { | 819 | list_for_each_entry_safe(p, n, &father->children, sibling) { |
780 | int ptrace; | ||
781 | |||
782 | ptrace = p->ptrace; | ||
783 | |||
784 | /* if father isn't the real parent, then ptrace must be enabled */ | ||
785 | BUG_ON(father != p->real_parent && !ptrace); | ||
786 | |||
787 | if (father == p->real_parent) { | ||
788 | /* reparent with a reaper, real father it's us */ | ||
789 | p->real_parent = reaper; | ||
790 | reparent_thread(p, father, 0); | ||
791 | } else { | ||
792 | /* reparent ptraced task to its real parent */ | ||
793 | __ptrace_unlink (p); | ||
794 | if (p->exit_state == EXIT_ZOMBIE && !task_detached(p) && | ||
795 | thread_group_empty(p)) | ||
796 | do_notify_parent(p, p->exit_signal); | ||
797 | } | ||
798 | |||
799 | /* | ||
800 | * if the ptraced child is a detached zombie we must collect | ||
801 | * it before we exit, or it will remain zombie forever since | ||
802 | * we prevented it from self-reap itself while it was being | ||
803 | * traced by us, to be able to see it in wait4. | ||
804 | */ | ||
805 | if (unlikely(ptrace && p->exit_state == EXIT_ZOMBIE && task_detached(p))) | ||
806 | list_add(&p->ptrace_list, &ptrace_dead); | ||
807 | } | ||
808 | |||
809 | list_for_each_entry_safe(p, n, &father->ptrace_children, ptrace_list) { | ||
810 | p->real_parent = reaper; | 820 | p->real_parent = reaper; |
811 | reparent_thread(p, father, 1); | 821 | if (p->parent == father) { |
822 | BUG_ON(p->ptrace); | ||
823 | p->parent = p->real_parent; | ||
824 | } | ||
825 | reparent_thread(p, father); | ||
812 | } | 826 | } |
813 | 827 | ||
814 | write_unlock_irq(&tasklist_lock); | 828 | write_unlock_irq(&tasklist_lock); |
815 | BUG_ON(!list_empty(&father->children)); | 829 | BUG_ON(!list_empty(&father->children)); |
816 | BUG_ON(!list_empty(&father->ptrace_children)); | ||
817 | |||
818 | list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_list) { | ||
819 | list_del_init(&p->ptrace_list); | ||
820 | release_task(p); | ||
821 | } | ||
822 | 830 | ||
831 | ptrace_exit_finish(father, &ptrace_dead); | ||
823 | } | 832 | } |
824 | 833 | ||
825 | /* | 834 | /* |
@@ -1180,13 +1189,6 @@ static int eligible_child(enum pid_type type, struct pid *pid, int options, | |||
1180 | return 0; | 1189 | return 0; |
1181 | } | 1190 | } |
1182 | 1191 | ||
1183 | /* | ||
1184 | * Do not consider detached threads that are | ||
1185 | * not ptraced: | ||
1186 | */ | ||
1187 | if (task_detached(p) && !p->ptrace) | ||
1188 | return 0; | ||
1189 | |||
1190 | /* Wait for all children (clone and not) if __WALL is set; | 1192 | /* Wait for all children (clone and not) if __WALL is set; |
1191 | * otherwise, wait for clone children *only* if __WCLONE is | 1193 | * otherwise, wait for clone children *only* if __WCLONE is |
1192 | * set; otherwise, wait for non-clone children *only*. (Note: | 1194 | * set; otherwise, wait for non-clone children *only*. (Note: |
@@ -1399,7 +1401,7 @@ static int wait_task_zombie(struct task_struct *p, int options, | |||
1399 | * the lock and this task is uninteresting. If we return nonzero, we have | 1401 | * the lock and this task is uninteresting. If we return nonzero, we have |
1400 | * released the lock and the system call should return. | 1402 | * released the lock and the system call should return. |
1401 | */ | 1403 | */ |
1402 | static int wait_task_stopped(struct task_struct *p, | 1404 | static int wait_task_stopped(int ptrace, struct task_struct *p, |
1403 | int options, struct siginfo __user *infop, | 1405 | int options, struct siginfo __user *infop, |
1404 | int __user *stat_addr, struct rusage __user *ru) | 1406 | int __user *stat_addr, struct rusage __user *ru) |
1405 | { | 1407 | { |
@@ -1407,7 +1409,7 @@ static int wait_task_stopped(struct task_struct *p, | |||
1407 | uid_t uid = 0; /* unneeded, required by compiler */ | 1409 | uid_t uid = 0; /* unneeded, required by compiler */ |
1408 | pid_t pid; | 1410 | pid_t pid; |
1409 | 1411 | ||
1410 | if (!(p->ptrace & PT_PTRACED) && !(options & WUNTRACED)) | 1412 | if (!(options & WUNTRACED)) |
1411 | return 0; | 1413 | return 0; |
1412 | 1414 | ||
1413 | exit_code = 0; | 1415 | exit_code = 0; |
@@ -1416,7 +1418,7 @@ static int wait_task_stopped(struct task_struct *p, | |||
1416 | if (unlikely(!task_is_stopped_or_traced(p))) | 1418 | if (unlikely(!task_is_stopped_or_traced(p))) |
1417 | goto unlock_sig; | 1419 | goto unlock_sig; |
1418 | 1420 | ||
1419 | if (!(p->ptrace & PT_PTRACED) && p->signal->group_stop_count > 0) | 1421 | if (!ptrace && p->signal->group_stop_count > 0) |
1420 | /* | 1422 | /* |
1421 | * A group stop is in progress and this is the group leader. | 1423 | * A group stop is in progress and this is the group leader. |
1422 | * We won't report until all threads have stopped. | 1424 | * We won't report until all threads have stopped. |
@@ -1445,7 +1447,7 @@ unlock_sig: | |||
1445 | */ | 1447 | */ |
1446 | get_task_struct(p); | 1448 | get_task_struct(p); |
1447 | pid = task_pid_vnr(p); | 1449 | pid = task_pid_vnr(p); |
1448 | why = (p->ptrace & PT_PTRACED) ? CLD_TRAPPED : CLD_STOPPED; | 1450 | why = ptrace ? CLD_TRAPPED : CLD_STOPPED; |
1449 | read_unlock(&tasklist_lock); | 1451 | read_unlock(&tasklist_lock); |
1450 | 1452 | ||
1451 | if (unlikely(options & WNOWAIT)) | 1453 | if (unlikely(options & WNOWAIT)) |
@@ -1536,7 +1538,7 @@ static int wait_task_continued(struct task_struct *p, int options, | |||
1536 | * Returns zero if the search for a child should continue; | 1538 | * Returns zero if the search for a child should continue; |
1537 | * then *@notask_error is 0 if @p is an eligible child, or still -ECHILD. | 1539 | * then *@notask_error is 0 if @p is an eligible child, or still -ECHILD. |
1538 | */ | 1540 | */ |
1539 | static int wait_consider_task(struct task_struct *parent, | 1541 | static int wait_consider_task(struct task_struct *parent, int ptrace, |
1540 | struct task_struct *p, int *notask_error, | 1542 | struct task_struct *p, int *notask_error, |
1541 | enum pid_type type, struct pid *pid, int options, | 1543 | enum pid_type type, struct pid *pid, int options, |
1542 | struct siginfo __user *infop, | 1544 | struct siginfo __user *infop, |
@@ -1546,6 +1548,15 @@ static int wait_consider_task(struct task_struct *parent, | |||
1546 | if (ret <= 0) | 1548 | if (ret <= 0) |
1547 | return ret; | 1549 | return ret; |
1548 | 1550 | ||
1551 | if (likely(!ptrace) && unlikely(p->ptrace)) { | ||
1552 | /* | ||
1553 | * This child is hidden by ptrace. | ||
1554 | * We aren't allowed to see it now, but eventually we will. | ||
1555 | */ | ||
1556 | *notask_error = 0; | ||
1557 | return 0; | ||
1558 | } | ||
1559 | |||
1549 | if (p->exit_state == EXIT_DEAD) | 1560 | if (p->exit_state == EXIT_DEAD) |
1550 | return 0; | 1561 | return 0; |
1551 | 1562 | ||
@@ -1562,7 +1573,8 @@ static int wait_consider_task(struct task_struct *parent, | |||
1562 | *notask_error = 0; | 1573 | *notask_error = 0; |
1563 | 1574 | ||
1564 | if (task_is_stopped_or_traced(p)) | 1575 | if (task_is_stopped_or_traced(p)) |
1565 | return wait_task_stopped(p, options, infop, stat_addr, ru); | 1576 | return wait_task_stopped(ptrace, p, options, |
1577 | infop, stat_addr, ru); | ||
1566 | 1578 | ||
1567 | return wait_task_continued(p, options, infop, stat_addr, ru); | 1579 | return wait_task_continued(p, options, infop, stat_addr, ru); |
1568 | } | 1580 | } |
@@ -1583,11 +1595,16 @@ static int do_wait_thread(struct task_struct *tsk, int *notask_error, | |||
1583 | struct task_struct *p; | 1595 | struct task_struct *p; |
1584 | 1596 | ||
1585 | list_for_each_entry(p, &tsk->children, sibling) { | 1597 | list_for_each_entry(p, &tsk->children, sibling) { |
1586 | int ret = wait_consider_task(tsk, p, notask_error, | 1598 | /* |
1587 | type, pid, options, | 1599 | * Do not consider detached threads. |
1588 | infop, stat_addr, ru); | 1600 | */ |
1589 | if (ret) | 1601 | if (!task_detached(p)) { |
1590 | return ret; | 1602 | int ret = wait_consider_task(tsk, 0, p, notask_error, |
1603 | type, pid, options, | ||
1604 | infop, stat_addr, ru); | ||
1605 | if (ret) | ||
1606 | return ret; | ||
1607 | } | ||
1591 | } | 1608 | } |
1592 | 1609 | ||
1593 | return 0; | 1610 | return 0; |
@@ -1601,21 +1618,16 @@ static int ptrace_do_wait(struct task_struct *tsk, int *notask_error, | |||
1601 | struct task_struct *p; | 1618 | struct task_struct *p; |
1602 | 1619 | ||
1603 | /* | 1620 | /* |
1604 | * If we never saw an eligile child, check for children stolen by | 1621 | * Traditionally we see ptrace'd stopped tasks regardless of options. |
1605 | * ptrace. We don't leave -ECHILD in *@notask_error if there are any, | ||
1606 | * because we will eventually be allowed to wait for them again. | ||
1607 | */ | 1622 | */ |
1608 | if (!*notask_error) | 1623 | options |= WUNTRACED; |
1609 | return 0; | ||
1610 | 1624 | ||
1611 | list_for_each_entry(p, &tsk->ptrace_children, ptrace_list) { | 1625 | list_for_each_entry(p, &tsk->ptraced, ptrace_entry) { |
1612 | int ret = eligible_child(type, pid, options, p); | 1626 | int ret = wait_consider_task(tsk, 1, p, notask_error, |
1613 | if (unlikely(ret < 0)) | 1627 | type, pid, options, |
1628 | infop, stat_addr, ru); | ||
1629 | if (ret) | ||
1614 | return ret; | 1630 | return ret; |
1615 | if (ret) { | ||
1616 | *notask_error = 0; | ||
1617 | return 0; | ||
1618 | } | ||
1619 | } | 1631 | } |
1620 | 1632 | ||
1621 | return 0; | 1633 | return 0; |
diff --git a/kernel/fork.c b/kernel/fork.c index 4bd2f516401f..adefc1131f27 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
@@ -1125,8 +1125,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, | |||
1125 | */ | 1125 | */ |
1126 | p->group_leader = p; | 1126 | p->group_leader = p; |
1127 | INIT_LIST_HEAD(&p->thread_group); | 1127 | INIT_LIST_HEAD(&p->thread_group); |
1128 | INIT_LIST_HEAD(&p->ptrace_children); | 1128 | INIT_LIST_HEAD(&p->ptrace_entry); |
1129 | INIT_LIST_HEAD(&p->ptrace_list); | 1129 | INIT_LIST_HEAD(&p->ptraced); |
1130 | 1130 | ||
1131 | /* Now that the task is set up, run cgroup callbacks if | 1131 | /* Now that the task is set up, run cgroup callbacks if |
1132 | * necessary. We need to run them before the task is visible | 1132 | * 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 | } | 1198 | } |
1199 | 1199 | ||
1200 | if (likely(p->pid)) { | 1200 | if (likely(p->pid)) { |
1201 | add_parent(p); | 1201 | list_add_tail(&p->sibling, &p->real_parent->children); |
1202 | if (unlikely(p->ptrace & PT_PTRACED)) | 1202 | if (unlikely(p->ptrace & PT_PTRACED)) |
1203 | __ptrace_link(p, current->parent); | 1203 | __ptrace_link(p, current->parent); |
1204 | 1204 | ||
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index e337390fce01..8392a9da6450 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -33,13 +33,9 @@ | |||
33 | */ | 33 | */ |
34 | void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) | 34 | void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) |
35 | { | 35 | { |
36 | BUG_ON(!list_empty(&child->ptrace_list)); | 36 | BUG_ON(!list_empty(&child->ptrace_entry)); |
37 | if (child->parent == new_parent) | 37 | list_add(&child->ptrace_entry, &new_parent->ptraced); |
38 | return; | ||
39 | list_add(&child->ptrace_list, &child->parent->ptrace_children); | ||
40 | remove_parent(child); | ||
41 | child->parent = new_parent; | 38 | child->parent = new_parent; |
42 | add_parent(child); | ||
43 | } | 39 | } |
44 | 40 | ||
45 | /* | 41 | /* |
@@ -73,12 +69,8 @@ void __ptrace_unlink(struct task_struct *child) | |||
73 | BUG_ON(!child->ptrace); | 69 | BUG_ON(!child->ptrace); |
74 | 70 | ||
75 | child->ptrace = 0; | 71 | child->ptrace = 0; |
76 | if (ptrace_reparented(child)) { | 72 | child->parent = child->real_parent; |
77 | list_del_init(&child->ptrace_list); | 73 | list_del_init(&child->ptrace_entry); |
78 | remove_parent(child); | ||
79 | child->parent = child->real_parent; | ||
80 | add_parent(child); | ||
81 | } | ||
82 | 74 | ||
83 | if (task_is_traced(child)) | 75 | if (task_is_traced(child)) |
84 | ptrace_untrace(child); | 76 | ptrace_untrace(child); |
@@ -492,15 +484,34 @@ int ptrace_traceme(void) | |||
492 | /* | 484 | /* |
493 | * Are we already being traced? | 485 | * Are we already being traced? |
494 | */ | 486 | */ |
487 | repeat: | ||
495 | task_lock(current); | 488 | task_lock(current); |
496 | if (!(current->ptrace & PT_PTRACED)) { | 489 | if (!(current->ptrace & PT_PTRACED)) { |
490 | /* | ||
491 | * See ptrace_attach() comments about the locking here. | ||
492 | */ | ||
493 | unsigned long flags; | ||
494 | if (!write_trylock_irqsave(&tasklist_lock, flags)) { | ||
495 | task_unlock(current); | ||
496 | do { | ||
497 | cpu_relax(); | ||
498 | } while (!write_can_lock(&tasklist_lock)); | ||
499 | goto repeat; | ||
500 | } | ||
501 | |||
497 | ret = security_ptrace(current->parent, current, | 502 | ret = security_ptrace(current->parent, current, |
498 | PTRACE_MODE_ATTACH); | 503 | PTRACE_MODE_ATTACH); |
504 | |||
499 | /* | 505 | /* |
500 | * Set the ptrace bit in the process ptrace flags. | 506 | * Set the ptrace bit in the process ptrace flags. |
507 | * Then link us on our parent's ptraced list. | ||
501 | */ | 508 | */ |
502 | if (!ret) | 509 | if (!ret) { |
503 | current->ptrace |= PT_PTRACED; | 510 | current->ptrace |= PT_PTRACED; |
511 | __ptrace_link(current, current->real_parent); | ||
512 | } | ||
513 | |||
514 | write_unlock_irqrestore(&tasklist_lock, flags); | ||
504 | } | 515 | } |
505 | task_unlock(current); | 516 | task_unlock(current); |
506 | return ret; | 517 | return ret; |