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