]> Git Repo - linux.git/blob - tools/perf/arch/x86/tests/bp-modify.c
Linux 6.14-rc3
[linux.git] / tools / perf / arch / x86 / tests / bp-modify.c
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 }
This page took 0.045475 seconds and 4 git commands to generate.