diff options
author | Oleg Nesterov <oleg@redhat.com> | 2011-04-07 14:44:11 -0400 |
---|---|---|
committer | Oleg Nesterov <oleg@redhat.com> | 2011-04-07 14:44:11 -0400 |
commit | e46bc9b6fd65bc9f406a4211fbf95683cc9c2937 (patch) | |
tree | 57046f6b2f4674a0c9048ab1ad1ff50fae7e373a /kernel/ptrace.c | |
parent | 2b9accbee563f535046ff2cd382d0acaa92e130c (diff) | |
parent | 321fb561971ba0f10ce18c0f8a4b9fbfc7cef4b9 (diff) |
Merge branch 'ptrace' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/misc into ptrace
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 118 |
1 files changed, 81 insertions, 37 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 0fc1eed28d27..512bd017218d 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -37,35 +37,33 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) | |||
37 | child->parent = new_parent; | 37 | child->parent = new_parent; |
38 | } | 38 | } |
39 | 39 | ||
40 | /* | 40 | /** |
41 | * Turn a tracing stop into a normal stop now, since with no tracer there | 41 | * __ptrace_unlink - unlink ptracee and restore its execution state |
42 | * would be no way to wake it up with SIGCONT or SIGKILL. If there was a | 42 | * @child: ptracee to be unlinked |
43 | * signal sent that would resume the child, but didn't because it was in | ||
44 | * TASK_TRACED, resume it now. | ||
45 | * Requires that irqs be disabled. | ||
46 | */ | ||
47 | static void ptrace_untrace(struct task_struct *child) | ||
48 | { | ||
49 | spin_lock(&child->sighand->siglock); | ||
50 | if (task_is_traced(child)) { | ||
51 | /* | ||
52 | * If the group stop is completed or in progress, | ||
53 | * this thread was already counted as stopped. | ||
54 | */ | ||
55 | if (child->signal->flags & SIGNAL_STOP_STOPPED || | ||
56 | child->signal->group_stop_count) | ||
57 | __set_task_state(child, TASK_STOPPED); | ||
58 | else | ||
59 | signal_wake_up(child, 1); | ||
60 | } | ||
61 | spin_unlock(&child->sighand->siglock); | ||
62 | } | ||
63 | |||
64 | /* | ||
65 | * unptrace a task: move it back to its original parent and | ||
66 | * remove it from the ptrace list. | ||
67 | * | 43 | * |
68 | * Must be called with the tasklist lock write-held. | 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. | ||
64 | * | ||
65 | * CONTEXT: | ||
66 | * write_lock_irq(tasklist_lock) | ||
69 | */ | 67 | */ |
70 | void __ptrace_unlink(struct task_struct *child) | 68 | void __ptrace_unlink(struct task_struct *child) |
71 | { | 69 | { |
@@ -75,8 +73,27 @@ void __ptrace_unlink(struct task_struct *child) | |||
75 | child->parent = child->real_parent; | 73 | child->parent = child->real_parent; |
76 | list_del_init(&child->ptrace_entry); | 74 | list_del_init(&child->ptrace_entry); |
77 | 75 | ||
78 | if (task_is_traced(child)) | 76 | spin_lock(&child->sighand->siglock); |
79 | ptrace_untrace(child); | 77 | |
78 | /* | ||
79 | * Reinstate GROUP_STOP_PENDING if group stop is in effect and | ||
80 | * @child isn't dead. | ||
81 | */ | ||
82 | if (!(child->flags & PF_EXITING) && | ||
83 | (child->signal->flags & SIGNAL_STOP_STOPPED || | ||
84 | child->signal->group_stop_count)) | ||
85 | child->group_stop |= GROUP_STOP_PENDING; | ||
86 | |||
87 | /* | ||
88 | * If transition to TASK_STOPPED is pending or in TASK_TRACED, kick | ||
89 | * @child in the butt. Note that @resume should be used iff @child | ||
90 | * is in TASK_TRACED; otherwise, we might unduly disrupt | ||
91 | * TASK_KILLABLE sleeps. | ||
92 | */ | ||
93 | if (child->group_stop & GROUP_STOP_PENDING || task_is_traced(child)) | ||
94 | signal_wake_up(child, task_is_traced(child)); | ||
95 | |||
96 | spin_unlock(&child->sighand->siglock); | ||
80 | } | 97 | } |
81 | 98 | ||
82 | /* | 99 | /* |
@@ -95,16 +112,14 @@ int ptrace_check_attach(struct task_struct *child, int kill) | |||
95 | */ | 112 | */ |
96 | read_lock(&tasklist_lock); | 113 | read_lock(&tasklist_lock); |
97 | if ((child->ptrace & PT_PTRACED) && child->parent == current) { | 114 | if ((child->ptrace & PT_PTRACED) && child->parent == current) { |
98 | ret = 0; | ||
99 | /* | 115 | /* |
100 | * child->sighand can't be NULL, release_task() | 116 | * child->sighand can't be NULL, release_task() |
101 | * does ptrace_unlink() before __exit_signal(). | 117 | * does ptrace_unlink() before __exit_signal(). |
102 | */ | 118 | */ |
103 | spin_lock_irq(&child->sighand->siglock); | 119 | spin_lock_irq(&child->sighand->siglock); |
104 | if (task_is_stopped(child)) | 120 | WARN_ON_ONCE(task_is_stopped(child)); |
105 | child->state = TASK_TRACED; | 121 | if (task_is_traced(child) || kill) |
106 | else if (!task_is_traced(child) && !kill) | 122 | ret = 0; |
107 | ret = -ESRCH; | ||
108 | spin_unlock_irq(&child->sighand->siglock); | 123 | spin_unlock_irq(&child->sighand->siglock); |
109 | } | 124 | } |
110 | read_unlock(&tasklist_lock); | 125 | read_unlock(&tasklist_lock); |
@@ -168,6 +183,7 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
168 | 183 | ||
169 | static int ptrace_attach(struct task_struct *task) | 184 | static int ptrace_attach(struct task_struct *task) |
170 | { | 185 | { |
186 | bool wait_trap = false; | ||
171 | int retval; | 187 | int retval; |
172 | 188 | ||
173 | audit_ptrace(task); | 189 | audit_ptrace(task); |
@@ -207,12 +223,42 @@ static int ptrace_attach(struct task_struct *task) | |||
207 | __ptrace_link(task, current); | 223 | __ptrace_link(task, current); |
208 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); | 224 | send_sig_info(SIGSTOP, SEND_SIG_FORCED, task); |
209 | 225 | ||
226 | spin_lock(&task->sighand->siglock); | ||
227 | |||
228 | /* | ||
229 | * If the task is already STOPPED, set GROUP_STOP_PENDING and | ||
230 | * TRAPPING, and kick it so that it transits to TRACED. TRAPPING | ||
231 | * will be cleared if the child completes the transition or any | ||
232 | * event which clears the group stop states happens. We'll wait | ||
233 | * for the transition to complete before returning from this | ||
234 | * function. | ||
235 | * | ||
236 | * This hides STOPPED -> RUNNING -> TRACED transition from the | ||
237 | * attaching thread but a different thread in the same group can | ||
238 | * still observe the transient RUNNING state. IOW, if another | ||
239 | * thread's WNOHANG wait(2) on the stopped tracee races against | ||
240 | * ATTACH, the wait(2) may fail due to the transient RUNNING. | ||
241 | * | ||
242 | * The following task_is_stopped() test is safe as both transitions | ||
243 | * in and out of STOPPED are protected by siglock. | ||
244 | */ | ||
245 | if (task_is_stopped(task)) { | ||
246 | task->group_stop |= GROUP_STOP_PENDING | GROUP_STOP_TRAPPING; | ||
247 | signal_wake_up(task, 1); | ||
248 | wait_trap = true; | ||
249 | } | ||
250 | |||
251 | spin_unlock(&task->sighand->siglock); | ||
252 | |||
210 | retval = 0; | 253 | retval = 0; |
211 | unlock_tasklist: | 254 | unlock_tasklist: |
212 | write_unlock_irq(&tasklist_lock); | 255 | write_unlock_irq(&tasklist_lock); |
213 | unlock_creds: | 256 | unlock_creds: |
214 | mutex_unlock(&task->signal->cred_guard_mutex); | 257 | mutex_unlock(&task->signal->cred_guard_mutex); |
215 | out: | 258 | out: |
259 | if (wait_trap) | ||
260 | wait_event(current->signal->wait_chldexit, | ||
261 | !(task->group_stop & GROUP_STOP_TRAPPING)); | ||
216 | return retval; | 262 | return retval; |
217 | } | 263 | } |
218 | 264 | ||
@@ -315,8 +361,6 @@ static int ptrace_detach(struct task_struct *child, unsigned int data) | |||
315 | if (child->ptrace) { | 361 | if (child->ptrace) { |
316 | child->exit_code = data; | 362 | child->exit_code = data; |
317 | dead = __ptrace_detach(current, child); | 363 | dead = __ptrace_detach(current, child); |
318 | if (!child->exit_state) | ||
319 | wake_up_state(child, TASK_TRACED | TASK_STOPPED); | ||
320 | } | 364 | } |
321 | write_unlock_irq(&tasklist_lock); | 365 | write_unlock_irq(&tasklist_lock); |
322 | 366 | ||