19b3579fcSJiri Olsa // SPDX-License-Identifier: GPL-2.0
29b3579fcSJiri Olsa #include <linux/compiler.h>
39b3579fcSJiri Olsa #include <sys/types.h>
49b3579fcSJiri Olsa #include <sys/wait.h>
59b3579fcSJiri Olsa #include <sys/user.h>
69b3579fcSJiri Olsa #include <syscall.h>
79b3579fcSJiri Olsa #include <unistd.h>
89b3579fcSJiri Olsa #include <stdio.h>
99b3579fcSJiri Olsa #include <stdlib.h>
108520a98dSArnaldo Carvalho de Melo #include <string.h>
119b3579fcSJiri Olsa #include <sys/ptrace.h>
129b3579fcSJiri Olsa #include <asm/ptrace.h>
139b3579fcSJiri Olsa #include <errno.h>
149b3579fcSJiri Olsa #include "debug.h"
159b3579fcSJiri Olsa #include "tests/tests.h"
169b3579fcSJiri Olsa #include "arch-tests.h"
179b3579fcSJiri Olsa
bp_1(void)189b3579fcSJiri Olsa static noinline int bp_1(void)
199b3579fcSJiri Olsa {
209b3579fcSJiri Olsa pr_debug("in %s\n", __func__);
219b3579fcSJiri Olsa return 0;
229b3579fcSJiri Olsa }
239b3579fcSJiri Olsa
bp_2(void)249b3579fcSJiri Olsa static noinline int bp_2(void)
259b3579fcSJiri Olsa {
269b3579fcSJiri Olsa pr_debug("in %s\n", __func__);
279b3579fcSJiri Olsa return 0;
289b3579fcSJiri Olsa }
299b3579fcSJiri Olsa
spawn_child(void)309b3579fcSJiri Olsa static int spawn_child(void)
319b3579fcSJiri Olsa {
329b3579fcSJiri Olsa int child = fork();
339b3579fcSJiri Olsa
349b3579fcSJiri Olsa if (child == 0) {
359b3579fcSJiri Olsa /*
369b3579fcSJiri Olsa * The child sets itself for as tracee and
379b3579fcSJiri Olsa * waits in signal for parent to trace it,
389b3579fcSJiri Olsa * then it calls bp_1 and quits.
399b3579fcSJiri Olsa */
409b3579fcSJiri Olsa int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
419b3579fcSJiri Olsa
429b3579fcSJiri Olsa if (err) {
439b3579fcSJiri Olsa pr_debug("failed to PTRACE_TRACEME\n");
449b3579fcSJiri Olsa exit(1);
459b3579fcSJiri Olsa }
469b3579fcSJiri Olsa
479b3579fcSJiri Olsa raise(SIGCONT);
489b3579fcSJiri Olsa bp_1();
499b3579fcSJiri Olsa exit(0);
509b3579fcSJiri Olsa }
519b3579fcSJiri Olsa
529b3579fcSJiri Olsa return child;
539b3579fcSJiri Olsa }
549b3579fcSJiri Olsa
559b3579fcSJiri Olsa /*
569b3579fcSJiri Olsa * This tests creates HW breakpoint, tries to
579b3579fcSJiri Olsa * change it and checks it was properly changed.
589b3579fcSJiri Olsa */
bp_modify1(void)599b3579fcSJiri Olsa static int bp_modify1(void)
609b3579fcSJiri Olsa {
619b3579fcSJiri Olsa pid_t child;
629b3579fcSJiri Olsa int status;
639b3579fcSJiri Olsa unsigned long rip = 0, dr7 = 1;
649b3579fcSJiri Olsa
659b3579fcSJiri Olsa child = spawn_child();
669b3579fcSJiri Olsa
679b3579fcSJiri Olsa waitpid(child, &status, 0);
689b3579fcSJiri Olsa if (WIFEXITED(status)) {
699b3579fcSJiri Olsa pr_debug("tracee exited prematurely 1\n");
709b3579fcSJiri Olsa return TEST_FAIL;
719b3579fcSJiri Olsa }
729b3579fcSJiri Olsa
739b3579fcSJiri Olsa /*
749b3579fcSJiri Olsa * The parent does following steps:
759b3579fcSJiri Olsa * - creates a new breakpoint (id 0) for bp_2 function
764d39c89fSIngo Molnar * - changes that breakpoint to bp_1 function
779b3579fcSJiri Olsa * - waits for the breakpoint to hit and checks
789b3579fcSJiri Olsa * it has proper rip of bp_1 function
799b3579fcSJiri Olsa * - detaches the child
809b3579fcSJiri Olsa */
819b3579fcSJiri Olsa if (ptrace(PTRACE_POKEUSER, child,
829b3579fcSJiri Olsa offsetof(struct user, u_debugreg[0]), bp_2)) {
839b3579fcSJiri Olsa pr_debug("failed to set breakpoint, 1st time: %s\n",
849b3579fcSJiri Olsa strerror(errno));
859b3579fcSJiri Olsa goto out;
869b3579fcSJiri Olsa }
879b3579fcSJiri Olsa
889b3579fcSJiri Olsa if (ptrace(PTRACE_POKEUSER, child,
899b3579fcSJiri Olsa offsetof(struct user, u_debugreg[0]), bp_1)) {
909b3579fcSJiri Olsa pr_debug("failed to set breakpoint, 2nd time: %s\n",
919b3579fcSJiri Olsa strerror(errno));
929b3579fcSJiri Olsa goto out;
939b3579fcSJiri Olsa }
949b3579fcSJiri Olsa
959b3579fcSJiri Olsa if (ptrace(PTRACE_POKEUSER, child,
969b3579fcSJiri Olsa offsetof(struct user, u_debugreg[7]), dr7)) {
979b3579fcSJiri Olsa pr_debug("failed to set dr7: %s\n", strerror(errno));
989b3579fcSJiri Olsa goto out;
999b3579fcSJiri Olsa }
1009b3579fcSJiri Olsa
1019b3579fcSJiri Olsa if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
1029b3579fcSJiri Olsa pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
1039b3579fcSJiri Olsa goto out;
1049b3579fcSJiri Olsa }
1059b3579fcSJiri Olsa
1069b3579fcSJiri Olsa waitpid(child, &status, 0);
1079b3579fcSJiri Olsa if (WIFEXITED(status)) {
1089b3579fcSJiri Olsa pr_debug("tracee exited prematurely 2\n");
1099b3579fcSJiri Olsa return TEST_FAIL;
1109b3579fcSJiri Olsa }
1119b3579fcSJiri Olsa
1129b3579fcSJiri Olsa rip = ptrace(PTRACE_PEEKUSER, child,
1139b3579fcSJiri Olsa offsetof(struct user_regs_struct, rip), NULL);
1149b3579fcSJiri Olsa if (rip == (unsigned long) -1) {
1159b3579fcSJiri Olsa pr_debug("failed to PTRACE_PEEKUSER: %s\n",
1169b3579fcSJiri Olsa strerror(errno));
1179b3579fcSJiri Olsa goto out;
1189b3579fcSJiri Olsa }
1199b3579fcSJiri Olsa
1209b3579fcSJiri Olsa pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
1219b3579fcSJiri Olsa
1229b3579fcSJiri Olsa out:
1239b3579fcSJiri Olsa if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
1249b3579fcSJiri Olsa pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
1259b3579fcSJiri Olsa return TEST_FAIL;
1269b3579fcSJiri Olsa }
1279b3579fcSJiri Olsa
1289b3579fcSJiri Olsa return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
1299b3579fcSJiri Olsa }
1309b3579fcSJiri Olsa
1319b3579fcSJiri Olsa /*
1329b3579fcSJiri Olsa * This tests creates HW breakpoint, tries to
1339b3579fcSJiri Olsa * change it to bogus value and checks the original
1349b3579fcSJiri Olsa * breakpoint is hit.
1359b3579fcSJiri Olsa */
bp_modify2(void)1369b3579fcSJiri Olsa static int bp_modify2(void)
1379b3579fcSJiri Olsa {
1389b3579fcSJiri Olsa pid_t child;
1399b3579fcSJiri Olsa int status;
1409b3579fcSJiri Olsa unsigned long rip = 0, dr7 = 1;
1419b3579fcSJiri Olsa
1429b3579fcSJiri Olsa child = spawn_child();
1439b3579fcSJiri Olsa
1449b3579fcSJiri Olsa waitpid(child, &status, 0);
1459b3579fcSJiri Olsa if (WIFEXITED(status)) {
1469b3579fcSJiri Olsa pr_debug("tracee exited prematurely 1\n");
1479b3579fcSJiri Olsa return TEST_FAIL;
1489b3579fcSJiri Olsa }
1499b3579fcSJiri Olsa
1509b3579fcSJiri Olsa /*
1519b3579fcSJiri Olsa * The parent does following steps:
1529b3579fcSJiri Olsa * - creates a new breakpoint (id 0) for bp_1 function
1539b3579fcSJiri Olsa * - tries to change that breakpoint to (-1) address
1549b3579fcSJiri Olsa * - waits for the breakpoint to hit and checks
1559b3579fcSJiri Olsa * it has proper rip of bp_1 function
1569b3579fcSJiri Olsa * - detaches the child
1579b3579fcSJiri Olsa */
1589b3579fcSJiri Olsa if (ptrace(PTRACE_POKEUSER, child,
1599b3579fcSJiri Olsa offsetof(struct user, u_debugreg[0]), bp_1)) {
1609b3579fcSJiri Olsa pr_debug("failed to set breakpoint: %s\n",
1619b3579fcSJiri Olsa strerror(errno));
1629b3579fcSJiri Olsa goto out;
1639b3579fcSJiri Olsa }
1649b3579fcSJiri Olsa
1659b3579fcSJiri Olsa if (ptrace(PTRACE_POKEUSER, child,
1669b3579fcSJiri Olsa offsetof(struct user, u_debugreg[7]), dr7)) {
1679b3579fcSJiri Olsa pr_debug("failed to set dr7: %s\n", strerror(errno));
1689b3579fcSJiri Olsa goto out;
1699b3579fcSJiri Olsa }
1709b3579fcSJiri Olsa
1719b3579fcSJiri Olsa if (!ptrace(PTRACE_POKEUSER, child,
1729b3579fcSJiri Olsa offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
1739b3579fcSJiri Olsa pr_debug("failed, breakpoint set to bogus address\n");
1749b3579fcSJiri Olsa goto out;
1759b3579fcSJiri Olsa }
1769b3579fcSJiri Olsa
1779b3579fcSJiri Olsa if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
1789b3579fcSJiri Olsa pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
1799b3579fcSJiri Olsa goto out;
1809b3579fcSJiri Olsa }
1819b3579fcSJiri Olsa
1829b3579fcSJiri Olsa waitpid(child, &status, 0);
1839b3579fcSJiri Olsa if (WIFEXITED(status)) {
1849b3579fcSJiri Olsa pr_debug("tracee exited prematurely 2\n");
1859b3579fcSJiri Olsa return TEST_FAIL;
1869b3579fcSJiri Olsa }
1879b3579fcSJiri Olsa
1889b3579fcSJiri Olsa rip = ptrace(PTRACE_PEEKUSER, child,
1899b3579fcSJiri Olsa offsetof(struct user_regs_struct, rip), NULL);
1909b3579fcSJiri Olsa if (rip == (unsigned long) -1) {
1919b3579fcSJiri Olsa pr_debug("failed to PTRACE_PEEKUSER: %s\n",
1929b3579fcSJiri Olsa strerror(errno));
1939b3579fcSJiri Olsa goto out;
1949b3579fcSJiri Olsa }
1959b3579fcSJiri Olsa
1969b3579fcSJiri Olsa pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
1979b3579fcSJiri Olsa
1989b3579fcSJiri Olsa out:
1999b3579fcSJiri Olsa if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
2009b3579fcSJiri Olsa pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
2019b3579fcSJiri Olsa return TEST_FAIL;
2029b3579fcSJiri Olsa }
2039b3579fcSJiri Olsa
2049b3579fcSJiri Olsa return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
2059b3579fcSJiri Olsa }
2069b3579fcSJiri Olsa
test__bp_modify(struct test_suite * test __maybe_unused,int subtest __maybe_unused)207*33f44bfdSIan Rogers int test__bp_modify(struct test_suite *test __maybe_unused,
2089b3579fcSJiri Olsa int subtest __maybe_unused)
2099b3579fcSJiri Olsa {
2109b3579fcSJiri Olsa TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
2119b3579fcSJiri Olsa TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
2129b3579fcSJiri Olsa
2139b3579fcSJiri Olsa return 0;
2149b3579fcSJiri Olsa }
215