123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- // SPDX-License-Identifier: GPL-2.0
- #include <linux/compiler.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <sys/user.h>
- #include <syscall.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <sys/ptrace.h>
- #include <asm/ptrace.h>
- #include <errno.h>
- #include "debug.h"
- #include "tests/tests.h"
- #include "arch-tests.h"
- static noinline int bp_1(void)
- {
- pr_debug("in %s\n", __func__);
- return 0;
- }
- static noinline int bp_2(void)
- {
- pr_debug("in %s\n", __func__);
- return 0;
- }
- static int spawn_child(void)
- {
- int child = fork();
- if (child == 0) {
- /*
- * The child sets itself for as tracee and
- * waits in signal for parent to trace it,
- * then it calls bp_1 and quits.
- */
- int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
- if (err) {
- pr_debug("failed to PTRACE_TRACEME\n");
- exit(1);
- }
- raise(SIGCONT);
- bp_1();
- exit(0);
- }
- return child;
- }
- /*
- * This tests creates HW breakpoint, tries to
- * change it and checks it was properly changed.
- */
- static int bp_modify1(void)
- {
- pid_t child;
- int status;
- unsigned long rip = 0, dr7 = 1;
- child = spawn_child();
- waitpid(child, &status, 0);
- if (WIFEXITED(status)) {
- pr_debug("tracee exited prematurely 1\n");
- return TEST_FAIL;
- }
- /*
- * The parent does following steps:
- * - creates a new breakpoint (id 0) for bp_2 function
- * - changes that breakponit to bp_1 function
- * - waits for the breakpoint to hit and checks
- * it has proper rip of bp_1 function
- * - detaches the child
- */
- if (ptrace(PTRACE_POKEUSER, child,
- offsetof(struct user, u_debugreg[0]), bp_2)) {
- pr_debug("failed to set breakpoint, 1st time: %s\n",
- strerror(errno));
- goto out;
- }
- if (ptrace(PTRACE_POKEUSER, child,
- offsetof(struct user, u_debugreg[0]), bp_1)) {
- pr_debug("failed to set breakpoint, 2nd time: %s\n",
- strerror(errno));
- goto out;
- }
- if (ptrace(PTRACE_POKEUSER, child,
- offsetof(struct user, u_debugreg[7]), dr7)) {
- pr_debug("failed to set dr7: %s\n", strerror(errno));
- goto out;
- }
- if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
- pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
- goto out;
- }
- waitpid(child, &status, 0);
- if (WIFEXITED(status)) {
- pr_debug("tracee exited prematurely 2\n");
- return TEST_FAIL;
- }
- rip = ptrace(PTRACE_PEEKUSER, child,
- offsetof(struct user_regs_struct, rip), NULL);
- if (rip == (unsigned long) -1) {
- pr_debug("failed to PTRACE_PEEKUSER: %s\n",
- strerror(errno));
- goto out;
- }
- pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
- out:
- if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
- pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
- return TEST_FAIL;
- }
- return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
- }
- /*
- * This tests creates HW breakpoint, tries to
- * change it to bogus value and checks the original
- * breakpoint is hit.
- */
- static int bp_modify2(void)
- {
- pid_t child;
- int status;
- unsigned long rip = 0, dr7 = 1;
- child = spawn_child();
- waitpid(child, &status, 0);
- if (WIFEXITED(status)) {
- pr_debug("tracee exited prematurely 1\n");
- return TEST_FAIL;
- }
- /*
- * The parent does following steps:
- * - creates a new breakpoint (id 0) for bp_1 function
- * - tries to change that breakpoint to (-1) address
- * - waits for the breakpoint to hit and checks
- * it has proper rip of bp_1 function
- * - detaches the child
- */
- if (ptrace(PTRACE_POKEUSER, child,
- offsetof(struct user, u_debugreg[0]), bp_1)) {
- pr_debug("failed to set breakpoint: %s\n",
- strerror(errno));
- goto out;
- }
- if (ptrace(PTRACE_POKEUSER, child,
- offsetof(struct user, u_debugreg[7]), dr7)) {
- pr_debug("failed to set dr7: %s\n", strerror(errno));
- goto out;
- }
- if (!ptrace(PTRACE_POKEUSER, child,
- offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
- pr_debug("failed, breakpoint set to bogus address\n");
- goto out;
- }
- if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
- pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
- goto out;
- }
- waitpid(child, &status, 0);
- if (WIFEXITED(status)) {
- pr_debug("tracee exited prematurely 2\n");
- return TEST_FAIL;
- }
- rip = ptrace(PTRACE_PEEKUSER, child,
- offsetof(struct user_regs_struct, rip), NULL);
- if (rip == (unsigned long) -1) {
- pr_debug("failed to PTRACE_PEEKUSER: %s\n",
- strerror(errno));
- goto out;
- }
- pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
- out:
- if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
- pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
- return TEST_FAIL;
- }
- return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
- }
- int test__bp_modify(struct test *test __maybe_unused,
- int subtest __maybe_unused)
- {
- TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
- TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
- return 0;
- }
|