aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Lutomirski <luto@kernel.org>2021-05-16 13:41:17 -0700
committerAndy Lutomirski <luto@kernel.org>2021-05-16 13:44:33 -0700
commit2280f3f0171419981d72bcec49db68570c7289fe (patch)
treef8c1c54d9d1c792f441f605f8dff76edd13f196d
parentd5271e495182520913ff5e7a319a5863a009ab56 (diff)
downloadlinux-x86/ptrace.tar.gz
ptrace, x86: Add improved regset APIx86/ptrace
-rw-r--r--include/linux/regset.h27
-rw-r--r--include/uapi/linux/ptrace.h11
-rw-r--r--kernel/ptrace.c10
3 files changed, 40 insertions, 8 deletions
diff --git a/include/linux/regset.h b/include/linux/regset.h
index 8e0c9febf495f5..8b07102e104a94 100644
--- a/include/linux/regset.h
+++ b/include/linux/regset.h
@@ -195,15 +195,34 @@ struct user_regset_view {
* implementation is machine-dependent but its interface is universal.
*/
/**
- * task_user_regset_view - Return the process's native regset view.
+ * task_user_regset_view - Return a guess for the process's regset view.
* @tsk: a thread of the process in question
*
- * Return the &struct user_regset_view that is native for the given process.
- * For example, what it would access when it called ptrace().
- * Throughout the life of the process, this only changes at exec.
+ * Return the &struct user_regset_view that is use used by the historically
+ * broken PTRACE_GET/SETREGSET APIs and by core dumps for the given process.
+ *
+ * This function is inherently defective for ptrace(), as there is no way
+ * to ensure that it returns the regset that the caller expects. For
+ * example, on x86_64, a task's native regset view is always the x86_64
+ * view, but ptrace() has no way to request it.
*/
const struct user_regset_view *task_user_regset_view(struct task_struct *tsk);
+/**
+ * task_machine_regset_view - Return the requested regset view for a process.
+ * @tsk: a thread of the process in question
+ *
+ * Return the requested &struct user_regset_view if valid for the target
+ * task or an error.
+ */
+#ifndef task_get_regset_view
+static inline const struct user_regset_view *
+task_machine_regset_view(task_struct *tsk, u16 e_machine)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif
+
/*
* These are helpers for writing regset get/set functions in arch code.
diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h
index fb810650900029..3b4edc8e6f2c73 100644
--- a/include/uapi/linux/ptrace.h
+++ b/include/uapi/linux/ptrace.h
@@ -65,6 +65,17 @@ struct ptrace_peeksiginfo_args {
#define PTRACE_SETSIGMASK 0x420b
#define PTRACE_SECCOMP_GET_FILTER 0x420c
+#define PTRACE_GETREGSET2 0x420f
+#define PTRACE_SETREGSET2 0x4210
+
+struct ptrace_regset2 {
+ __u64 uptr; /* pointer to user buffer */
+ __u64 ulen; /* size of user bufer */
+
+ __u32 e_machine; /* ELF machine of the requested regset view */
+ __u32 regset_type; /* regset type within the requested view */
+};
+
/* Read signals from a shared (process wide) queue */
#define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index d49bfa1e53e6b4..e869239209bb05 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -807,10 +807,10 @@ find_regset(const struct user_regset_view *view, unsigned int type)
return NULL;
}
-static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
+static int ptrace_regset(struct task_struct *task, int req,
+ const struct user_regset_view *view, 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;
@@ -1038,7 +1038,8 @@ int ptrace_request(struct task_struct *child, long request,
__get_user(kiov.iov_len, &uiov->iov_len))
return -EFAULT;
- ret = ptrace_regset(child, request, addr, &kiov);
+ ret = ptrace_regset(child, request,
+ task_user_regset_view(child), addr, &kiov);
if (!ret)
ret = __put_user(kiov.iov_len, &uiov->iov_len);
break;
@@ -1207,7 +1208,8 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
kiov.iov_base = compat_ptr(ptr);
kiov.iov_len = len;
- ret = ptrace_regset(child, request, addr, &kiov);
+ ret = ptrace_regset(child, request,
+ task_user_regset_view(child), addr, &kiov);
if (!ret)
ret = __put_user(kiov.iov_len, &uiov->iov_len);
break;