• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
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 
set_breakpoint_addr(void * addr,int n)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 
toggle_breakpoint(int n,int type,int len,int local,int global,int set)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 */
dummy_func(void)116 static void dummy_func(void) { }
dummy_func1(void)117 static void dummy_func1(void) { }
dummy_func2(void)118 static void dummy_func2(void) { }
dummy_func3(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 
check_trapped(void)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 
write_var(int len)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 
read_var(int len)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  */
trigger_tests(void)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 
check_success(const char * msg)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 
launch_instruction_breakpoints(char * buf,int local,int global)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 
launch_watchpoints(char * buf,int mode,int len,int local,int global)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 */
launch_tests(void)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 
main(int argc,char ** argv)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 }
395