selftests/powerpc/ptrace: Add peek/poke of FPRs
authorMichael Ellerman <mpe@ellerman.id.au>
Mon, 27 Jun 2022 14:02:39 +0000 (00:02 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Mon, 25 Jul 2022 02:05:16 +0000 (12:05 +1000)
Currently the ptrace-gpr test only tests the GET/SET(FP)REGS ptrace
APIs. But there's an alternate (older) API, called PEEK/POKEUSR.

Add some minimal testing of PEEK/POKEUSR of the FPRs. This is sufficient
to detect the bug that was fixed recently in the 32-bit ptrace FPR
handling.

Depends-on: 8e1278444446 ("powerpc/32: Fix overread/overwrite of thread_struct via ptrace")
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20220627140239.2464900-13-mpe@ellerman.id.au
tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
tools/testing/selftests/powerpc/ptrace/ptrace.h

index c5dcb8c..9ed87d2 100644 (file)
@@ -46,22 +46,42 @@ static int child(void)
 
 int trace_gpr(pid_t child)
 {
+       __u64 tmp, fpr[32], *peeked_fprs;
        unsigned long gpr[18];
-       __u64 tmp, fpr[32];
 
        FAIL_IF(start_trace(child));
+
+       // Check child GPRs match what we expect using GETREGS
        FAIL_IF(show_gpr(child, gpr));
        FAIL_IF(validate_gpr(gpr, child_gpr_val));
-       FAIL_IF(show_fpr(child, fpr));
 
+       // Check child FPRs match what we expect using GETFPREGS
+       FAIL_IF(show_fpr(child, fpr));
        memcpy(&tmp, &child_fpr_val, sizeof(tmp));
        FAIL_IF(validate_fpr(fpr, tmp));
 
+       // Check child FPRs match what we expect using PEEKUSR
+       peeked_fprs = peek_fprs(child);
+       FAIL_IF(!peeked_fprs);
+       FAIL_IF(validate_fpr(peeked_fprs, tmp));
+       free(peeked_fprs);
+
+       // Write child GPRs using SETREGS
        FAIL_IF(write_gpr(child, parent_gpr_val));
 
+       // Write child FPRs using SETFPREGS
        memcpy(&tmp, &parent_fpr_val, sizeof(tmp));
        FAIL_IF(write_fpr(child, tmp));
 
+       // Check child FPRs match what we just set, using PEEKUSR
+       peeked_fprs = peek_fprs(child);
+       FAIL_IF(!peeked_fprs);
+       FAIL_IF(validate_fpr(peeked_fprs, tmp));
+
+       // Write child FPRs using POKEUSR
+       FAIL_IF(poke_fprs(child, (unsigned long *)peeked_fprs));
+
+       // Child will check its FPRs match before exiting
        FAIL_IF(stop_trace(child));
 
        return TEST_PASS;
index 4672e84..4e0233c 100644 (file)
@@ -23,6 +23,7 @@
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #include <sys/user.h>
+#include <sys/syscall.h>
 #include <linux/elf.h>
 #include <linux/types.h>
 #include <linux/auxvec.h>
@@ -440,6 +441,70 @@ int show_gpr(pid_t child, unsigned long *gpr)
        return TEST_PASS;
 }
 
+long sys_ptrace(enum __ptrace_request request, pid_t pid, unsigned long addr, unsigned long data)
+{
+       return syscall(__NR_ptrace, request, pid, (void *)addr, data);
+}
+
+// 33 because of FPSCR
+#define PT_NUM_FPRS    (33 * (sizeof(__u64) / sizeof(unsigned long)))
+
+__u64 *peek_fprs(pid_t child)
+{
+       unsigned long *fprs, *p, addr;
+       long ret;
+       int i;
+
+       fprs = malloc(sizeof(unsigned long) * PT_NUM_FPRS);
+       if (!fprs) {
+               perror("malloc() failed");
+               return NULL;
+       }
+
+       for (i = 0, p = fprs; i < PT_NUM_FPRS; i++, p++) {
+               addr = sizeof(unsigned long) * (PT_FPR0 + i);
+               ret = sys_ptrace(PTRACE_PEEKUSER, child, addr, (unsigned long)p);
+               if (ret) {
+                       perror("ptrace(PTRACE_PEEKUSR) failed");
+                       return NULL;
+               }
+       }
+
+       addr = sizeof(unsigned long) * (PT_FPR0 + i);
+       ret = sys_ptrace(PTRACE_PEEKUSER, child, addr, (unsigned long)&addr);
+       if (!ret) {
+               printf("ptrace(PTRACE_PEEKUSR) succeeded unexpectedly!\n");
+               return NULL;
+       }
+
+       return (__u64 *)fprs;
+}
+
+int poke_fprs(pid_t child, unsigned long *fprs)
+{
+       unsigned long *p, addr;
+       long ret;
+       int i;
+
+       for (i = 0, p = fprs; i < PT_NUM_FPRS; i++, p++) {
+               addr = sizeof(unsigned long) * (PT_FPR0 + i);
+               ret = sys_ptrace(PTRACE_POKEUSER, child, addr, *p);
+               if (ret) {
+                       perror("ptrace(PTRACE_POKEUSR) failed");
+                       return -1;
+               }
+       }
+
+       addr = sizeof(unsigned long) * (PT_FPR0 + i);
+       ret = sys_ptrace(PTRACE_POKEUSER, child, addr, addr);
+       if (!ret) {
+               printf("ptrace(PTRACE_POKEUSR) succeeded unexpectedly!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
 int write_gpr(pid_t child, unsigned long val)
 {
        struct pt_regs *regs;