diff options
-rw-r--r-- | include/uapi/linux/ptrace.h | 12 | ||||
-rw-r--r-- | kernel/ptrace.c | 80 |
2 files changed, 92 insertions, 0 deletions
diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h index 022ab186a812..52ebcc89f306 100644 --- a/include/uapi/linux/ptrace.h +++ b/include/uapi/linux/ptrace.h | |||
@@ -5,6 +5,7 @@ | |||
5 | 5 | ||
6 | /* has the defines to get at the registers. */ | 6 | /* has the defines to get at the registers. */ |
7 | 7 | ||
8 | #include <linux/types.h> | ||
8 | 9 | ||
9 | #define PTRACE_TRACEME 0 | 10 | #define PTRACE_TRACEME 0 |
10 | #define PTRACE_PEEKTEXT 1 | 11 | #define PTRACE_PEEKTEXT 1 |
@@ -52,6 +53,17 @@ | |||
52 | #define PTRACE_INTERRUPT 0x4207 | 53 | #define PTRACE_INTERRUPT 0x4207 |
53 | #define PTRACE_LISTEN 0x4208 | 54 | #define PTRACE_LISTEN 0x4208 |
54 | 55 | ||
56 | #define PTRACE_PEEKSIGINFO 0x4209 | ||
57 | |||
58 | struct ptrace_peeksiginfo_args { | ||
59 | __u64 off; /* from which siginfo to start */ | ||
60 | __u32 flags; | ||
61 | __s32 nr; /* how may siginfos to take */ | ||
62 | }; | ||
63 | |||
64 | /* Read signals from a shared (process wide) queue */ | ||
65 | #define PTRACE_PEEKSIGINFO_SHARED (1 << 0) | ||
66 | |||
55 | /* Wait extended result codes for the above trace options. */ | 67 | /* Wait extended result codes for the above trace options. */ |
56 | #define PTRACE_EVENT_FORK 1 | 68 | #define PTRACE_EVENT_FORK 1 |
57 | #define PTRACE_EVENT_VFORK 2 | 69 | #define PTRACE_EVENT_VFORK 2 |
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index acbd28424d81..17ae54da0ec2 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/regset.h> | 24 | #include <linux/regset.h> |
25 | #include <linux/hw_breakpoint.h> | 25 | #include <linux/hw_breakpoint.h> |
26 | #include <linux/cn_proc.h> | 26 | #include <linux/cn_proc.h> |
27 | #include <linux/compat.h> | ||
27 | 28 | ||
28 | 29 | ||
29 | static int ptrace_trapping_sleep_fn(void *flags) | 30 | static int ptrace_trapping_sleep_fn(void *flags) |
@@ -618,6 +619,81 @@ static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info) | |||
618 | return error; | 619 | return error; |
619 | } | 620 | } |
620 | 621 | ||
622 | static int ptrace_peek_siginfo(struct task_struct *child, | ||
623 | unsigned long addr, | ||
624 | unsigned long data) | ||
625 | { | ||
626 | struct ptrace_peeksiginfo_args arg; | ||
627 | struct sigpending *pending; | ||
628 | struct sigqueue *q; | ||
629 | int ret, i; | ||
630 | |||
631 | ret = copy_from_user(&arg, (void __user *) addr, | ||
632 | sizeof(struct ptrace_peeksiginfo_args)); | ||
633 | if (ret) | ||
634 | return -EFAULT; | ||
635 | |||
636 | if (arg.flags & ~PTRACE_PEEKSIGINFO_SHARED) | ||
637 | return -EINVAL; /* unknown flags */ | ||
638 | |||
639 | if (arg.nr < 0) | ||
640 | return -EINVAL; | ||
641 | |||
642 | if (arg.flags & PTRACE_PEEKSIGINFO_SHARED) | ||
643 | pending = &child->signal->shared_pending; | ||
644 | else | ||
645 | pending = &child->pending; | ||
646 | |||
647 | for (i = 0; i < arg.nr; ) { | ||
648 | siginfo_t info; | ||
649 | s32 off = arg.off + i; | ||
650 | |||
651 | spin_lock_irq(&child->sighand->siglock); | ||
652 | list_for_each_entry(q, &pending->list, list) { | ||
653 | if (!off--) { | ||
654 | copy_siginfo(&info, &q->info); | ||
655 | break; | ||
656 | } | ||
657 | } | ||
658 | spin_unlock_irq(&child->sighand->siglock); | ||
659 | |||
660 | if (off >= 0) /* beyond the end of the list */ | ||
661 | break; | ||
662 | |||
663 | #ifdef CONFIG_COMPAT | ||
664 | if (unlikely(is_compat_task())) { | ||
665 | compat_siginfo_t __user *uinfo = compat_ptr(data); | ||
666 | |||
667 | ret = copy_siginfo_to_user32(uinfo, &info); | ||
668 | ret |= __put_user(info.si_code, &uinfo->si_code); | ||
669 | } else | ||
670 | #endif | ||
671 | { | ||
672 | siginfo_t __user *uinfo = (siginfo_t __user *) data; | ||
673 | |||
674 | ret = copy_siginfo_to_user(uinfo, &info); | ||
675 | ret |= __put_user(info.si_code, &uinfo->si_code); | ||
676 | } | ||
677 | |||
678 | if (ret) { | ||
679 | ret = -EFAULT; | ||
680 | break; | ||
681 | } | ||
682 | |||
683 | data += sizeof(siginfo_t); | ||
684 | i++; | ||
685 | |||
686 | if (signal_pending(current)) | ||
687 | break; | ||
688 | |||
689 | cond_resched(); | ||
690 | } | ||
691 | |||
692 | if (i > 0) | ||
693 | return i; | ||
694 | |||
695 | return ret; | ||
696 | } | ||
621 | 697 | ||
622 | #ifdef PTRACE_SINGLESTEP | 698 | #ifdef PTRACE_SINGLESTEP |
623 | #define is_singlestep(request) ((request) == PTRACE_SINGLESTEP) | 699 | #define is_singlestep(request) ((request) == PTRACE_SINGLESTEP) |
@@ -748,6 +824,10 @@ int ptrace_request(struct task_struct *child, long request, | |||
748 | ret = put_user(child->ptrace_message, datalp); | 824 | ret = put_user(child->ptrace_message, datalp); |
749 | break; | 825 | break; |
750 | 826 | ||
827 | case PTRACE_PEEKSIGINFO: | ||
828 | ret = ptrace_peek_siginfo(child, addr, data); | ||
829 | break; | ||
830 | |||
751 | case PTRACE_GETSIGINFO: | 831 | case PTRACE_GETSIGINFO: |
752 | ret = ptrace_getsiginfo(child, &siginfo); | 832 | ret = ptrace_getsiginfo(child, &siginfo); |
753 | if (!ret) | 833 | if (!ret) |