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