From 1d76741520c031730319ed976a6c394213991504 Mon Sep 17 00:00:00 2001 From: Dmitry Chagin Date: Thu, 18 May 2023 20:02:35 +0300 Subject: [PATCH] linux(4): Implement ptrace_pokeusr for x86_64 Differential Revision: https://reviews.freebsd.org/D40097 MFC after: 1 week --- sys/amd64/linux/linux_machdep.c | 73 +++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 3 deletions(-) diff --git a/sys/amd64/linux/linux_machdep.c b/sys/amd64/linux/linux_machdep.c index ddb291169a0..6ac0ab0cd3d 100644 --- a/sys/amd64/linux/linux_machdep.c +++ b/sys/amd64/linux/linux_machdep.c @@ -397,12 +397,79 @@ linux_ptrace_peekuser(struct thread *td, pid_t pid, void *addr, void *data) return (copyout(&val, data, sizeof(val))); } +static inline bool +linux_invalid_selector(u_short val) +{ + + return (val != 0 && ISPL(val) != SEL_UPL); +} + +struct linux_segreg_off { + uintptr_t reg; + bool is0; +}; + +const struct linux_segreg_off linux_segregs_off[] = { + { + .reg = offsetof(struct linux_pt_regset, gs), + .is0 = true, + }, + { + .reg = offsetof(struct linux_pt_regset, fs), + .is0 = true, + }, + { + .reg = offsetof(struct linux_pt_regset, ds), + .is0 = true, + }, + { + .reg = offsetof(struct linux_pt_regset, es), + .is0 = true, + }, + { + .reg = offsetof(struct linux_pt_regset, cs), + .is0 = false, + }, + { + .reg = offsetof(struct linux_pt_regset, ss), + .is0 = false, + }, +}; + int linux_ptrace_pokeuser(struct thread *td, pid_t pid, void *addr, void *data) { + struct linux_pt_regset reg; + struct reg b_reg, b_reg1; + int error, i; - LINUX_RATELIMIT_MSG_OPT1("PTRACE_POKEUSER offset %ld " - "not implemented; returning EINVAL", (uintptr_t)addr); - return (EINVAL); + if ((uintptr_t)addr & (sizeof(data) -1) || (uintptr_t)addr < 0) + return (EIO); + if ((uintptr_t)addr >= sizeof(struct linux_pt_regset)) { + LINUX_RATELIMIT_MSG_OPT1("PTRACE_POKEUSER offset %ld " + "not implemented; returning EINVAL", (uintptr_t)addr); + return (EINVAL); + } + + if (LINUX_URO(addr, fs_base)) + return (kern_ptrace(td, PT_SETFSBASE, pid, data, 0)); + if (LINUX_URO(addr, gs_base)) + return (kern_ptrace(td, PT_SETGSBASE, pid, data, 0)); + for (i = 0; i < nitems(linux_segregs_off); i++) { + if ((uintptr_t)addr == linux_segregs_off[i].reg) { + if (linux_invalid_selector((uintptr_t)data)) + return (EIO); + if (!linux_segregs_off[i].is0 && (uintptr_t)data == 0) + return (EIO); + } + } + if ((error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0)) != 0) + return (error); + bsd_to_linux_regset(&b_reg, ®); + *(®.r15 + ((uintptr_t)addr / sizeof(reg.r15))) = (uint64_t)data; + linux_to_bsd_regset(&b_reg1, ®); + b_reg1.r_err = b_reg.r_err; + b_reg1.r_trapno = b_reg.r_trapno; + return (kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0)); } #undef LINUX_URO -- 2.45.2