The branch main has been updated by dchagin:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=1d76741520c031730319ed976a6c394213991504

commit 1d76741520c031730319ed976a6c394213991504
Author:     Dmitry Chagin <dcha...@freebsd.org>
AuthorDate: 2023-05-18 17:02:35 +0000
Commit:     Dmitry Chagin <dcha...@freebsd.org>
CommitDate: 2023-05-18 17:02:35 +0000

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

Reply via email to