bp-modify.c (4834B)
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 18static noinline int bp_1(void) 19{ 20 pr_debug("in %s\n", __func__); 21 return 0; 22} 23 24static noinline int bp_2(void) 25{ 26 pr_debug("in %s\n", __func__); 27 return 0; 28} 29 30static 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 */ 59static 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 122out: 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 */ 136static 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 198out: 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 207int test__bp_modify(struct test_suite *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}