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