Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/breakpoints/breakpoint_test.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <[email protected]>
4
*
5
* Selftests for breakpoints (and more generally the do_debug() path) in x86.
6
*/
7
8
9
#include <sys/ptrace.h>
10
#include <unistd.h>
11
#include <stddef.h>
12
#include <sys/user.h>
13
#include <stdio.h>
14
#include <stdlib.h>
15
#include <signal.h>
16
#include <sys/types.h>
17
#include <sys/wait.h>
18
#include <errno.h>
19
#include <string.h>
20
21
#include "../kselftest.h"
22
23
#define COUNT_ISN_BPS 4
24
#define COUNT_WPS 4
25
26
/* Breakpoint access modes */
27
enum {
28
BP_X = 1,
29
BP_RW = 2,
30
BP_W = 4,
31
};
32
33
static pid_t child_pid;
34
35
/*
36
* Ensures the child and parent are always "talking" about
37
* the same test sequence. (ie: that we haven't forgotten
38
* to call check_trapped() somewhere).
39
*/
40
static int nr_tests;
41
42
static void set_breakpoint_addr(void *addr, int n)
43
{
44
int ret;
45
46
ret = ptrace(PTRACE_POKEUSER, child_pid,
47
offsetof(struct user, u_debugreg[n]), addr);
48
if (ret)
49
ksft_exit_fail_msg("Can't set breakpoint addr: %s\n",
50
strerror(errno));
51
}
52
53
static void toggle_breakpoint(int n, int type, int len,
54
int local, int global, int set)
55
{
56
int ret;
57
58
int xtype, xlen;
59
unsigned long vdr7, dr7;
60
61
switch (type) {
62
case BP_X:
63
xtype = 0;
64
break;
65
case BP_W:
66
xtype = 1;
67
break;
68
case BP_RW:
69
xtype = 3;
70
break;
71
}
72
73
switch (len) {
74
case 1:
75
xlen = 0;
76
break;
77
case 2:
78
xlen = 4;
79
break;
80
case 4:
81
xlen = 0xc;
82
break;
83
case 8:
84
xlen = 8;
85
break;
86
}
87
88
dr7 = ptrace(PTRACE_PEEKUSER, child_pid,
89
offsetof(struct user, u_debugreg[7]), 0);
90
91
vdr7 = (xlen | xtype) << 16;
92
vdr7 <<= 4 * n;
93
94
if (local) {
95
vdr7 |= 1 << (2 * n);
96
vdr7 |= 1 << 8;
97
}
98
if (global) {
99
vdr7 |= 2 << (2 * n);
100
vdr7 |= 1 << 9;
101
}
102
103
if (set)
104
dr7 |= vdr7;
105
else
106
dr7 &= ~vdr7;
107
108
ret = ptrace(PTRACE_POKEUSER, child_pid,
109
offsetof(struct user, u_debugreg[7]), dr7);
110
if (ret) {
111
ksft_print_msg("Can't set dr7: %s\n", strerror(errno));
112
exit(-1);
113
}
114
}
115
116
/* Dummy variables to test read/write accesses */
117
static unsigned long long dummy_var[4];
118
119
/* Dummy functions to test execution accesses */
120
static void dummy_func(void) { }
121
static void dummy_func1(void) { }
122
static void dummy_func2(void) { }
123
static void dummy_func3(void) { }
124
125
static void (*dummy_funcs[])(void) = {
126
dummy_func,
127
dummy_func1,
128
dummy_func2,
129
dummy_func3,
130
};
131
132
static int trapped;
133
134
static void check_trapped(void)
135
{
136
/*
137
* If we haven't trapped, wake up the parent
138
* so that it notices the failure.
139
*/
140
if (!trapped)
141
kill(getpid(), SIGUSR1);
142
trapped = 0;
143
144
nr_tests++;
145
}
146
147
static void write_var(int len)
148
{
149
char *pcval; short *psval; int *pival; long long *plval;
150
int i;
151
152
for (i = 0; i < 4; i++) {
153
switch (len) {
154
case 1:
155
pcval = (char *)&dummy_var[i];
156
*pcval = 0xff;
157
break;
158
case 2:
159
psval = (short *)&dummy_var[i];
160
*psval = 0xffff;
161
break;
162
case 4:
163
pival = (int *)&dummy_var[i];
164
*pival = 0xffffffff;
165
break;
166
case 8:
167
plval = (long long *)&dummy_var[i];
168
*plval = 0xffffffffffffffffLL;
169
break;
170
}
171
check_trapped();
172
}
173
}
174
175
static void read_var(int len)
176
{
177
char cval; short sval; int ival; long long lval;
178
int i;
179
180
for (i = 0; i < 4; i++) {
181
switch (len) {
182
case 1:
183
cval = *(char *)&dummy_var[i];
184
break;
185
case 2:
186
sval = *(short *)&dummy_var[i];
187
break;
188
case 4:
189
ival = *(int *)&dummy_var[i];
190
break;
191
case 8:
192
lval = *(long long *)&dummy_var[i];
193
break;
194
}
195
check_trapped();
196
}
197
}
198
199
/*
200
* Do the r/w/x accesses to trigger the breakpoints. And run
201
* the usual traps.
202
*/
203
static void trigger_tests(void)
204
{
205
int len, local, global, i;
206
char val;
207
int ret;
208
209
ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
210
if (ret) {
211
ksft_print_msg("Can't be traced? %s\n", strerror(errno));
212
return;
213
}
214
215
/* Wake up father so that it sets up the first test */
216
kill(getpid(), SIGUSR1);
217
218
/* Test instruction breakpoints */
219
for (local = 0; local < 2; local++) {
220
for (global = 0; global < 2; global++) {
221
if (!local && !global)
222
continue;
223
224
for (i = 0; i < COUNT_ISN_BPS; i++) {
225
dummy_funcs[i]();
226
check_trapped();
227
}
228
}
229
}
230
231
/* Test write watchpoints */
232
for (len = 1; len <= sizeof(long); len <<= 1) {
233
for (local = 0; local < 2; local++) {
234
for (global = 0; global < 2; global++) {
235
if (!local && !global)
236
continue;
237
write_var(len);
238
}
239
}
240
}
241
242
/* Test read/write watchpoints (on read accesses) */
243
for (len = 1; len <= sizeof(long); len <<= 1) {
244
for (local = 0; local < 2; local++) {
245
for (global = 0; global < 2; global++) {
246
if (!local && !global)
247
continue;
248
read_var(len);
249
}
250
}
251
}
252
253
/* Icebp trap */
254
asm(".byte 0xf1\n");
255
check_trapped();
256
257
/* Int 3 trap */
258
asm("int $3\n");
259
check_trapped();
260
261
kill(getpid(), SIGUSR1);
262
}
263
264
static void check_success(const char *msg)
265
{
266
int child_nr_tests;
267
int status;
268
int ret;
269
270
/* Wait for the child to SIGTRAP */
271
wait(&status);
272
273
ret = 0;
274
275
if (WSTOPSIG(status) == SIGTRAP) {
276
child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid,
277
&nr_tests, 0);
278
if (child_nr_tests == nr_tests)
279
ret = 1;
280
if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1))
281
ksft_exit_fail_msg("Can't poke: %s\n", strerror(errno));
282
}
283
284
nr_tests++;
285
286
if (ret)
287
ksft_test_result_pass("%s", msg);
288
else
289
ksft_test_result_fail("%s", msg);
290
}
291
292
static void launch_instruction_breakpoints(char *buf, int local, int global)
293
{
294
int i;
295
296
for (i = 0; i < COUNT_ISN_BPS; i++) {
297
set_breakpoint_addr(dummy_funcs[i], i);
298
toggle_breakpoint(i, BP_X, 1, local, global, 1);
299
ptrace(PTRACE_CONT, child_pid, NULL, 0);
300
sprintf(buf, "Test breakpoint %d with local: %d global: %d\n",
301
i, local, global);
302
check_success(buf);
303
toggle_breakpoint(i, BP_X, 1, local, global, 0);
304
}
305
}
306
307
static void launch_watchpoints(char *buf, int mode, int len,
308
int local, int global)
309
{
310
const char *mode_str;
311
int i;
312
313
if (mode == BP_W)
314
mode_str = "write";
315
else
316
mode_str = "read";
317
318
for (i = 0; i < COUNT_WPS; i++) {
319
set_breakpoint_addr(&dummy_var[i], i);
320
toggle_breakpoint(i, mode, len, local, global, 1);
321
ptrace(PTRACE_CONT, child_pid, NULL, 0);
322
sprintf(buf,
323
"Test %s watchpoint %d with len: %d local: %d global: %d\n",
324
mode_str, i, len, local, global);
325
check_success(buf);
326
toggle_breakpoint(i, mode, len, local, global, 0);
327
}
328
}
329
330
/* Set the breakpoints and check the child successfully trigger them */
331
static void launch_tests(void)
332
{
333
char buf[1024];
334
unsigned int tests = 0;
335
int len, local, global, i;
336
337
tests += 3 * COUNT_ISN_BPS;
338
tests += sizeof(long) / 2 * 3 * COUNT_WPS;
339
tests += sizeof(long) / 2 * 3 * COUNT_WPS;
340
tests += 2;
341
ksft_set_plan(tests);
342
343
/* Instruction breakpoints */
344
for (local = 0; local < 2; local++) {
345
for (global = 0; global < 2; global++) {
346
if (!local && !global)
347
continue;
348
launch_instruction_breakpoints(buf, local, global);
349
}
350
}
351
352
/* Write watchpoint */
353
for (len = 1; len <= sizeof(long); len <<= 1) {
354
for (local = 0; local < 2; local++) {
355
for (global = 0; global < 2; global++) {
356
if (!local && !global)
357
continue;
358
launch_watchpoints(buf, BP_W, len,
359
local, global);
360
}
361
}
362
}
363
364
/* Read-Write watchpoint */
365
for (len = 1; len <= sizeof(long); len <<= 1) {
366
for (local = 0; local < 2; local++) {
367
for (global = 0; global < 2; global++) {
368
if (!local && !global)
369
continue;
370
launch_watchpoints(buf, BP_RW, len,
371
local, global);
372
}
373
}
374
}
375
376
/* Icebp traps */
377
ptrace(PTRACE_CONT, child_pid, NULL, 0);
378
check_success("Test icebp\n");
379
380
/* Int 3 traps */
381
ptrace(PTRACE_CONT, child_pid, NULL, 0);
382
check_success("Test int 3 trap\n");
383
384
ptrace(PTRACE_CONT, child_pid, NULL, 0);
385
}
386
387
int main(int argc, char **argv)
388
{
389
pid_t pid;
390
int ret;
391
392
ksft_print_header();
393
394
pid = fork();
395
if (!pid) {
396
trigger_tests();
397
exit(0);
398
}
399
400
child_pid = pid;
401
402
wait(NULL);
403
404
launch_tests();
405
406
wait(NULL);
407
408
ksft_exit_pass();
409
}
410
411