diff options
Diffstat (limited to 'kernel/ptrace.c')
| -rw-r--r-- | kernel/ptrace.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 23bd09cd042e..42ad8ae729a0 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 | |||
| 517 | static const struct user_regset * | ||
| 518 | find_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 | |||
| 532 | static 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 -EINVAL; | ||
| 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 | |||
| 514 | int ptrace_request(struct task_struct *child, long request, | 556 | int 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); |
