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