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_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 | |