summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/powerpc
diff options
context:
space:
mode:
authorMichael Ellerman <mpe@ellerman.id.au>2022-06-27 17:02:39 +0300
committerMichael Ellerman <mpe@ellerman.id.au>2022-07-25 05:05:16 +0300
commit6c9c7d8fbc3a2a0cfed0e7a5b39581847b632f0b (patch)
treee311845cb16be9907549aa0e42704e47331792f2 /tools/testing/selftests/powerpc
parentc5a814cc992002c36fa5b7db5fbd55efb7430386 (diff)
downloadlinux-6c9c7d8fbc3a2a0cfed0e7a5b39581847b632f0b.tar.xz
selftests/powerpc/ptrace: Add peek/poke of FPRs
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
Diffstat (limited to 'tools/testing/selftests/powerpc')
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c24
-rw-r--r--tools/testing/selftests/powerpc/ptrace/ptrace.h65
2 files changed, 87 insertions, 2 deletions
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
index c5dcb8c02616..9ed87d297799 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace-gpr.c
@@ -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;
diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace.h b/tools/testing/selftests/powerpc/ptrace/ptrace.h
index 4672e848604f..4e0233c0f2b3 100644
--- a/tools/testing/selftests/powerpc/ptrace/ptrace.h
+++ b/tools/testing/selftests/powerpc/ptrace/ptrace.h
@@ -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;