Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/compiler.h>
0003 #include <sys/types.h>
0004 #include <sys/wait.h>
0005 #include <sys/user.h>
0006 #include <syscall.h>
0007 #include <unistd.h>
0008 #include <stdio.h>
0009 #include <stdlib.h>
0010 #include <string.h>
0011 #include <sys/ptrace.h>
0012 #include <asm/ptrace.h>
0013 #include <errno.h>
0014 #include "debug.h"
0015 #include "tests/tests.h"
0016 #include "arch-tests.h"
0017 
0018 static noinline int bp_1(void)
0019 {
0020     pr_debug("in %s\n", __func__);
0021     return 0;
0022 }
0023 
0024 static noinline int bp_2(void)
0025 {
0026     pr_debug("in %s\n", __func__);
0027     return 0;
0028 }
0029 
0030 static int spawn_child(void)
0031 {
0032     int child = fork();
0033 
0034     if (child == 0) {
0035         /*
0036          * The child sets itself for as tracee and
0037          * waits in signal for parent to trace it,
0038          * then it calls bp_1 and quits.
0039          */
0040         int err = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
0041 
0042         if (err) {
0043             pr_debug("failed to PTRACE_TRACEME\n");
0044             exit(1);
0045         }
0046 
0047         raise(SIGCONT);
0048         bp_1();
0049         exit(0);
0050     }
0051 
0052     return child;
0053 }
0054 
0055 /*
0056  * This tests creates HW breakpoint, tries to
0057  * change it and checks it was properly changed.
0058  */
0059 static int bp_modify1(void)
0060 {
0061     pid_t child;
0062     int status;
0063     unsigned long rip = 0, dr7 = 1;
0064 
0065     child = spawn_child();
0066 
0067     waitpid(child, &status, 0);
0068     if (WIFEXITED(status)) {
0069         pr_debug("tracee exited prematurely 1\n");
0070         return TEST_FAIL;
0071     }
0072 
0073     /*
0074      * The parent does following steps:
0075      *  - creates a new breakpoint (id 0) for bp_2 function
0076      *  - changes that breakpoint to bp_1 function
0077      *  - waits for the breakpoint to hit and checks
0078      *    it has proper rip of bp_1 function
0079      *  - detaches the child
0080      */
0081     if (ptrace(PTRACE_POKEUSER, child,
0082            offsetof(struct user, u_debugreg[0]), bp_2)) {
0083         pr_debug("failed to set breakpoint, 1st time: %s\n",
0084              strerror(errno));
0085         goto out;
0086     }
0087 
0088     if (ptrace(PTRACE_POKEUSER, child,
0089            offsetof(struct user, u_debugreg[0]), bp_1)) {
0090         pr_debug("failed to set breakpoint, 2nd time: %s\n",
0091              strerror(errno));
0092         goto out;
0093     }
0094 
0095     if (ptrace(PTRACE_POKEUSER, child,
0096            offsetof(struct user, u_debugreg[7]), dr7)) {
0097         pr_debug("failed to set dr7: %s\n", strerror(errno));
0098         goto out;
0099     }
0100 
0101     if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
0102         pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
0103         goto out;
0104     }
0105 
0106     waitpid(child, &status, 0);
0107     if (WIFEXITED(status)) {
0108         pr_debug("tracee exited prematurely 2\n");
0109         return TEST_FAIL;
0110     }
0111 
0112     rip = ptrace(PTRACE_PEEKUSER, child,
0113              offsetof(struct user_regs_struct, rip), NULL);
0114     if (rip == (unsigned long) -1) {
0115         pr_debug("failed to PTRACE_PEEKUSER: %s\n",
0116              strerror(errno));
0117         goto out;
0118     }
0119 
0120     pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
0121 
0122 out:
0123     if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
0124         pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
0125         return TEST_FAIL;
0126     }
0127 
0128     return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
0129 }
0130 
0131 /*
0132  * This tests creates HW breakpoint, tries to
0133  * change it to bogus value and checks the original
0134  * breakpoint is hit.
0135  */
0136 static int bp_modify2(void)
0137 {
0138     pid_t child;
0139     int status;
0140     unsigned long rip = 0, dr7 = 1;
0141 
0142     child = spawn_child();
0143 
0144     waitpid(child, &status, 0);
0145     if (WIFEXITED(status)) {
0146         pr_debug("tracee exited prematurely 1\n");
0147         return TEST_FAIL;
0148     }
0149 
0150     /*
0151      * The parent does following steps:
0152      *  - creates a new breakpoint (id 0) for bp_1 function
0153      *  - tries to change that breakpoint to (-1) address
0154      *  - waits for the breakpoint to hit and checks
0155      *    it has proper rip of bp_1 function
0156      *  - detaches the child
0157      */
0158     if (ptrace(PTRACE_POKEUSER, child,
0159            offsetof(struct user, u_debugreg[0]), bp_1)) {
0160         pr_debug("failed to set breakpoint: %s\n",
0161              strerror(errno));
0162         goto out;
0163     }
0164 
0165     if (ptrace(PTRACE_POKEUSER, child,
0166            offsetof(struct user, u_debugreg[7]), dr7)) {
0167         pr_debug("failed to set dr7: %s\n", strerror(errno));
0168         goto out;
0169     }
0170 
0171     if (!ptrace(PTRACE_POKEUSER, child,
0172            offsetof(struct user, u_debugreg[0]), (unsigned long) (-1))) {
0173         pr_debug("failed, breakpoint set to bogus address\n");
0174         goto out;
0175     }
0176 
0177     if (ptrace(PTRACE_CONT, child, NULL, NULL)) {
0178         pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno));
0179         goto out;
0180     }
0181 
0182     waitpid(child, &status, 0);
0183     if (WIFEXITED(status)) {
0184         pr_debug("tracee exited prematurely 2\n");
0185         return TEST_FAIL;
0186     }
0187 
0188     rip = ptrace(PTRACE_PEEKUSER, child,
0189              offsetof(struct user_regs_struct, rip), NULL);
0190     if (rip == (unsigned long) -1) {
0191         pr_debug("failed to PTRACE_PEEKUSER: %s\n",
0192              strerror(errno));
0193         goto out;
0194     }
0195 
0196     pr_debug("rip %lx, bp_1 %p\n", rip, bp_1);
0197 
0198 out:
0199     if (ptrace(PTRACE_DETACH, child, NULL, NULL)) {
0200         pr_debug("failed to PTRACE_DETACH: %s", strerror(errno));
0201         return TEST_FAIL;
0202     }
0203 
0204     return rip == (unsigned long) bp_1 ? TEST_OK : TEST_FAIL;
0205 }
0206 
0207 int test__bp_modify(struct test_suite *test __maybe_unused,
0208             int subtest __maybe_unused)
0209 {
0210     TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
0211     TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());
0212 
0213     return 0;
0214 }