aboutsummaryrefslogtreecommitdiff
path: root/sys/amd64/linux/linux_machdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/amd64/linux/linux_machdep.c')
-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