diff options
-rw-r--r-- | kernel/ptrace.c | 59 |
1 files changed, 39 insertions, 20 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index e6098434b533..43485866749a 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -41,7 +41,26 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) | |||
41 | * __ptrace_unlink - unlink ptracee and restore its execution state | 41 | * __ptrace_unlink - unlink ptracee and restore its execution state |
42 | * @child: ptracee to be unlinked | 42 | * @child: ptracee to be unlinked |
43 | * | 43 | * |
44 | * Remove @child from the ptrace list, move it back to the original parent. | 44 | * Remove @child from the ptrace list, move it back to the original parent, |
45 | * and restore the execution state so that it conforms to the group stop | ||
46 | * state. | ||
47 | * | ||
48 | * Unlinking can happen via two paths - explicit PTRACE_DETACH or ptracer | ||
49 | * exiting. For PTRACE_DETACH, unless the ptracee has been killed between | ||
50 | * ptrace_check_attach() and here, it's guaranteed to be in TASK_TRACED. | ||
51 | * If the ptracer is exiting, the ptracee can be in any state. | ||
52 | * | ||
53 | * After detach, the ptracee should be in a state which conforms to the | ||
54 | * group stop. If the group is stopped or in the process of stopping, the | ||
55 | * ptracee should be put into TASK_STOPPED; otherwise, it should be woken | ||
56 | * up from TASK_TRACED. | ||
57 | * | ||
58 | * If the ptracee is in TASK_TRACED and needs to be moved to TASK_STOPPED, | ||
59 | * it goes through TRACED -> RUNNING -> STOPPED transition which is similar | ||
60 | * to but in the opposite direction of what happens while attaching to a | ||
61 | * stopped task. However, in this direction, the intermediate RUNNING | ||
62 | * state is not hidden even from the current ptracer and if it immediately | ||
63 | * re-attaches and performs a WNOHANG wait(2), it may fail. | ||
45 | * | 64 | * |
46 | * CONTEXT: | 65 | * CONTEXT: |
47 | * write_lock_irq(tasklist_lock) | 66 | * write_lock_irq(tasklist_lock) |
@@ -55,25 +74,25 @@ void __ptrace_unlink(struct task_struct *child) | |||
55 | list_del_init(&child->ptrace_entry); | 74 | list_del_init(&child->ptrace_entry); |
56 | 75 | ||
57 | spin_lock(&child->sighand->siglock); | 76 | spin_lock(&child->sighand->siglock); |
58 | if (task_is_traced(child)) { | 77 | |
59 | /* | 78 | /* |
60 | * If group stop is completed or in progress, it should | 79 | * Reinstate GROUP_STOP_PENDING if group stop is in effect and |
61 | * participate in the group stop. Set GROUP_STOP_PENDING | 80 | * @child isn't dead. |
62 | * before kicking it. | 81 | */ |
63 | * | 82 | if (!(child->flags & PF_EXITING) && |
64 | * This involves TRACED -> RUNNING -> STOPPED transition | 83 | (child->signal->flags & SIGNAL_STOP_STOPPED || |
65 | * which is similar to but in the opposite direction of | 84 | child->signal->group_stop_count)) |
66 | * what happens while attaching to a stopped task. | 85 | child->group_stop |= GROUP_STOP_PENDING; |
67 | * However, in this direction, the intermediate RUNNING | 86 | |
68 | * state is not hidden even from the current ptracer and if | 87 | /* |
69 | * it immediately re-attaches and performs a WNOHANG | 88 | * If transition to TASK_STOPPED is pending or in TASK_TRACED, kick |
70 | * wait(2), it may fail. | 89 | * @child in the butt. Note that @resume should be used iff @child |
71 | */ | 90 | * is in TASK_TRACED; otherwise, we might unduly disrupt |
72 | if (child->signal->flags & SIGNAL_STOP_STOPPED || | 91 | * TASK_KILLABLE sleeps. |
73 | child->signal->group_stop_count) | 92 | */ |
74 | child->group_stop |= GROUP_STOP_PENDING; | 93 | if (child->group_stop & GROUP_STOP_PENDING || task_is_traced(child)) |
75 | signal_wake_up(child, 1); | 94 | signal_wake_up(child, task_is_traced(child)); |
76 | } | 95 | |
77 | spin_unlock(&child->sighand->siglock); | 96 | spin_unlock(&child->sighand->siglock); |
78 | } | 97 | } |
79 | 98 | ||