blob: 74837547a17578042f4fb4a6e54d522b397fc740 [file] [log] [blame]
Pavel Labath1faca6c2016-04-21 15:13:22 +01001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <sys/ptrace.h>
18
19#include <elf.h>
20#include <sched.h>
21#include <sys/prctl.h>
22#include <sys/uio.h>
23#include <sys/user.h>
24#include <unistd.h>
25
26#include <gtest/gtest.h>
27
28// Host libc does not define this.
29#ifndef TRAP_HWBKPT
30#define TRAP_HWBKPT 4
31#endif
32
Pavel Labathfb082ee2017-01-23 15:41:35 +000033template <typename T>
34static void __attribute__((noreturn)) watchpoint_fork_child(unsigned cpu, T& data) {
Pavel Labath1faca6c2016-04-21 15:13:22 +010035 // Extra precaution: make sure we go away if anything happens to our parent.
36 if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
37 perror("prctl(PR_SET_PDEATHSIG)");
38 _exit(1);
39 }
40
41 cpu_set_t cpus;
42 CPU_ZERO(&cpus);
43 CPU_SET(cpu, &cpus);
44 if (sched_setaffinity(0, sizeof cpus, &cpus) == -1) {
45 perror("sched_setaffinity");
46 _exit(2);
47 }
48 if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
49 perror("ptrace(PTRACE_TRACEME)");
50 _exit(3);
51 }
52
53 raise(SIGSTOP); // Synchronize with the tracer, let it set the watchpoint.
54
55 data = 1; // Now trigger the watchpoint.
56
57 _exit(0);
58}
59
60class ChildGuard {
61 public:
Chih-Hung Hsieh62e3a072016-05-03 12:08:05 -070062 explicit ChildGuard(pid_t pid) : pid(pid) {}
Pavel Labath1faca6c2016-04-21 15:13:22 +010063
64 ~ChildGuard() {
65 kill(pid, SIGKILL);
66 int status;
67 waitpid(pid, &status, 0);
68 }
69
70 private:
71 pid_t pid;
72};
73
Pavel Labathfb082ee2017-01-23 15:41:35 +000074enum class HwFeature { Watchpoint, Breakpoint };
75
76static bool is_hw_feature_supported(pid_t child, HwFeature feature) {
Pavel Labath1faca6c2016-04-21 15:13:22 +010077#if defined(__arm__)
78 long capabilities;
79 long result = ptrace(PTRACE_GETHBPREGS, child, 0, &capabilities);
80 if (result == -1) {
81 EXPECT_EQ(EIO, errno);
82 return false;
83 }
Pavel Labathfb082ee2017-01-23 15:41:35 +000084 switch (feature) {
85 case HwFeature::Watchpoint:
86 return ((capabilities >> 8) & 0xff) > 0;
87 case HwFeature::Breakpoint:
88 return (capabilities & 0xff) > 0;
89 }
Pavel Labath1faca6c2016-04-21 15:13:22 +010090#elif defined(__aarch64__)
91 user_hwdebug_state dreg_state;
92 iovec iov;
93 iov.iov_base = &dreg_state;
94 iov.iov_len = sizeof(dreg_state);
95
Pavel Labathfb082ee2017-01-23 15:41:35 +000096 long result = ptrace(PTRACE_GETREGSET, child,
97 feature == HwFeature::Watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK, &iov);
Pavel Labath1faca6c2016-04-21 15:13:22 +010098 if (result == -1) {
99 EXPECT_EQ(EINVAL, errno);
100 return false;
101 }
102 return (dreg_state.dbg_info & 0xff) > 0;
103#elif defined(__i386__) || defined(__x86_64__)
Pavel Labathfb082ee2017-01-23 15:41:35 +0000104 // We assume watchpoints and breakpoints are always supported on x86.
Pavel Labath1faca6c2016-04-21 15:13:22 +0100105 (void) child;
Pavel Labathfb082ee2017-01-23 15:41:35 +0000106 (void)feature;
Pavel Labath1faca6c2016-04-21 15:13:22 +0100107 return true;
108#else
109 // TODO: mips support.
110 (void) child;
Pavel Labathfb082ee2017-01-23 15:41:35 +0000111 (void)feature;
Pavel Labath1faca6c2016-04-21 15:13:22 +0100112 return false;
113#endif
114}
115
116static void set_watchpoint(pid_t child, const void *address, size_t size) {
117#if defined(__arm__) || defined(__aarch64__)
118 const unsigned byte_mask = (1 << size) - 1;
119 const unsigned type = 2; // Write.
120 const unsigned enable = 1;
121 const unsigned control = byte_mask << 5 | type << 3 | enable;
122
123#ifdef __arm__
124 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, -1, &address)) << strerror(errno);
125 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, -2, &control)) << strerror(errno);
126#else // aarch64
127 user_hwdebug_state dreg_state;
128 memset(&dreg_state, 0, sizeof dreg_state);
129 dreg_state.dbg_regs[0].addr = reinterpret_cast<uintptr_t>(address);
130 dreg_state.dbg_regs[0].ctrl = control;
131
132 iovec iov;
133 iov.iov_base = &dreg_state;
134 iov.iov_len = offsetof(user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]);
135
136 ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, child, NT_ARM_HW_WATCH, &iov)) << strerror(errno);
137#endif
138#elif defined(__i386__) || defined(__x86_64__)
139 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[0]), address)) << strerror(errno);
140 errno = 0;
141 unsigned data = ptrace(PTRACE_PEEKUSER, child, offsetof(user, u_debugreg[7]), nullptr);
142 ASSERT_EQ(0, errno);
143
144 const unsigned size_flag = (size == 8) ? 2 : size - 1;
145 const unsigned enable = 1;
146 const unsigned type = 1; // Write.
147
148 const unsigned mask = 3 << 18 | 3 << 16 | 1;
149 const unsigned value = size_flag << 18 | type << 16 | enable;
150 data &= mask;
151 data |= value;
152 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[7]), data)) << strerror(errno);
153#else
154 (void) child;
155 (void) address;
156 (void) size;
157#endif
158}
159
160template<typename T>
161static void run_watchpoint_test_impl(unsigned cpu) {
162 alignas(8) T data = 0;
163
164 pid_t child = fork();
165 ASSERT_NE(-1, child) << strerror(errno);
Pavel Labathfb082ee2017-01-23 15:41:35 +0000166 if (child == 0) watchpoint_fork_child(cpu, data);
Pavel Labath1faca6c2016-04-21 15:13:22 +0100167
168 ChildGuard guard(child);
169
170 int status;
171 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
172 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
173 ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
174
Pavel Labathfb082ee2017-01-23 15:41:35 +0000175 if (!is_hw_feature_supported(child, HwFeature::Watchpoint)) {
176 GTEST_LOG_(INFO) << "Skipping test because hardware support is not available.\n";
177 return;
178 }
Pavel Labath1faca6c2016-04-21 15:13:22 +0100179
180 set_watchpoint(child, &data, sizeof data);
181
182 ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
183 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
184 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
185 ASSERT_EQ(SIGTRAP, WSTOPSIG(status)) << "Status was: " << status;
186
187 siginfo_t siginfo;
188 ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
189 ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
190#if defined(__arm__) || defined(__aarch64__)
191 ASSERT_EQ(&data, siginfo.si_addr);
192#endif
193}
194
195static void run_watchpoint_test(unsigned cpu) {
196 run_watchpoint_test_impl<uint8_t>(cpu);
197 run_watchpoint_test_impl<uint16_t>(cpu);
198 run_watchpoint_test_impl<uint32_t>(cpu);
Josh Gaob36efa42016-09-15 13:55:41 -0700199#if defined(__LP64__)
Pavel Labath1faca6c2016-04-21 15:13:22 +0100200 run_watchpoint_test_impl<uint64_t>(cpu);
201#endif
202}
203
204// Test watchpoint API. The test is considered successful if our watchpoints get hit OR the
205// system reports that watchpoint support is not present. We run the test for different
206// watchpoint sizes, while pinning the process to each cpu in turn, for better coverage.
Pavel Labathfb082ee2017-01-23 15:41:35 +0000207TEST(sys_ptrace, watchpoint_stress) {
Pavel Labath1faca6c2016-04-21 15:13:22 +0100208 cpu_set_t available_cpus;
209 ASSERT_EQ(0, sched_getaffinity(0, sizeof available_cpus, &available_cpus));
210
211 for (size_t cpu = 0; cpu < CPU_SETSIZE; ++cpu) {
212 if (!CPU_ISSET(cpu, &available_cpus)) continue;
213 run_watchpoint_test(cpu);
214 }
215}
Pavel Labathfb082ee2017-01-23 15:41:35 +0000216
217static void __attribute__((noinline)) breakpoint_func() {
218 asm volatile("");
219}
220
221static void __attribute__((noreturn)) breakpoint_fork_child() {
222 // Extra precaution: make sure we go away if anything happens to our parent.
223 if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
224 perror("prctl(PR_SET_PDEATHSIG)");
225 _exit(1);
226 }
227
228 if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
229 perror("ptrace(PTRACE_TRACEME)");
230 _exit(2);
231 }
232
233 raise(SIGSTOP); // Synchronize with the tracer, let it set the breakpoint.
234
235 breakpoint_func(); // Now trigger the breakpoint.
236
237 _exit(0);
238}
239
240static void set_breakpoint(pid_t child) {
241 uintptr_t address = uintptr_t(breakpoint_func);
242#if defined(__arm__) || defined(__aarch64__)
243 address &= ~3;
244 const unsigned byte_mask = 0xf;
245 const unsigned enable = 1;
246 const unsigned control = byte_mask << 5 | enable;
247
248#ifdef __arm__
249 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, 1, &address)) << strerror(errno);
250 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, 2, &control)) << strerror(errno);
251#else // aarch64
252 user_hwdebug_state dreg_state;
253 memset(&dreg_state, 0, sizeof dreg_state);
254 dreg_state.dbg_regs[0].addr = reinterpret_cast<uintptr_t>(address);
255 dreg_state.dbg_regs[0].ctrl = control;
256
257 iovec iov;
258 iov.iov_base = &dreg_state;
259 iov.iov_len = offsetof(user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]);
260
261 ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, child, NT_ARM_HW_BREAK, &iov)) << strerror(errno);
262#endif
263#elif defined(__i386__) || defined(__x86_64__)
264 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[0]), address))
265 << strerror(errno);
266 errno = 0;
267 unsigned data = ptrace(PTRACE_PEEKUSER, child, offsetof(user, u_debugreg[7]), nullptr);
268 ASSERT_EQ(0, errno);
269
270 const unsigned size = 0;
271 const unsigned enable = 1;
272 const unsigned type = 0; // Execute
273
274 const unsigned mask = 3 << 18 | 3 << 16 | 1;
275 const unsigned value = size << 18 | type << 16 | enable;
276 data &= mask;
277 data |= value;
278 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[7]), data))
279 << strerror(errno);
280#else
281 (void)child;
Pavel Labathbb9713d2017-01-27 13:04:26 +0000282 (void)address;
Pavel Labathfb082ee2017-01-23 15:41:35 +0000283#endif
284}
285
286// Test hardware breakpoint API. The test is considered successful if the breakpoints get hit OR the
287// system reports that hardware breakpoint support is not present.
288TEST(sys_ptrace, hardware_breakpoint) {
289 pid_t child = fork();
290 ASSERT_NE(-1, child) << strerror(errno);
291 if (child == 0) breakpoint_fork_child();
292
293 ChildGuard guard(child);
294
295 int status;
296 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
297 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
298 ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
299
300 if (!is_hw_feature_supported(child, HwFeature::Breakpoint)) {
301 GTEST_LOG_(INFO) << "Skipping test because hardware support is not available.\n";
302 return;
303 }
304
305 set_breakpoint(child);
306
307 ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
308 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
309 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
310 ASSERT_EQ(SIGTRAP, WSTOPSIG(status)) << "Status was: " << status;
311
312 siginfo_t siginfo;
313 ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
314 ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
315}