linux/tools/perf/arch/x86/tests/bp-modify.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/compiler.h>
   3#include <sys/types.h>
   4#include <sys/wait.h>
   5#include <sys/user.h>
   6#include <syscall.h>
   7#include <unistd.h>
   8#include <stdio.h>
   9#include <stdlib.h>
  10#include <string.h>
  11#include <sys/ptrace.h>
  12#include <asm/ptrace.h>
  13#include <errno.h>
  14#include "debug.h"
  15#include "tests/tests.h"
  16#include "arch-tests.h"
  17
  18static noinline int bp_1(void)
  19{
  20        pr_debug("in %s\n", __func__);
  21        return 0;
  22}
  23
  24static noinline int bp_2(void)
  25{
  26        pr_debug("in %s\n", __func__);
  27        return 0;
  28}
  29
  30static int spawn_child(void)
  31{
  32        int child = fork();
  33
  34        if (child == 0) {
  35                /*
  36                 * The child sets itself for as tracee and
  37                 * waits in signal for parent to trace it,
  38                 * then it calls bp_1 and quits.
  39                 */
  40                int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
  41
  42                if (err) {
  43                        pr_debug("failed to PTRACE_TRACEME\n");
  44                        exit(1);
  45                }
  46
  47                raise(SIGCONT);
  48                bp_1();
  49                exit(0);
  50        }
  51
  52        return child;
  53}
  54
  55/*
  56 * This tests creates HW breakpoint, tries to
  57 * change it and checks it was properly changed.
  58 */
  59static int bp_modify1(void)
  60{
  61        pid_t child;
  62        int status;
  63        unsigned long rip = 0, dr7 = 1;
  64
  65        child = spawn_child();
  66
  67        waitpid(child, &status, 0);
  68        if (WIFEXITED(status)) {
  69                pr_debug("tracee exited prematurely 1\n");
  70                return TEST_FAIL;
  71        }
  72
  73        /*
  74         * The parent does following steps:
  75         *  - creates a new breakpoint (id 0) for bp_2 function
  76         *  - changes that breakpoint to bp_1 function
  77         *  - waits for the breakpoint to hit and checks
  78         *    it has proper rip of bp_1 function
  79         *  - detaches the child
  80         */
  81        if (ptrace(PTRACE_POKEUSER, child,
  82                   offsetof(struct user, u_debugreg[0]), bp_2)) {
  83                pr_debug("failed to set breakpoint, 1st time: %s\n",
  84                         strerror(errno));
  85                goto out;
  86        }
  87
  88        if (ptrace(PTRACE_POKEUSER, child,
  89                   offsetof(struct user, u_debugreg[0]), bp_1)) {
  90                pr_debug("failed to set breakpoint, 2nd time: %s\n",
  91                         strerror(errno));
  92                goto out;
  93        }
  94
  95        if (ptrace(PTRACE_POKEUSER, child,
  96                   offsetof(struct user, u_debugreg[7]), dr7)) {
  97                pr_debug("failed to set dr7: %s\n", strerror(errno));
  98                goto out;
  99        }
 100
 101        if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
 102                pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
 103                goto out;
 104        }
 105
 106        waitpid(child, &status, 0);
 107        if (WIFEXITED(status)) {
 108                pr_debug("tracee exited prematurely 2\n");
 109                return TEST_FAIL;
 110        }
 111
 112        rip = ptrace(PTRACE_PEEKUSER, child,
 113                     offsetof(struct user_regs_struct, rip), NULL);
 114        if (rip == (unsigned long) -1) {
 115                pr_debug("failed to PTRACE_PEEKUSER: %s\n",
 116                         strerror(errno));
 117                goto out;
 118        }
 119
 120        pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
 121
 122out:
 123        if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
 124                pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
 125                return TEST_FAIL;
 126        }
 127
 128        return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
 129}
 130
 131/*
 132 * This tests creates HW breakpoint, tries to
 133 * change it to bogus value and checks the original
 134 * breakpoint is hit.
 135 */
 136static int bp_modify2(void)
 137{
 138        pid_t child;
 139        int status;
 140        unsigned long rip = 0, dr7 = 1;
 141
 142        child = spawn_child();
 143
 144        waitpid(child, &status, 0);
 145        if (WIFEXITED(status)) {
 146                pr_debug("tracee exited prematurely 1\n");
 147                return TEST_FAIL;
 148        }
 149
 150        /*
 151         * The parent does following steps:
 152         *  - creates a new breakpoint (id 0) for bp_1 function
 153         *  - tries to change that breakpoint to (-1) address
 154         *  - waits for the breakpoint to hit and checks
 155         *    it has proper rip of bp_1 function
 156         *  - detaches the child
 157         */
 158        if (ptrace(PTRACE_POKEUSER, child,
 159                   offsetof(struct user, u_debugreg[0]), bp_1)) {
 160                pr_debug("failed to set breakpoint: %s\n",
 161                         strerror(errno));
 162                goto out;
 163        }
 164
 165        if (ptrace(PTRACE_POKEUSER, child,
 166                   offsetof(struct user, u_debugreg[7]), dr7)) {
 167                pr_debug("failed to set dr7: %s\n", strerror(errno));
 168                goto out;
 169        }
 170
 171        if (!ptrace(PTRACE_POKEUSER, child,
 172                   offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
 173                pr_debug("failed, breakpoint set to bogus address\n");
 174                goto out;
 175        }
 176
 177        if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
 178                pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
 179                goto out;
 180        }
 181
 182        waitpid(child, &status, 0);
 183        if (WIFEXITED(status)) {
 184                pr_debug("tracee exited prematurely 2\n");
 185                return TEST_FAIL;
 186        }
 187
 188        rip = ptrace(PTRACE_PEEKUSER, child,
 189                     offsetof(struct user_regs_struct, rip), NULL);
 190        if (rip == (unsigned long) -1) {
 191                pr_debug("failed to PTRACE_PEEKUSER: %s\n",
 192                         strerror(errno));
 193                goto out;
 194        }
 195
 196        pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
 197
 198out:
 199        if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
 200                pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
 201                return TEST_FAIL;
 202        }
 203
 204        return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
 205}
 206
 207int test__bp_modify(struct test *test __maybe_unused,
 208                    int subtest __maybe_unused)
 209{
 210        TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
 211        TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
 212
 213        return 0;
 214}
 215