diff options
author | Andy Lutomirski <luto@kernel.org> | 2021-05-16 13:41:17 -0700 |
---|---|---|
committer | Andy Lutomirski <luto@kernel.org> | 2021-05-16 13:44:33 -0700 |
commit | 2280f3f0171419981d72bcec49db68570c7289fe (patch) | |
tree | f8c1c54d9d1c792f441f605f8dff76edd13f196d | |
parent | d5271e495182520913ff5e7a319a5863a009ab56 (diff) | |
download | linux-x86/ptrace.tar.gz |
ptrace, x86: Add improved regset APIx86/ptrace
-rw-r--r-- | include/linux/regset.h | 27 | ||||
-rw-r--r-- | include/uapi/linux/ptrace.h | 11 | ||||
-rw-r--r-- | kernel/ptrace.c | 10 |
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; |