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