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