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 */ 24enum { 25 BP_X = 1, 26 BP_RW = 2, 27 BP_W = 4, 28}; 29 30static 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 */ 37static int nr_tests; 38 39static 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 51static 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 */ 115static unsigned long long dummy_var[4]; 116 117/* Dummy functions to test execution accesses */ 118static void dummy_func(void) { } 119static void dummy_func1(void) { } 120static void dummy_func2(void) { } 121static void dummy_func3(void) { } 122 123static void (*dummy_funcs[])(void) = { 124 dummy_func, 125 dummy_func1, 126 dummy_func2, 127 dummy_func3, 128}; 129 130static int trapped; 131 132static 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 145static 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 173static 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 */ 201static 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 262static 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 289static 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 304static 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 */ 327static 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 376int 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