Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | // 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 <string.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 breakpoint 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_suite *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; } |