diff options
author | James Morris <jmorris@namei.org> | 2009-05-08 03:56:47 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2009-05-08 03:56:47 -0400 |
commit | d254117099d711f215e62427f55dfb8ebd5ad011 (patch) | |
tree | 0848ff8dd74314fec14a86497f8d288c86ba7c65 /kernel/ptrace.c | |
parent | 07ff7a0b187f3951788f64ae1f30e8109bc8e9eb (diff) | |
parent | 8c9ed899b44c19e81859fbb0e9d659fe2f8630fc (diff) |
Merge branch 'master' into next
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 130 |
1 files changed, 101 insertions, 29 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index c9cf48b21f05..0692ab5a0d67 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -21,9 +21,7 @@ | |||
21 | #include <linux/audit.h> | 21 | #include <linux/audit.h> |
22 | #include <linux/pid_namespace.h> | 22 | #include <linux/pid_namespace.h> |
23 | #include <linux/syscalls.h> | 23 | #include <linux/syscalls.h> |
24 | 24 | #include <linux/uaccess.h> | |
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/uaccess.h> | ||
27 | 25 | ||
28 | 26 | ||
29 | /* | 27 | /* |
@@ -48,7 +46,7 @@ void __ptrace_link(struct task_struct *child, struct task_struct *new_parent) | |||
48 | list_add(&child->ptrace_entry, &new_parent->ptraced); | 46 | list_add(&child->ptrace_entry, &new_parent->ptraced); |
49 | child->parent = new_parent; | 47 | child->parent = new_parent; |
50 | } | 48 | } |
51 | 49 | ||
52 | /* | 50 | /* |
53 | * Turn a tracing stop into a normal stop now, since with no tracer there | 51 | * Turn a tracing stop into a normal stop now, since with no tracer there |
54 | * would be no way to wake it up with SIGCONT or SIGKILL. If there was a | 52 | * would be no way to wake it up with SIGCONT or SIGKILL. If there was a |
@@ -60,11 +58,15 @@ static void ptrace_untrace(struct task_struct *child) | |||
60 | { | 58 | { |
61 | spin_lock(&child->sighand->siglock); | 59 | spin_lock(&child->sighand->siglock); |
62 | if (task_is_traced(child)) { | 60 | if (task_is_traced(child)) { |
63 | if (child->signal->flags & SIGNAL_STOP_STOPPED) { | 61 | /* |
62 | * If the group stop is completed or in progress, | ||
63 | * this thread was already counted as stopped. | ||
64 | */ | ||
65 | if (child->signal->flags & SIGNAL_STOP_STOPPED || | ||
66 | child->signal->group_stop_count) | ||
64 | __set_task_state(child, TASK_STOPPED); | 67 | __set_task_state(child, TASK_STOPPED); |
65 | } else { | 68 | else |
66 | signal_wake_up(child, 1); | 69 | signal_wake_up(child, 1); |
67 | } | ||
68 | } | 70 | } |
69 | spin_unlock(&child->sighand->siglock); | 71 | spin_unlock(&child->sighand->siglock); |
70 | } | 72 | } |
@@ -169,7 +171,7 @@ bool ptrace_may_access(struct task_struct *task, unsigned int mode) | |||
169 | task_lock(task); | 171 | task_lock(task); |
170 | err = __ptrace_may_access(task, mode); | 172 | err = __ptrace_may_access(task, mode); |
171 | task_unlock(task); | 173 | task_unlock(task); |
172 | return (!err ? true : false); | 174 | return !err; |
173 | } | 175 | } |
174 | 176 | ||
175 | int ptrace_attach(struct task_struct *task) | 177 | int ptrace_attach(struct task_struct *task) |
@@ -186,7 +188,7 @@ int ptrace_attach(struct task_struct *task) | |||
186 | /* Protect exec's credential calculations against our interference; | 188 | /* Protect exec's credential calculations against our interference; |
187 | * SUID, SGID and LSM creds get determined differently under ptrace. | 189 | * SUID, SGID and LSM creds get determined differently under ptrace. |
188 | */ | 190 | */ |
189 | retval = mutex_lock_interruptible(¤t->cred_exec_mutex); | 191 | retval = mutex_lock_interruptible(&task->cred_exec_mutex); |
190 | if (retval < 0) | 192 | if (retval < 0) |
191 | goto out; | 193 | goto out; |
192 | 194 | ||
@@ -230,23 +232,63 @@ repeat: | |||
230 | bad: | 232 | bad: |
231 | write_unlock_irqrestore(&tasklist_lock, flags); | 233 | write_unlock_irqrestore(&tasklist_lock, flags); |
232 | task_unlock(task); | 234 | task_unlock(task); |
233 | mutex_unlock(¤t->cred_exec_mutex); | 235 | mutex_unlock(&task->cred_exec_mutex); |
234 | out: | 236 | out: |
235 | return retval; | 237 | return retval; |
236 | } | 238 | } |
237 | 239 | ||
238 | static inline void __ptrace_detach(struct task_struct *child, unsigned int data) | 240 | /* |
241 | * Called with irqs disabled, returns true if childs should reap themselves. | ||
242 | */ | ||
243 | static int ignoring_children(struct sighand_struct *sigh) | ||
239 | { | 244 | { |
240 | child->exit_code = data; | 245 | int ret; |
241 | /* .. re-parent .. */ | 246 | spin_lock(&sigh->siglock); |
242 | __ptrace_unlink(child); | 247 | ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) || |
243 | /* .. and wake it up. */ | 248 | (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT); |
244 | if (child->exit_state != EXIT_ZOMBIE) | 249 | spin_unlock(&sigh->siglock); |
245 | wake_up_process(child); | 250 | return ret; |
251 | } | ||
252 | |||
253 | /* | ||
254 | * Called with tasklist_lock held for writing. | ||
255 | * Unlink a traced task, and clean it up if it was a traced zombie. | ||
256 | * Return true if it needs to be reaped with release_task(). | ||
257 | * (We can't call release_task() here because we already hold tasklist_lock.) | ||
258 | * | ||
259 | * If it's a zombie, our attachedness prevented normal parent notification | ||
260 | * or self-reaping. Do notification now if it would have happened earlier. | ||
261 | * If it should reap itself, return true. | ||
262 | * | ||
263 | * If it's our own child, there is no notification to do. | ||
264 | * But if our normal children self-reap, then this child | ||
265 | * was prevented by ptrace and we must reap it now. | ||
266 | */ | ||
267 | static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p) | ||
268 | { | ||
269 | __ptrace_unlink(p); | ||
270 | |||
271 | if (p->exit_state == EXIT_ZOMBIE) { | ||
272 | if (!task_detached(p) && thread_group_empty(p)) { | ||
273 | if (!same_thread_group(p->real_parent, tracer)) | ||
274 | do_notify_parent(p, p->exit_signal); | ||
275 | else if (ignoring_children(tracer->sighand)) | ||
276 | p->exit_signal = -1; | ||
277 | } | ||
278 | if (task_detached(p)) { | ||
279 | /* Mark it as in the process of being reaped. */ | ||
280 | p->exit_state = EXIT_DEAD; | ||
281 | return true; | ||
282 | } | ||
283 | } | ||
284 | |||
285 | return false; | ||
246 | } | 286 | } |
247 | 287 | ||
248 | int ptrace_detach(struct task_struct *child, unsigned int data) | 288 | int ptrace_detach(struct task_struct *child, unsigned int data) |
249 | { | 289 | { |
290 | bool dead = false; | ||
291 | |||
250 | if (!valid_signal(data)) | 292 | if (!valid_signal(data)) |
251 | return -EIO; | 293 | return -EIO; |
252 | 294 | ||
@@ -255,14 +297,45 @@ int ptrace_detach(struct task_struct *child, unsigned int data) | |||
255 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | 297 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
256 | 298 | ||
257 | write_lock_irq(&tasklist_lock); | 299 | write_lock_irq(&tasklist_lock); |
258 | /* protect against de_thread()->release_task() */ | 300 | /* |
259 | if (child->ptrace) | 301 | * This child can be already killed. Make sure de_thread() or |
260 | __ptrace_detach(child, data); | 302 | * our sub-thread doing do_wait() didn't do release_task() yet. |
303 | */ | ||
304 | if (child->ptrace) { | ||
305 | child->exit_code = data; | ||
306 | dead = __ptrace_detach(current, child); | ||
307 | } | ||
261 | write_unlock_irq(&tasklist_lock); | 308 | write_unlock_irq(&tasklist_lock); |
262 | 309 | ||
310 | if (unlikely(dead)) | ||
311 | release_task(child); | ||
312 | |||
263 | return 0; | 313 | return 0; |
264 | } | 314 | } |
265 | 315 | ||
316 | /* | ||
317 | * Detach all tasks we were using ptrace on. | ||
318 | */ | ||
319 | void exit_ptrace(struct task_struct *tracer) | ||
320 | { | ||
321 | struct task_struct *p, *n; | ||
322 | LIST_HEAD(ptrace_dead); | ||
323 | |||
324 | write_lock_irq(&tasklist_lock); | ||
325 | list_for_each_entry_safe(p, n, &tracer->ptraced, ptrace_entry) { | ||
326 | if (__ptrace_detach(tracer, p)) | ||
327 | list_add(&p->ptrace_entry, &ptrace_dead); | ||
328 | } | ||
329 | write_unlock_irq(&tasklist_lock); | ||
330 | |||
331 | BUG_ON(!list_empty(&tracer->ptraced)); | ||
332 | |||
333 | list_for_each_entry_safe(p, n, &ptrace_dead, ptrace_entry) { | ||
334 | list_del_init(&p->ptrace_entry); | ||
335 | release_task(p); | ||
336 | } | ||
337 | } | ||
338 | |||
266 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) | 339 | int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len) |
267 | { | 340 | { |
268 | int copied = 0; | 341 | int copied = 0; |
@@ -283,7 +356,7 @@ int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst | |||
283 | copied += retval; | 356 | copied += retval; |
284 | src += retval; | 357 | src += retval; |
285 | dst += retval; | 358 | dst += retval; |
286 | len -= retval; | 359 | len -= retval; |
287 | } | 360 | } |
288 | return copied; | 361 | return copied; |
289 | } | 362 | } |
@@ -308,7 +381,7 @@ int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long ds | |||
308 | copied += retval; | 381 | copied += retval; |
309 | src += retval; | 382 | src += retval; |
310 | dst += retval; | 383 | dst += retval; |
311 | len -= retval; | 384 | len -= retval; |
312 | } | 385 | } |
313 | return copied; | 386 | return copied; |
314 | } | 387 | } |
@@ -421,9 +494,9 @@ static int ptrace_resume(struct task_struct *child, long request, long data) | |||
421 | if (unlikely(!arch_has_single_step())) | 494 | if (unlikely(!arch_has_single_step())) |
422 | return -EIO; | 495 | return -EIO; |
423 | user_enable_single_step(child); | 496 | user_enable_single_step(child); |
424 | } | 497 | } else { |
425 | else | ||
426 | user_disable_single_step(child); | 498 | user_disable_single_step(child); |
499 | } | ||
427 | 500 | ||
428 | child->exit_code = data; | 501 | child->exit_code = data; |
429 | wake_up_process(child); | 502 | wake_up_process(child); |
@@ -531,10 +604,11 @@ repeat: | |||
531 | ret = security_ptrace_traceme(current->parent); | 604 | ret = security_ptrace_traceme(current->parent); |
532 | 605 | ||
533 | /* | 606 | /* |
534 | * Set the ptrace bit in the process ptrace flags. | 607 | * Check PF_EXITING to ensure ->real_parent has not passed |
535 | * Then link us on our parent's ptraced list. | 608 | * exit_ptrace(). Otherwise we don't report the error but |
609 | * pretend ->real_parent untraces us right after return. | ||
536 | */ | 610 | */ |
537 | if (!ret) { | 611 | if (!ret && !(current->real_parent->flags & PF_EXITING)) { |
538 | current->ptrace |= PT_PTRACED; | 612 | current->ptrace |= PT_PTRACED; |
539 | __ptrace_link(current, current->real_parent); | 613 | __ptrace_link(current, current->real_parent); |
540 | } | 614 | } |
@@ -612,8 +686,6 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data) | |||
612 | goto out_put_task_struct; | 686 | goto out_put_task_struct; |
613 | 687 | ||
614 | ret = arch_ptrace(child, request, addr, data); | 688 | ret = arch_ptrace(child, request, addr, data); |
615 | if (ret < 0) | ||
616 | goto out_put_task_struct; | ||
617 | 689 | ||
618 | out_put_task_struct: | 690 | out_put_task_struct: |
619 | put_task_struct(child); | 691 | put_task_struct(child); |