aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/elf.h6
-rw-r--r--include/linux/ptrace.h15
-rw-r--r--kernel/ptrace.c88
3 files changed, 108 insertions, 1 deletions
diff --git a/include/linux/elf.h b/include/linux/elf.h
index a8c4af073ce9..d8e6e61ad9ff 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -349,7 +349,11 @@ typedef struct elf64_shdr {
349#define ELF_OSABI ELFOSABI_NONE 349#define ELF_OSABI ELFOSABI_NONE
350#endif 350#endif
351 351
352/* Notes used in ET_CORE */ 352/*
353 * Notes used in ET_CORE. Architectures export some of the arch register sets
354 * using the corresponding note types via the PTRACE_GETREGSET and
355 * PTRACE_SETREGSET requests.
356 */
353#define NT_PRSTATUS 1 357#define NT_PRSTATUS 1
354#define NT_PRFPREG 2 358#define NT_PRFPREG 2
355#define NT_PRPSINFO 3 359#define NT_PRPSINFO 3
diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 56f2d63a5cbb..dbfa821d5a6e 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -27,6 +27,21 @@
27#define PTRACE_GETSIGINFO 0x4202 27#define PTRACE_GETSIGINFO 0x4202
28#define PTRACE_SETSIGINFO 0x4203 28#define PTRACE_SETSIGINFO 0x4203
29 29
30/*
31 * Generic ptrace interface that exports the architecture specific regsets
32 * using the corresponding NT_* types (which are also used in the core dump).
33 *
34 * This interface usage is as follows:
35 * struct iovec iov = { buf, len};
36 *
37 * ret = ptrace(PTRACE_GETREGSET/PTRACE_SETREGSET, pid, NT_XXX_TYPE, &iov);
38 *
39 * On the successful completion, iov.len will be updated by the kernel,
40 * specifying how much the kernel has written/read to/from the user's iov.buf.
41 */
42#define PTRACE_GETREGSET 0x4204
43#define PTRACE_SETREGSET 0x4205
44
30/* options set using PTRACE_SETOPTIONS */ 45/* options set using PTRACE_SETOPTIONS */
31#define PTRACE_O_TRACESYSGOOD 0x00000001 46#define PTRACE_O_TRACESYSGOOD 0x00000001
32#define PTRACE_O_TRACEFORK 0x00000002 47#define PTRACE_O_TRACEFORK 0x00000002
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 23bd09cd042e..13b4554d8fbb 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -22,6 +22,7 @@
22#include <linux/pid_namespace.h> 22#include <linux/pid_namespace.h>
23#include <linux/syscalls.h> 23#include <linux/syscalls.h>
24#include <linux/uaccess.h> 24#include <linux/uaccess.h>
25#include <linux/regset.h>
25 26
26 27
27/* 28/*
@@ -511,6 +512,47 @@ static int ptrace_resume(struct task_struct *child, long request, long data)
511 return 0; 512 return 0;
512} 513}
513 514
515#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
516
517static const struct user_regset *
518find_regset(const struct user_regset_view *view, unsigned int type)
519{
520 const struct user_regset *regset;
521 int n;
522
523 for (n = 0; n < view->n; ++n) {
524 regset = view->regsets + n;
525 if (regset->core_note_type == type)
526 return regset;
527 }
528
529 return NULL;
530}
531
532static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
533 struct iovec *kiov)
534{
535 const struct user_regset_view *view = task_user_regset_view(task);
536 const struct user_regset *regset = find_regset(view, type);
537 int regset_no;
538
539 if (!regset || (kiov->iov_len % regset->size) != 0)
540 return -EIO;
541
542 regset_no = regset - view->regsets;
543 kiov->iov_len = min(kiov->iov_len,
544 (__kernel_size_t) (regset->n * regset->size));
545
546 if (req == PTRACE_GETREGSET)
547 return copy_regset_to_user(task, view, regset_no, 0,
548 kiov->iov_len, kiov->iov_base);
549 else
550 return copy_regset_from_user(task, view, regset_no, 0,
551 kiov->iov_len, kiov->iov_base);
552}
553
554#endif
555
514int ptrace_request(struct task_struct *child, long request, 556int ptrace_request(struct task_struct *child, long request,
515 long addr, long data) 557 long addr, long data)
516{ 558{
@@ -573,6 +615,26 @@ int ptrace_request(struct task_struct *child, long request,
573 return 0; 615 return 0;
574 return ptrace_resume(child, request, SIGKILL); 616 return ptrace_resume(child, request, SIGKILL);
575 617
618#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
619 case PTRACE_GETREGSET:
620 case PTRACE_SETREGSET:
621 {
622 struct iovec kiov;
623 struct iovec __user *uiov = (struct iovec __user *) data;
624
625 if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
626 return -EFAULT;
627
628 if (__get_user(kiov.iov_base, &uiov->iov_base) ||
629 __get_user(kiov.iov_len, &uiov->iov_len))
630 return -EFAULT;
631
632 ret = ptrace_regset(child, request, addr, &kiov);
633 if (!ret)
634 ret = __put_user(kiov.iov_len, &uiov->iov_len);
635 break;
636 }
637#endif
576 default: 638 default:
577 break; 639 break;
578 } 640 }
@@ -711,6 +773,32 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
711 else 773 else
712 ret = ptrace_setsiginfo(child, &siginfo); 774 ret = ptrace_setsiginfo(child, &siginfo);
713 break; 775 break;
776#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
777 case PTRACE_GETREGSET:
778 case PTRACE_SETREGSET:
779 {
780 struct iovec kiov;
781 struct compat_iovec __user *uiov =
782 (struct compat_iovec __user *) datap;
783 compat_uptr_t ptr;
784 compat_size_t len;
785
786 if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
787 return -EFAULT;
788
789 if (__get_user(ptr, &uiov->iov_base) ||
790 __get_user(len, &uiov->iov_len))
791 return -EFAULT;
792
793 kiov.iov_base = compat_ptr(ptr);
794 kiov.iov_len = len;
795
796 ret = ptrace_regset(child, request, addr, &kiov);
797 if (!ret)
798 ret = __put_user(kiov.iov_len, &uiov->iov_len);
799 break;
800 }
801#endif
714 802
715 default: 803 default:
716 ret = ptrace_request(child, request, addr, data); 804 ret = ptrace_request(child, request, addr, data);