From 6c9c7d8fbc3a2a0cfed0e7a5b39581847b632f0b Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Tue, 28 Jun 2022 00:02:39 +1000 Subject: 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 Link: https://lore.kernel.org/r/20220627140239.2464900-13-mpe@ellerman.id.au --- .../testing/selftests/powerpc/ptrace/ptrace-gpr.c | 24 +++++++- tools/testing/selftests/powerpc/ptrace/ptrace.h | 65 ++++++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) (limited to 'tools/testing') 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 #include #include +#include #include #include #include @@ -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; -- cgit v1.2.3-59-g8ed1b