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: %s\n", 84 strerror(errno)); 85 goto out; 86 } 87 88 if (ptrace(PTRACE_POKEUSER, child, 89 offsetof(struct user, u_debugreg[0]), bp_1)) { 90 pr_debug("failed to set breakpoint, 2nd time: %s\n", 91 strerror(errno)); 92 goto out; 93 } 94 95 if (ptrace(PTRACE_POKEUSER, child, 96 offsetof(struct user, u_debugreg[7]), dr7)) { 97 pr_debug("failed to set dr7: %s\n", strerror(errno)); 98 goto out; 99 } 100 101 if (ptrace(PTRACE_CONT, child, NULL, NULL)) { 102 pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); 103 goto out; 104 } 105 106 waitpid(child, &status, 0); 107 if (WIFEXITED(status)) { 108 pr_debug("tracee exited prematurely 2\n"); 109 return TEST_FAIL; 110 } 111 112 rip = ptrace(PTRACE_PEEKUSER, child, 113 offsetof(struct user_regs_struct, rip), NULL); 114 if (rip == (unsigned long) -1) { 115 pr_debug("failed to PTRACE_PEEKUSER: %s\n", 116 strerror(errno)); 117 goto out; 118 } 119 120 pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); 121 122 out: 123 if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { 124 pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); 125 return TEST_FAIL; 126 } 127 128 return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; 129 } 130 131 /* 132 * This tests creates HW breakpoint, tries to 133 * change it to bogus value and checks the original 134 * breakpoint is hit. 135 */ 136 static int bp_modify2(void) 137 { 138 pid_t child; 139 int status; 140 unsigned long rip = 0, dr7 = 1; 141 142 child = spawn_child(); 143 144 waitpid(child, &status, 0); 145 if (WIFEXITED(status)) { 146 pr_debug("tracee exited prematurely 1\n"); 147 return TEST_FAIL; 148 } 149 150 /* 151 * The parent does following steps: 152 * - creates a new breakpoint (id 0) for bp_1 function 153 * - tries to change that breakpoint to (-1) address 154 * - waits for the breakpoint to hit and checks 155 * it has proper rip of bp_1 function 156 * - detaches the child 157 */ 158 if (ptrace(PTRACE_POKEUSER, child, 159 offsetof(struct user, u_debugreg[0]), bp_1)) { 160 pr_debug("failed to set breakpoint: %s\n", 161 strerror(errno)); 162 goto out; 163 } 164 165 if (ptrace(PTRACE_POKEUSER, child, 166 offsetof(struct user, u_debugreg[7]), dr7)) { 167 pr_debug("failed to set dr7: %s\n", strerror(errno)); 168 goto out; 169 } 170 171 if (!ptrace(PTRACE_POKEUSER, child, 172 offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) { 173 pr_debug("failed, breakpoint set to bogus address\n"); 174 goto out; 175 } 176 177 if (ptrace(PTRACE_CONT, child, NULL, NULL)) { 178 pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno)); 179 goto out; 180 } 181 182 waitpid(child, &status, 0); 183 if (WIFEXITED(status)) { 184 pr_debug("tracee exited prematurely 2\n"); 185 return TEST_FAIL; 186 } 187 188 rip = ptrace(PTRACE_PEEKUSER, child, 189 offsetof(struct user_regs_struct, rip), NULL); 190 if (rip == (unsigned long) -1) { 191 pr_debug("failed to PTRACE_PEEKUSER: %s\n", 192 strerror(errno)); 193 goto out; 194 } 195 196 pr_debug("rip %lx, bp_1 %p\n", rip, bp_1); 197 198 out: 199 if (ptrace(PTRACE_DETACH, child, NULL, NULL)) { 200 pr_debug("failed to PTRACE_DETACH: %s", strerror(errno)); 201 return TEST_FAIL; 202 } 203 204 return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL; 205 } 206 207 int test__bp_modify(struct test *test __maybe_unused, 208 int subtest __maybe_unused) 209 { 210 TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1()); 211 TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2()); 212 213 return 0; 214 } 215