diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 77 |
1 files changed, 46 insertions, 31 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 656476eedb1b..cceaf09ac413 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -408,54 +408,62 @@ int ptrace_request(struct task_struct *child, long request, | |||
408 | return ret; | 408 | return ret; |
409 | } | 409 | } |
410 | 410 | ||
411 | #ifndef __ARCH_SYS_PTRACE | 411 | /** |
412 | static int ptrace_get_task_struct(long request, long pid, | 412 | * ptrace_traceme -- helper for PTRACE_TRACEME |
413 | struct task_struct **childp) | 413 | * |
414 | * Performs checks and sets PT_PTRACED. | ||
415 | * Should be used by all ptrace implementations for PTRACE_TRACEME. | ||
416 | */ | ||
417 | int ptrace_traceme(void) | ||
414 | { | 418 | { |
415 | struct task_struct *child; | ||
416 | int ret; | 419 | int ret; |
417 | 420 | ||
418 | /* | 421 | /* |
419 | * Callers use child == NULL as an indication to exit early even | 422 | * Are we already being traced? |
420 | * when the return value is 0, so make sure it is non-NULL here. | 423 | */ |
424 | if (current->ptrace & PT_PTRACED) | ||
425 | return -EPERM; | ||
426 | ret = security_ptrace(current->parent, current); | ||
427 | if (ret) | ||
428 | return -EPERM; | ||
429 | /* | ||
430 | * Set the ptrace bit in the process ptrace flags. | ||
421 | */ | 431 | */ |
422 | *childp = NULL; | 432 | current->ptrace |= PT_PTRACED; |
433 | return 0; | ||
434 | } | ||
423 | 435 | ||
424 | if (request == PTRACE_TRACEME) { | 436 | /** |
425 | /* | 437 | * ptrace_get_task_struct -- grab a task struct reference for ptrace |
426 | * Are we already being traced? | 438 | * @pid: process id to grab a task_struct reference of |
427 | */ | 439 | * |
428 | if (current->ptrace & PT_PTRACED) | 440 | * This function is a helper for ptrace implementations. It checks |
429 | return -EPERM; | 441 | * permissions and then grabs a task struct for use of the actual |
430 | ret = security_ptrace(current->parent, current); | 442 | * ptrace implementation. |
431 | if (ret) | 443 | * |
432 | return -EPERM; | 444 | * Returns the task_struct for @pid or an ERR_PTR() on failure. |
433 | /* | 445 | */ |
434 | * Set the ptrace bit in the process ptrace flags. | 446 | struct task_struct *ptrace_get_task_struct(pid_t pid) |
435 | */ | 447 | { |
436 | current->ptrace |= PT_PTRACED; | 448 | struct task_struct *child; |
437 | return 0; | ||
438 | } | ||
439 | 449 | ||
440 | /* | 450 | /* |
441 | * You may not mess with init | 451 | * Tracing init is not allowed. |
442 | */ | 452 | */ |
443 | if (pid == 1) | 453 | if (pid == 1) |
444 | return -EPERM; | 454 | return ERR_PTR(-EPERM); |
445 | 455 | ||
446 | ret = -ESRCH; | ||
447 | read_lock(&tasklist_lock); | 456 | read_lock(&tasklist_lock); |
448 | child = find_task_by_pid(pid); | 457 | child = find_task_by_pid(pid); |
449 | if (child) | 458 | if (child) |
450 | get_task_struct(child); | 459 | get_task_struct(child); |
451 | read_unlock(&tasklist_lock); | 460 | read_unlock(&tasklist_lock); |
452 | if (!child) | 461 | if (!child) |
453 | return -ESRCH; | 462 | return ERR_PTR(-ESRCH); |
454 | 463 | return child; | |
455 | *childp = child; | ||
456 | return 0; | ||
457 | } | 464 | } |
458 | 465 | ||
466 | #ifndef __ARCH_SYS_PTRACE | ||
459 | asmlinkage long sys_ptrace(long request, long pid, long addr, long data) | 467 | asmlinkage long sys_ptrace(long request, long pid, long addr, long data) |
460 | { | 468 | { |
461 | struct task_struct *child; | 469 | struct task_struct *child; |
@@ -465,9 +473,16 @@ asmlinkage long sys_ptrace(long request, long pid, long addr, long data) | |||
465 | * This lock_kernel fixes a subtle race with suid exec | 473 | * This lock_kernel fixes a subtle race with suid exec |
466 | */ | 474 | */ |
467 | lock_kernel(); | 475 | lock_kernel(); |
468 | ret = ptrace_get_task_struct(request, pid, &child); | 476 | if (request == PTRACE_TRACEME) { |
469 | if (!child) | 477 | ret = ptrace_traceme(); |
470 | goto out; | 478 | goto out; |
479 | } | ||
480 | |||
481 | child = ptrace_get_task_struct(pid); | ||
482 | if (IS_ERR(child)) { | ||
483 | ret = PTR_ERR(child); | ||
484 | goto out; | ||
485 | } | ||
471 | 486 | ||
472 | if (request == PTRACE_ATTACH) { | 487 | if (request == PTRACE_ATTACH) { |
473 | ret = ptrace_attach(child); | 488 | ret = ptrace_attach(child); |