diff options
author | Oleg Nesterov <oleg@redhat.com> | 2009-04-02 19:58:18 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-04-02 22:05:00 -0400 |
commit | 39c626ae47c469abdfd30c6e42eff884931380d6 (patch) | |
tree | 58cbe75bac79ce8ef55c94189df26448d0283918 /kernel/ptrace.c | |
parent | 7f5d3652d469cdf9eb2365dfea7ce3fb9e1409cc (diff) |
forget_original_parent: split out the un-ptrace part
By discussion with Roland.
- Rename ptrace_exit() to exit_ptrace(), and change it to do all the
necessary work with ->ptraced list by its own.
- Move this code from exit.c to ptrace.c
- Update the comment in ptrace_detach() to explain the rechecking of
the child->ptrace.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: "Metzger, Markus T" <markus.t.metzger@intel.com>
Cc: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 78 |
1 files changed, 76 insertions, 2 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index ee553b6ad125..f5a9fa5aafa1 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -235,9 +235,57 @@ out: | |||
235 | return retval; | 235 | return retval; |
236 | } | 236 | } |
237 | 237 | ||
238 | /* | ||
239 | * Called with irqs disabled, returns true if childs should reap themselves. | ||
240 | */ | ||
241 | static int ignoring_children(struct sighand_struct *sigh) | ||
242 | { | ||
243 | int ret; | ||
244 | spin_lock(&sigh->siglock); | ||
245 | ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) || | ||
246 | (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT); | ||
247 | spin_unlock(&sigh->siglock); | ||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | /* | ||
252 | * Called with tasklist_lock held for writing. | ||
253 | * Unlink a traced task, and clean it up if it was a traced zombie. | ||
254 | * Return true if it needs to be reaped with release_task(). | ||
255 | * (We can't call release_task() here because we already hold tasklist_lock.) | ||
256 | * | ||
257 | * If it's a zombie, our attachedness prevented normal parent notification | ||
258 | * or self-reaping. Do notification now if it would have happened earlier. | ||
259 | * If it should reap itself, return true. | ||
260 | * | ||
261 | * If it's our own child, there is no notification to do. | ||
262 | * But if our normal children self-reap, then this child | ||
263 | * was prevented by ptrace and we must reap it now. | ||
264 | */ | ||
265 | static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p) | ||
266 | { | ||
267 | __ptrace_unlink(p); | ||
268 | |||
269 | if (p->exit_state == EXIT_ZOMBIE) { | ||
270 | if (!task_detached(p) && thread_group_empty(p)) { | ||
271 | if (!same_thread_group(p->real_parent, tracer)) | ||
272 | do_notify_parent(p, p->exit_signal); | ||
273 | else if (ignoring_children(tracer->sighand)) | ||
274 | p->exit_signal = -1; | ||
275 | } | ||
276 | if (task_detached(p)) { | ||
277 | /* Mark it as in the process of being reaped. */ | ||
278 | p->exit_state = EXIT_DEAD; | ||
279 | return true; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | return false; | ||
284 | } | ||
285 | |||
238 | int ptrace_detach(struct task_struct *child, unsigned int data) | 286 | int ptrace_detach(struct task_struct *child, unsigned int data) |
239 | { | 287 | { |
240 | int dead = 0; | 288 | bool dead = false; |
241 | 289 | ||
242 | if (!valid_signal(data)) | 290 | if (!valid_signal(data)) |
243 | return -EIO; | 291 | return -EIO; |
@@ -247,7 +295,10 @@ int ptrace_detach(struct task_struct *child, unsigned int data) | |||
247 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 295 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
248 | 296 | ||
249 | write_lock_irq(&tasklist_lock); | 297 | write_lock_irq(&tasklist_lock); |
250 | /* protect against de_thread()->release_task() */ | 298 | /* |
299 | * This child can be already killed. Make sure de_thread() or | ||
300 | * our sub-thread doing do_wait() didn't do release_task() yet. | ||
301 | */ | ||
251 | if (child->ptrace) { | 302 | if (child->ptrace) { |
252 | child->exit_code = data; | 303 | child->exit_code = data; |
253 | 304 | ||
@@ -264,6 +315,29 @@ int ptrace_detach(struct task_struct *child, unsigned int data) | |||
264 | return 0; | 315 | return 0; |
265 | } | 316 | } |
266 | 317 | ||
318 | /* | ||
319 | * Detach all tasks we were using ptrace on. | ||
320 | */ | ||
321 | void exit_ptrace(struct task_struct *tracer) | ||
322 | { | ||
323 | struct task_struct *p, *n; | ||
324 | LIST_HEAD(ptrace_dead); | ||
325 | |||
326 | write_lock_irq(&tasklist_lock); | ||
327 | list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) { | ||
328 | if (__ptrace_detach(tracer, p)) | ||
329 | list_add(&p->ptrace_entry, &ptrace_dead); | ||
330 | } | ||
331 | write_unlock_irq(&tasklist_lock); | ||
332 | |||
333 | BUG_ON(!list_empty(&tracer->ptraced)); | ||
334 | |||
335 | list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) { | ||
336 | list_del_init(&p->ptrace_entry); | ||
337 | release_task(p); | ||
338 | } | ||
339 | } | ||
340 | |||
267 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) | 341 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) |
268 | { | 342 | { |
269 | int copied = 0; | 343 | int copied = 0; |