aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Chagin <dchagin@FreeBSD.org>2023-05-18 17:02:35 +0000
committerDmitry Chagin <dchagin@FreeBSD.org>2023-06-29 08:15:59 +0000
commit050cd99a65a565b678d47c0e6980e14f4fca9112 (patch)
treec97bce57acdd0d99f2753f93a33bb7cdfc6910c4
parentd056fef82aefb703441a33af719db9182ab566bc (diff)
downloadsrc-050cd99a65a565b678d47c0e6980e14f4fca9112.tar.gz
src-050cd99a65a565b678d47c0e6980e14f4fca9112.zip
linux(4): Implement ptrace_pokeusr for x86_64
Differential Revision: https://reviews.freebsd.org/D40097 MFC after: 1 week (cherry picked from commit 1d76741520c031730319ed976a6c394213991504)
-rw-r--r--sys/amd64/linux/linux_machdep.c73
1 files changed, 70 insertions, 3 deletions
diff --git a/sys/amd64/linux/linux_machdep.c b/sys/amd64/linux/linux_machdep.c
index ddb291169a03..6ac0ab0cd3d7 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, &reg);
+ *(&reg.r15 + ((uintptr_t)addr / sizeof(reg.r15))) = (uint64_t)data;
+ linux_to_bsd_regset(&b_reg1, &reg);
+ 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