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