1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/compiler.h> 3 #include <sys/types.h> 4 #include <sys/wait.h> 5 #include <sys/user.h> 6 #include <syscall.h> 7 #include <unistd.h> 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <sys/ptrace.h> 12 #include <asm/ptrace.h> 13 #include <errno.h> 14 #include "debug.h" 15 #include "tests/tests.h" 16 #include "arch-tests.h" 17 18 static noinline int bp_1(void) 19 { 20 pr_debug("in %s\n", __func__); 21 return 0; 22 } 23 24 static noinline int bp_2(void) 25 { 26 pr_debug("in %s\n", __func__); 27 return 0; 28 } 29 30 static int spawn_child(void) 31 { 32 int child = fork(); 33 34 if (child == 0) { 35 /* 36 * The child sets itself for as tracee and 37 * waits in signal for parent to trace it, 38 * then it calls bp_1 and quits. 39 */ 40 int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL); 41 42 if (err) { 43 pr_debug("failed to PTRACE_TRACEME\n"); 44 exit(1); 45 } 46 47 raise(SIGCONT); 48 bp_1(); 49 exit(0); 50 } 51 52 return child; 53 } 54 55 /* 56 * This tests creates HW breakpoint, tries to 57 * change it and checks it was properly changed. 58 */ 59 static int bp_modify1(void) 60 { 61 pid_t child; 62 int status; 63 unsigned long rip = 0, dr7 = 1; 64 65 child = spawn_child(); 66 67 waitpid(child, &status, 0); 68 if (WIFEXITED(status)) { 69 pr_debug("tracee exited prematurely 1\n"); 70 return TEST_FAIL; 71 } 72 73 /* 74 * The parent does following steps: 75 * - creates a new breakpoint (id 0) for bp_2 function 76 * - changes that breakpoint to bp_1 function 77 * - waits for the breakpoint to hit and checks 78 * it has proper rip of bp_1 function 79 * - detaches the child 80 */ 81 if (ptrace(PTRACE_POKEUSER, child, 82 offsetof(struct user, u_debugreg[0]), bp_2)) { 83 pr_debug("failed to set breakpoint, 1st time: %m\n"); 84 goto out; 85 } 86 87 if (ptrace(PTRACE_POKEUSER, child, 88 offsetof(struct user, u_debugreg[0]), bp_1)) { 89 pr_debug("failed to set breakpoint, 2nd time: %m\n"); 90 goto out; 91 } 92 93 if (ptrace(PTRACE_POKEUSER, child, 94 offsetof(struct user, u_debugreg[7]), dr7)) { 95 pr_debug("failed to set dr7: %m\n"); 96 goto out; 97 } 98 99 if (ptrace(PTRACE_CONT, child, NULL, NULL)) { 100 pr_debug("failed to PTRACE_CONT: %m\n"); 101 goto out; 102 } 103 104 waitpid(child, &status, 0); 105 if (WIFEXITED(status)) { 106 pr_debug("tracee exited prematurely 2\n"); 107 return TEST_FAIL; 108 } 109 110 rip = ptrace(PTRACE_PEEKUSER, child, 111 offsetof(struct user_regs_struct, rip), NULL); 112 if (rip == (unsigned long) -1) { 113 pr_debug("failed to PTRACE_PEEKUSER: %m\n"); 114 goto out; 115 } 116 117 pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); 118 out: 119 if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { 120 pr_debug("failed to PTRACE_DETACH: %m\n"); 121 return TEST_FAIL; 122 123 } 124 return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; 125 } 126 127 /* 128 * This tests creates HW breakpoint, tries to 129 * change it to bogus value and checks the original 130 * breakpoint is hit. 131 */ 132 static int bp_modify2(void) 133 { 134 pid_t child; 135 int status; 136 unsigned long rip = 0, dr7 = 1; 137 138 child = spawn_child(); 139 140 waitpid(child, &status, 0); 141 if (WIFEXITED(status)) { 142 pr_debug("tracee exited prematurely 1\n"); 143 return TEST_FAIL; 144 } 145 146 /* 147 * The parent does following steps: 148 * - creates a new breakpoint (id 0) for bp_1 function 149 * - tries to change that breakpoint to (-1) address 150 * - waits for the breakpoint to hit and checks 151 * it has proper rip of bp_1 function 152 * - detaches the child 153 */ 154 if (ptrace(PTRACE_POKEUSER, child, 155 offsetof(struct user, u_debugreg[0]), bp_1)) { 156 pr_debug("failed to set breakpoint: %m\n"); 157 goto out; 158 } 159 160 if (ptrace(PTRACE_POKEUSER, child, 161 offsetof(struct user, u_debugreg[7]), dr7)) { 162 pr_debug("failed to set dr7: %m\n"); 163 goto out; 164 } 165 166 if (!ptrace(PTRACE_POKEUSER, child, 167 offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) { 168 pr_debug("failed, breakpoint set to bogus address\n"); 169 goto out; 170 } 171 172 if (ptrace(PTRACE_CONT, child, NULL, NULL)) { 173 pr_debug("failed to PTRACE_CONT: %m\n"); 174 goto out; 175 } 176 177 waitpid(child, &status, 0); 178 if (WIFEXITED(status)) { 179 pr_debug("tracee exited prematurely 2\n"); 180 return TEST_FAIL; 181 } 182 183 rip = ptrace(PTRACE_PEEKUSER, child, 184 offsetof(struct user_regs_struct, rip), NULL); 185 if (rip == (unsigned long) -1) { 186 pr_debug("failed to PTRACE_PEEKUSER: %m\n"); 187 goto out; 188 } 189 190 pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); 191 192 out: 193 if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { 194 pr_debug("failed to PTRACE_DETACH: %m\n"); 195 return TEST_FAIL; 196 } 197 198 return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; 199 } 200 201 int test__bp_modify(struct test_suite *test __maybe_unused, 202 int subtest __maybe_unused) 203 { 204 TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1()); 205 TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2()); 206 207 return 0; 208 } 209