root/tools/testing/selftests/breakpoints/breakpoint_test.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_breakpoint_addr
  2. toggle_breakpoint
  3. dummy_func
  4. dummy_func1
  5. dummy_func2
  6. dummy_func3
  7. check_trapped
  8. write_var
  9. read_var
  10. trigger_tests
  11. check_success
  12. launch_instruction_breakpoints
  13. launch_watchpoints
  14. launch_tests
  15. main

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

/* [<][>][^][v][top][bottom][index][help] */