linux(4): Implement ptrace_pokeusr for x86_64

Differential Revision:	https://reviews.freebsd.org/D40097
MFC after:		1 week
This commit is contained in:
Dmitry Chagin 2023-05-18 20:02:35 +03:00
parent 3d0addcd35
commit 1d76741520

View File

@ -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