|
@@ -22,6 +22,7 @@
|
|
|
#include <linux/pid_namespace.h>
|
|
|
#include <linux/syscalls.h>
|
|
|
#include <linux/uaccess.h>
|
|
|
+#include <linux/regset.h>
|
|
|
|
|
|
|
|
|
/*
|
|
@@ -511,6 +512,47 @@ static int ptrace_resume(struct task_struct *child, long request, long data)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
|
|
|
+
|
|
|
+static const struct user_regset *
|
|
|
+find_regset(const struct user_regset_view *view, unsigned int type)
|
|
|
+{
|
|
|
+ const struct user_regset *regset;
|
|
|
+ int n;
|
|
|
+
|
|
|
+ for (n = 0; n < view->n; ++n) {
|
|
|
+ regset = view->regsets + n;
|
|
|
+ if (regset->core_note_type == type)
|
|
|
+ return regset;
|
|
|
+ }
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
|
|
|
+ struct iovec *kiov)
|
|
|
+{
|
|
|
+ const struct user_regset_view *view = task_user_regset_view(task);
|
|
|
+ const struct user_regset *regset = find_regset(view, type);
|
|
|
+ int regset_no;
|
|
|
+
|
|
|
+ if (!regset || (kiov->iov_len % regset->size) != 0)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ regset_no = regset - view->regsets;
|
|
|
+ kiov->iov_len = min(kiov->iov_len,
|
|
|
+ (__kernel_size_t) (regset->n * regset->size));
|
|
|
+
|
|
|
+ if (req == PTRACE_GETREGSET)
|
|
|
+ return copy_regset_to_user(task, view, regset_no, 0,
|
|
|
+ kiov->iov_len, kiov->iov_base);
|
|
|
+ else
|
|
|
+ return copy_regset_from_user(task, view, regset_no, 0,
|
|
|
+ kiov->iov_len, kiov->iov_base);
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
int ptrace_request(struct task_struct *child, long request,
|
|
|
long addr, long data)
|
|
|
{
|
|
@@ -573,6 +615,26 @@ int ptrace_request(struct task_struct *child, long request,
|
|
|
return 0;
|
|
|
return ptrace_resume(child, request, SIGKILL);
|
|
|
|
|
|
+#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
|
|
|
+ case PTRACE_GETREGSET:
|
|
|
+ case PTRACE_SETREGSET:
|
|
|
+ {
|
|
|
+ struct iovec kiov;
|
|
|
+ struct iovec __user *uiov = (struct iovec __user *) data;
|
|
|
+
|
|
|
+ if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ if (__get_user(kiov.iov_base, &uiov->iov_base) ||
|
|
|
+ __get_user(kiov.iov_len, &uiov->iov_len))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ ret = ptrace_regset(child, request, addr, &kiov);
|
|
|
+ if (!ret)
|
|
|
+ ret = __put_user(kiov.iov_len, &uiov->iov_len);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+#endif
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
@@ -711,6 +773,32 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
|
|
|
else
|
|
|
ret = ptrace_setsiginfo(child, &siginfo);
|
|
|
break;
|
|
|
+#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
|
|
|
+ case PTRACE_GETREGSET:
|
|
|
+ case PTRACE_SETREGSET:
|
|
|
+ {
|
|
|
+ struct iovec kiov;
|
|
|
+ struct compat_iovec __user *uiov =
|
|
|
+ (struct compat_iovec __user *) datap;
|
|
|
+ compat_uptr_t ptr;
|
|
|
+ compat_size_t len;
|
|
|
+
|
|
|
+ if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ if (__get_user(ptr, &uiov->iov_base) ||
|
|
|
+ __get_user(len, &uiov->iov_len))
|
|
|
+ return -EFAULT;
|
|
|
+
|
|
|
+ kiov.iov_base = compat_ptr(ptr);
|
|
|
+ kiov.iov_len = len;
|
|
|
+
|
|
|
+ ret = ptrace_regset(child, request, addr, &kiov);
|
|
|
+ if (!ret)
|
|
|
+ ret = __put_user(kiov.iov_len, &uiov->iov_len);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+#endif
|
|
|
|
|
|
default:
|
|
|
ret = ptrace_request(child, request, addr, data);
|