Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/arch/x86/tests/bp-modify.c
26292 views
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
216