215 lines · c
1// SPDX-License-Identifier: GPL-2.02#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 and37 * 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 to57 * 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 function76 * - changes that breakpoint to bp_1 function77 * - waits for the breakpoint to hit and checks78 * it has proper rip of bp_1 function79 * - detaches the child80 */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 to133 * change it to bogus value and checks the original134 * 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 function153 * - tries to change that breakpoint to (-1) address154 * - waits for the breakpoint to hit and checks155 * it has proper rip of bp_1 function156 * - detaches the child157 */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}215