diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 49 |
1 files changed, 44 insertions, 5 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 6acf8954017c..745fc2dd00c5 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -49,14 +49,22 @@ static void ptrace_untrace(struct task_struct *child) | |||
49 | spin_lock(&child->sighand->siglock); | 49 | spin_lock(&child->sighand->siglock); |
50 | if (task_is_traced(child)) { | 50 | if (task_is_traced(child)) { |
51 | /* | 51 | /* |
52 | * If the group stop is completed or in progress, | 52 | * If group stop is completed or in progress, it should |
53 | * this thread was already counted as stopped. | 53 | * participate in the group stop. Set GROUP_STOP_PENDING |
54 | * before kicking it. | ||
55 | * | ||
56 | * This involves TRACED -> RUNNING -> STOPPED transition | ||
57 | * which is similar to but in the opposite direction of | ||
58 | * what happens while attaching to a stopped task. | ||
59 | * However, in this direction, the intermediate RUNNING | ||
60 | * state is not hidden even from the current ptracer and if | ||
61 | * it immediately re-attaches and performs a WNOHANG | ||
62 | * wait(2), it may fail. | ||
54 | */ | 63 | */ |
55 | if (child->signal->flags & SIGNAL_STOP_STOPPED || | 64 | if (child->signal->flags & SIGNAL_STOP_STOPPED || |
56 | child->signal->group_stop_count) | 65 | child->signal->group_stop_count) |
57 | __set_task_state(child, TASK_STOPPED); | 66 | child->group_stop |= GROUP_STOP_PENDING; |
58 | else | 67 | signal_wake_up(child, 1); |
59 | signal_wake_up(child, 1); | ||
60 | } | 68 | } |
61 | spin_unlock(&child->sighand->siglock); | 69 | spin_unlock(&child->sighand->siglock); |
62 | } | 70 | } |
@@ -165,6 +173,7 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
165 | 173 | ||
166 | static int ptrace_attach(struct task_struct *task) | 174 | static int ptrace_attach(struct task_struct *task) |
167 | { | 175 | { |
176 | bool wait_trap = false; | ||
168 | int retval; | 177 | int retval; |
169 | 178 | ||
170 | audit_ptrace(task); | 179 | audit_ptrace(task); |
@@ -204,12 +213,42 @@ static int ptrace_attach(struct task_struct *task) | |||
204 | __ptrace_link(task, current); | 213 | __ptrace_link(task, current); |
205 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); | 214 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); |
206 | 215 | ||
216 | spin_lock(&task->sighand->siglock); | ||
217 | |||
218 | /* | ||
219 | * If the task is already STOPPED, set GROUP_STOP_PENDING and | ||
220 | * TRAPPING, and kick it so that it transits to TRACED. TRAPPING | ||
221 | * will be cleared if the child completes the transition or any | ||
222 | * event which clears the group stop states happens. We'll wait | ||
223 | * for the transition to complete before returning from this | ||
224 | * function. | ||
225 | * | ||
226 | * This hides STOPPED -> RUNNING -> TRACED transition from the | ||
227 | * attaching thread but a different thread in the same group can | ||
228 | * still observe the transient RUNNING state. IOW, if another | ||
229 | * thread's WNOHANG wait(2) on the stopped tracee races against | ||
230 | * ATTACH, the wait(2) may fail due to the transient RUNNING. | ||
231 | * | ||
232 | * The following task_is_stopped() test is safe as both transitions | ||
233 | * in and out of STOPPED are protected by siglock. | ||
234 | */ | ||
235 | if (task_is_stopped(task)) { | ||
236 | task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING; | ||
237 | signal_wake_up(task, 1); | ||
238 | wait_trap = true; | ||
239 | } | ||
240 | |||
241 | spin_unlock(&task->sighand->siglock); | ||
242 | |||
207 | retval = 0; | 243 | retval = 0; |
208 | unlock_tasklist: | 244 | unlock_tasklist: |
209 | write_unlock_irq(&tasklist_lock); | 245 | write_unlock_irq(&tasklist_lock); |
210 | unlock_creds: | 246 | unlock_creds: |
211 | mutex_unlock(&task->signal->cred_guard_mutex); | 247 | mutex_unlock(&task->signal->cred_guard_mutex); |
212 | out: | 248 | out: |
249 | if (wait_trap) | ||
250 | wait_event(current->signal->wait_chldexit, | ||
251 | !(task->group_stop & GROUP_STOP_TRAPPING)); | ||
213 | return retval; | 252 | return retval; |
214 | } | 253 | } |
215 | 254 | ||