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 
     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