diff options
Diffstat (limited to 'kernel/ptrace.c')
-rw-r--r-- | kernel/ptrace.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index c719bb9d79ab..e6e9b8be4b05 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -366,12 +366,73 @@ static int ptrace_setsiginfo(struct task_struct *child, siginfo_t __user * data) | |||
366 | return error; | 366 | return error; |
367 | } | 367 | } |
368 | 368 | ||
369 | |||
370 | #ifdef PTRACE_SINGLESTEP | ||
371 | #define is_singlestep(request) ((request) == PTRACE_SINGLESTEP) | ||
372 | #else | ||
373 | #define is_singlestep(request) 0 | ||
374 | #endif | ||
375 | |||
376 | #ifdef PTRACE_SINGLEBLOCK | ||
377 | #define is_singleblock(request) ((request) == PTRACE_SINGLEBLOCK) | ||
378 | #else | ||
379 | #define is_singleblock(request) 0 | ||
380 | #endif | ||
381 | |||
382 | #ifdef PTRACE_SYSEMU | ||
383 | #define is_sysemu_singlestep(request) ((request) == PTRACE_SYSEMU_SINGLESTEP) | ||
384 | #else | ||
385 | #define is_sysemu_singlestep(request) 0 | ||
386 | #endif | ||
387 | |||
388 | static int ptrace_resume(struct task_struct *child, long request, long data) | ||
389 | { | ||
390 | if (!valid_signal(data)) | ||
391 | return -EIO; | ||
392 | |||
393 | if (request == PTRACE_SYSCALL) | ||
394 | set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
395 | else | ||
396 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); | ||
397 | |||
398 | #ifdef TIF_SYSCALL_EMU | ||
399 | if (request == PTRACE_SYSEMU || request == PTRACE_SYSEMU_SINGLESTEP) | ||
400 | set_tsk_thread_flag(child, TIF_SYSCALL_EMU); | ||
401 | else | ||
402 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); | ||
403 | #endif | ||
404 | |||
405 | if (is_singleblock(request)) { | ||
406 | if (unlikely(!arch_has_block_step())) | ||
407 | return -EIO; | ||
408 | user_enable_block_step(child); | ||
409 | } else if (is_singlestep(request) || is_sysemu_singlestep(request)) { | ||
410 | if (unlikely(!arch_has_single_step())) | ||
411 | return -EIO; | ||
412 | user_enable_single_step(child); | ||
413 | } | ||
414 | else | ||
415 | user_disable_single_step(child); | ||
416 | |||
417 | child->exit_code = data; | ||
418 | wake_up_process(child); | ||
419 | |||
420 | return 0; | ||
421 | } | ||
422 | |||
369 | int ptrace_request(struct task_struct *child, long request, | 423 | int ptrace_request(struct task_struct *child, long request, |
370 | long addr, long data) | 424 | long addr, long data) |
371 | { | 425 | { |
372 | int ret = -EIO; | 426 | int ret = -EIO; |
373 | 427 | ||
374 | switch (request) { | 428 | switch (request) { |
429 | case PTRACE_PEEKTEXT: | ||
430 | case PTRACE_PEEKDATA: | ||
431 | return generic_ptrace_peekdata(child, addr, data); | ||
432 | case PTRACE_POKETEXT: | ||
433 | case PTRACE_POKEDATA: | ||
434 | return generic_ptrace_pokedata(child, addr, data); | ||
435 | |||
375 | #ifdef PTRACE_OLDSETOPTIONS | 436 | #ifdef PTRACE_OLDSETOPTIONS |
376 | case PTRACE_OLDSETOPTIONS: | 437 | case PTRACE_OLDSETOPTIONS: |
377 | #endif | 438 | #endif |
@@ -390,6 +451,26 @@ int ptrace_request(struct task_struct *child, long request, | |||
390 | case PTRACE_DETACH: /* detach a process that was attached. */ | 451 | case PTRACE_DETACH: /* detach a process that was attached. */ |
391 | ret = ptrace_detach(child, data); | 452 | ret = ptrace_detach(child, data); |
392 | break; | 453 | break; |
454 | |||
455 | #ifdef PTRACE_SINGLESTEP | ||
456 | case PTRACE_SINGLESTEP: | ||
457 | #endif | ||
458 | #ifdef PTRACE_SINGLEBLOCK | ||
459 | case PTRACE_SINGLEBLOCK: | ||
460 | #endif | ||
461 | #ifdef PTRACE_SYSEMU | ||
462 | case PTRACE_SYSEMU: | ||
463 | case PTRACE_SYSEMU_SINGLESTEP: | ||
464 | #endif | ||
465 | case PTRACE_SYSCALL: | ||
466 | case PTRACE_CONT: | ||
467 | return ptrace_resume(child, request, data); | ||
468 | |||
469 | case PTRACE_KILL: | ||
470 | if (child->exit_state) /* already dead */ | ||
471 | return 0; | ||
472 | return ptrace_resume(child, request, SIGKILL); | ||
473 | |||
393 | default: | 474 | default: |
394 | break; | 475 | break; |
395 | } | 476 | } |
@@ -526,3 +607,87 @@ int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data) | |||
526 | copied = access_process_vm(tsk, addr, &data, sizeof(data), 1); | 607 | copied = access_process_vm(tsk, addr, &data, sizeof(data), 1); |
527 | return (copied == sizeof(data)) ? 0 : -EIO; | 608 | return (copied == sizeof(data)) ? 0 : -EIO; |
528 | } | 609 | } |
610 | |||
611 | #ifdef CONFIG_COMPAT | ||
612 | #include <linux/compat.h> | ||
613 | |||
614 | int compat_ptrace_request(struct task_struct *child, compat_long_t request, | ||
615 | compat_ulong_t addr, compat_ulong_t data) | ||
616 | { | ||
617 | compat_ulong_t __user *datap = compat_ptr(data); | ||
618 | compat_ulong_t word; | ||
619 | int ret; | ||
620 | |||
621 | switch (request) { | ||
622 | case PTRACE_PEEKTEXT: | ||
623 | case PTRACE_PEEKDATA: | ||
624 | ret = access_process_vm(child, addr, &word, sizeof(word), 0); | ||
625 | if (ret != sizeof(word)) | ||
626 | ret = -EIO; | ||
627 | else | ||
628 | ret = put_user(word, datap); | ||
629 | break; | ||
630 | |||
631 | case PTRACE_POKETEXT: | ||
632 | case PTRACE_POKEDATA: | ||
633 | ret = access_process_vm(child, addr, &data, sizeof(data), 1); | ||
634 | ret = (ret != sizeof(data) ? -EIO : 0); | ||
635 | break; | ||
636 | |||
637 | case PTRACE_GETEVENTMSG: | ||
638 | ret = put_user((compat_ulong_t) child->ptrace_message, datap); | ||
639 | break; | ||
640 | |||
641 | default: | ||
642 | ret = ptrace_request(child, request, addr, data); | ||
643 | } | ||
644 | |||
645 | return ret; | ||
646 | } | ||
647 | |||
648 | #ifdef __ARCH_WANT_COMPAT_SYS_PTRACE | ||
649 | asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid, | ||
650 | compat_long_t addr, compat_long_t data) | ||
651 | { | ||
652 | struct task_struct *child; | ||
653 | long ret; | ||
654 | |||
655 | /* | ||
656 | * This lock_kernel fixes a subtle race with suid exec | ||
657 | */ | ||
658 | lock_kernel(); | ||
659 | if (request == PTRACE_TRACEME) { | ||
660 | ret = ptrace_traceme(); | ||
661 | goto out; | ||
662 | } | ||
663 | |||
664 | child = ptrace_get_task_struct(pid); | ||
665 | if (IS_ERR(child)) { | ||
666 | ret = PTR_ERR(child); | ||
667 | goto out; | ||
668 | } | ||
669 | |||
670 | if (request == PTRACE_ATTACH) { | ||
671 | ret = ptrace_attach(child); | ||
672 | /* | ||
673 | * Some architectures need to do book-keeping after | ||
674 | * a ptrace attach. | ||
675 | */ | ||
676 | if (!ret) | ||
677 | arch_ptrace_attach(child); | ||
678 | goto out_put_task_struct; | ||
679 | } | ||
680 | |||
681 | ret = ptrace_check_attach(child, request == PTRACE_KILL); | ||
682 | if (!ret) | ||
683 | ret = compat_arch_ptrace(child, request, addr, data); | ||
684 | |||
685 | out_put_task_struct: | ||
686 | put_task_struct(child); | ||
687 | out: | ||
688 | unlock_kernel(); | ||
689 | return ret; | ||
690 | } | ||
691 | #endif /* __ARCH_WANT_COMPAT_SYS_PTRACE */ | ||
692 | |||
693 | #endif /* CONFIG_COMPAT */ | ||