aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/ptrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r--kernel/ptrace.c49
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
166static int ptrace_attach(struct task_struct *task) 174static 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;
208unlock_tasklist: 244unlock_tasklist:
209 write_unlock_irq(&tasklist_lock); 245 write_unlock_irq(&tasklist_lock);
210unlock_creds: 246unlock_creds:
211 mutex_unlock(&task->signal->cred_guard_mutex); 247 mutex_unlock(&task->signal->cred_guard_mutex);
212out: 248out:
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