1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
11 #include <sys/ptrace.h>
12 #include <asm/ptrace.h>
15 #include "tests/tests.h"
16 #include "arch-tests.h"
18 static noinline
int bp_1(void)
20 pr_debug("in %s\n", __func__
);
24 static noinline
int bp_2(void)
26 pr_debug("in %s\n", __func__
);
30 static int spawn_child(void)
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.
40 int err
= ptrace(PTRACE_TRACEME
, 0, NULL
, NULL
);
43 pr_debug("failed to PTRACE_TRACEME\n");
56 * This tests creates HW breakpoint, tries to
57 * change it and checks it was properly changed.
59 static int bp_modify1(void)
63 unsigned long rip
= 0, dr7
= 1;
65 child
= spawn_child();
67 waitpid(child
, &status
, 0);
68 if (WIFEXITED(status
)) {
69 pr_debug("tracee exited prematurely 1\n");
74 * The parent does following steps:
75 * - creates a new breakpoint (id 0) for bp_2 function
76 * - changes that breakponit 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
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",
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",
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
));
101 if (ptrace(PTRACE_CONT
, child
, NULL
, NULL
)) {
102 pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno
));
106 waitpid(child
, &status
, 0);
107 if (WIFEXITED(status
)) {
108 pr_debug("tracee exited prematurely 2\n");
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",
120 pr_debug("rip %lx, bp_1 %p\n", rip
, bp_1
);
123 if (ptrace(PTRACE_DETACH
, child
, NULL
, NULL
)) {
124 pr_debug("failed to PTRACE_DETACH: %s", strerror(errno
));
128 return rip
== (unsigned long) bp_1
? TEST_OK
: TEST_FAIL
;
132 * This tests creates HW breakpoint, tries to
133 * change it to bogus value and checks the original
136 static int bp_modify2(void)
140 unsigned long rip
= 0, dr7
= 1;
142 child
= spawn_child();
144 waitpid(child
, &status
, 0);
145 if (WIFEXITED(status
)) {
146 pr_debug("tracee exited prematurely 1\n");
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
158 if (ptrace(PTRACE_POKEUSER
, child
,
159 offsetof(struct user
, u_debugreg
[0]), bp_1
)) {
160 pr_debug("failed to set breakpoint: %s\n",
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
));
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");
177 if (ptrace(PTRACE_CONT
, child
, NULL
, NULL
)) {
178 pr_debug("failed to PTRACE_CONT: %s\n", strerror(errno
));
182 waitpid(child
, &status
, 0);
183 if (WIFEXITED(status
)) {
184 pr_debug("tracee exited prematurely 2\n");
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",
196 pr_debug("rip %lx, bp_1 %p\n", rip
, bp_1
);
199 if (ptrace(PTRACE_DETACH
, child
, NULL
, NULL
)) {
200 pr_debug("failed to PTRACE_DETACH: %s", strerror(errno
));
204 return rip
== (unsigned long) bp_1
? TEST_OK
: TEST_FAIL
;
207 int test__bp_modify(struct test
*test __maybe_unused
,
208 int subtest __maybe_unused
)
210 TEST_ASSERT_VAL("modify test 1 failed\n", !bp_modify1());
211 TEST_ASSERT_VAL("modify test 2 failed\n", !bp_modify2());