git: 050cd99a65a5 - stable/13 - linux(4): Implement ptrace_pokeusr for x86_64

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Thu, 29 Jun 2023 08:20:37 UTC
The branch stable/13 has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=050cd99a65a565b678d47c0e6980e14f4fca9112

commit 050cd99a65a565b678d47c0e6980e14f4fca9112
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2023-05-18 17:02:35 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2023-06-29 08:15:59 +0000

    linux(4): Implement ptrace_pokeusr for x86_64
    
    Differential Revision:  https://reviews.freebsd.org/D40097
    MFC after:              1 week
    
    (cherry picked from commit 1d76741520c031730319ed976a6c394213991504)
---
 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