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