[PATCH -next 2/2] riscv: ptrace: Implement Compat PTRACE_{PEEK,POKE}USR

Yipeng Zou zouyipeng at huawei.com
Sun Aug 21 20:01:05 PDT 2022


Implement Compat PTRACE_{PEEK,POKE}USR for RV32 user app to peek/poke
tracee's space register.

As the Linux manual page say: the addr is define to a word offset in
the tracee's USER area, In riscv it's defined in:

struct user {
	struct user_regs {
		compat_ulong pc;	// addr 0
		compat_ulong ra;	// addr 4
		...
		...
		compat_ulong t6;	// addr 124
	}
	struct d_ext_state {
		u64 f[0]		// addr 128
		u64 f[1]		// addr 136
		...
		...
		u32 fcsr;		// addr 384 read-only
	}
}

Signed-off-by: Yipeng Zou <zouyipeng at huawei.com>
---
 arch/riscv/include/asm/compat.h | 13 ++++++++++
 arch/riscv/kernel/ptrace.c      | 45 +++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+)

diff --git a/arch/riscv/include/asm/compat.h b/arch/riscv/include/asm/compat.h
index 2ac955b51148..a6fc3706a26d 100644
--- a/arch/riscv/include/asm/compat.h
+++ b/arch/riscv/include/asm/compat.h
@@ -52,6 +52,19 @@ struct compat_user_regs_struct {
 	compat_ulong_t t6;
 };
 
+static inline compat_ulong_t creg_get_by_offset(struct pt_regs *regs, compat_ulong_t offset)
+{
+	unsigned long reg_value = *((unsigned long *)regs + offset / sizeof(compat_ulong_t));
+
+	return (compat_ulong_t)reg_value;
+}
+
+static inline void regs_set_by_offset(struct pt_regs *regs, compat_ulong_t offset,
+				      compat_ulong_t data)
+{
+	*((unsigned long *)regs + offset / sizeof(compat_ulong_t)) = (unsigned long)data;
+}
+
 static inline void regs_to_cregs(struct compat_user_regs_struct *cregs,
 				 struct pt_regs *regs)
 {
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
index 6dc0687e419f..22fedc13cbd2 100644
--- a/arch/riscv/kernel/ptrace.c
+++ b/arch/riscv/kernel/ptrace.c
@@ -391,12 +391,57 @@ static const struct user_regset_view compat_riscv_user_native_view = {
 	.n = ARRAY_SIZE(compat_riscv_user_regset),
 };
 
+static inline unsigned long compat_ptrace_get_gpr_reg(struct task_struct *child,
+						      compat_ulong_t offset)
+{
+	return creg_get_by_offset(task_pt_regs(child), offset);
+}
+
+static inline int compat_ptrace_set_gpr_reg(struct task_struct *child, compat_ulong_t offset,
+					    compat_ulong_t data)
+{
+	regs_set_by_offset(task_pt_regs(child), offset, data);
+	return 0;
+}
+
 long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 			compat_ulong_t caddr, compat_ulong_t cdata)
 {
 	long ret = -EIO;
+	unsigned long data = cdata;
+	compat_ulong_t __user *cdatap = (compat_ulong_t __user *)compat_ptr(data);
 
 	switch (request) {
+	case PTRACE_PEEKUSR:
+	case PTRACE_POKEUSR:
+		if (caddr < sizeof(struct compat_user_regs_struct)) {
+			/* access gprs addr must be aligned to compat_ulong_t */
+			if (caddr & (sizeof(caddr) - 1))
+				break;
+
+			if (request == PTRACE_PEEKUSR)
+				ret = put_user(compat_ptrace_get_gpr_reg(child, caddr), cdatap);
+			else	// PTRACE_POKEUSR
+				ret = compat_ptrace_set_gpr_reg(child, caddr, cdata);
+#ifdef CONFIG_FPU
+		} else if (caddr < (sizeof(struct compat_user_regs_struct) +
+				    sizeof(struct __riscv_d_ext_state))) {
+			caddr -= sizeof(struct compat_user_regs_struct);
+			/* access fprs addr must be aligned to __u64 */
+			if (caddr & (sizeof(__u64) - 1))
+				break;
+
+			if (request == PTRACE_PEEKUSR)
+				ret = put_user(ptrace_get_fpr_reg(child, (unsigned long)caddr),
+					       cdatap);
+			else	// PTRACE_POKEUSR
+				ret = ptrace_set_fpr_reg(child, (unsigned long)caddr,
+							 (unsigned long)cdata);
+#endif
+		} else {
+			// caddr invalid
+		}
+		break;
 	default:
 		ret = compat_ptrace_request(child, request, caddr, cdata);
 		break;
-- 
2.17.1




More information about the linux-riscv mailing list