blob: 8fe7a295f2dd21236ed97aed198bf4347b3e9115 [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>
Josh Gao5e3fe952017-02-16 14:12:41 -080020#include <fcntl.h>
Pavel Labath1faca6c2016-04-21 15:13:22 +010021#include <sched.h>
22#include <sys/prctl.h>
Josh Gao5e3fe952017-02-16 14:12:41 -080023#include <sys/ptrace.h>
Pavel Labath1faca6c2016-04-21 15:13:22 +010024#include <sys/uio.h>
25#include <sys/user.h>
Josh Gao5e3fe952017-02-16 14:12:41 -080026#include <sys/wait.h>
Pavel Labath1faca6c2016-04-21 15:13:22 +010027#include <unistd.h>
28
29#include <gtest/gtest.h>
30
Josh Gao5e3fe952017-02-16 14:12:41 -080031#include <android-base/unique_fd.h>
32
33using android::base::unique_fd;
34
Pavel Labath1faca6c2016-04-21 15:13:22 +010035// Host libc does not define this.
36#ifndef TRAP_HWBKPT
37#define TRAP_HWBKPT 4
38#endif
39
Pavel Labathfb082ee2017-01-23 15:41:35 +000040template <typename T>
41static void __attribute__((noreturn)) watchpoint_fork_child(unsigned cpu, T& data) {
Pavel Labath1faca6c2016-04-21 15:13:22 +010042 // Extra precaution: make sure we go away if anything happens to our parent.
43 if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
44 perror("prctl(PR_SET_PDEATHSIG)");
45 _exit(1);
46 }
47
48 cpu_set_t cpus;
49 CPU_ZERO(&cpus);
50 CPU_SET(cpu, &cpus);
51 if (sched_setaffinity(0, sizeof cpus, &cpus) == -1) {
52 perror("sched_setaffinity");
53 _exit(2);
54 }
55 if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
56 perror("ptrace(PTRACE_TRACEME)");
57 _exit(3);
58 }
59
60 raise(SIGSTOP); // Synchronize with the tracer, let it set the watchpoint.
61
62 data = 1; // Now trigger the watchpoint.
63
64 _exit(0);
65}
66
67class ChildGuard {
68 public:
Chih-Hung Hsieh62e3a072016-05-03 12:08:05 -070069 explicit ChildGuard(pid_t pid) : pid(pid) {}
Pavel Labath1faca6c2016-04-21 15:13:22 +010070
71 ~ChildGuard() {
72 kill(pid, SIGKILL);
73 int status;
74 waitpid(pid, &status, 0);
75 }
76
77 private:
78 pid_t pid;
79};
80
Pavel Labathfb082ee2017-01-23 15:41:35 +000081enum class HwFeature { Watchpoint, Breakpoint };
82
83static bool is_hw_feature_supported(pid_t child, HwFeature feature) {
Pavel Labath1faca6c2016-04-21 15:13:22 +010084#if defined(__arm__)
85 long capabilities;
86 long result = ptrace(PTRACE_GETHBPREGS, child, 0, &capabilities);
87 if (result == -1) {
88 EXPECT_EQ(EIO, errno);
89 return false;
90 }
Pavel Labathfb082ee2017-01-23 15:41:35 +000091 switch (feature) {
92 case HwFeature::Watchpoint:
93 return ((capabilities >> 8) & 0xff) > 0;
94 case HwFeature::Breakpoint:
95 return (capabilities & 0xff) > 0;
96 }
Pavel Labath1faca6c2016-04-21 15:13:22 +010097#elif defined(__aarch64__)
98 user_hwdebug_state dreg_state;
99 iovec iov;
100 iov.iov_base = &dreg_state;
101 iov.iov_len = sizeof(dreg_state);
102
Pavel Labathfb082ee2017-01-23 15:41:35 +0000103 long result = ptrace(PTRACE_GETREGSET, child,
104 feature == HwFeature::Watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK, &iov);
Pavel Labath1faca6c2016-04-21 15:13:22 +0100105 if (result == -1) {
106 EXPECT_EQ(EINVAL, errno);
107 return false;
108 }
109 return (dreg_state.dbg_info & 0xff) > 0;
110#elif defined(__i386__) || defined(__x86_64__)
Pavel Labathfb082ee2017-01-23 15:41:35 +0000111 // We assume watchpoints and breakpoints are always supported on x86.
Pavel Labath1faca6c2016-04-21 15:13:22 +0100112 (void) child;
Pavel Labathfb082ee2017-01-23 15:41:35 +0000113 (void)feature;
Pavel Labath1faca6c2016-04-21 15:13:22 +0100114 return true;
115#else
116 // TODO: mips support.
117 (void) child;
Pavel Labathfb082ee2017-01-23 15:41:35 +0000118 (void)feature;
Pavel Labath1faca6c2016-04-21 15:13:22 +0100119 return false;
120#endif
121}
122
123static void set_watchpoint(pid_t child, const void *address, size_t size) {
124#if defined(__arm__) || defined(__aarch64__)
125 const unsigned byte_mask = (1 << size) - 1;
126 const unsigned type = 2; // Write.
127 const unsigned enable = 1;
128 const unsigned control = byte_mask << 5 | type << 3 | enable;
129
130#ifdef __arm__
131 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, -1, &address)) << strerror(errno);
132 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, -2, &control)) << strerror(errno);
133#else // aarch64
134 user_hwdebug_state dreg_state;
135 memset(&dreg_state, 0, sizeof dreg_state);
136 dreg_state.dbg_regs[0].addr = reinterpret_cast<uintptr_t>(address);
137 dreg_state.dbg_regs[0].ctrl = control;
138
139 iovec iov;
140 iov.iov_base = &dreg_state;
141 iov.iov_len = offsetof(user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]);
142
143 ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, child, NT_ARM_HW_WATCH, &iov)) << strerror(errno);
144#endif
145#elif defined(__i386__) || defined(__x86_64__)
146 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[0]), address)) << strerror(errno);
147 errno = 0;
148 unsigned data = ptrace(PTRACE_PEEKUSER, child, offsetof(user, u_debugreg[7]), nullptr);
149 ASSERT_EQ(0, errno);
150
151 const unsigned size_flag = (size == 8) ? 2 : size - 1;
152 const unsigned enable = 1;
153 const unsigned type = 1; // Write.
154
155 const unsigned mask = 3 << 18 | 3 << 16 | 1;
156 const unsigned value = size_flag << 18 | type << 16 | enable;
157 data &= mask;
158 data |= value;
159 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[7]), data)) << strerror(errno);
160#else
161 (void) child;
162 (void) address;
163 (void) size;
164#endif
165}
166
167template<typename T>
168static void run_watchpoint_test_impl(unsigned cpu) {
169 alignas(8) T data = 0;
170
171 pid_t child = fork();
172 ASSERT_NE(-1, child) << strerror(errno);
Pavel Labathfb082ee2017-01-23 15:41:35 +0000173 if (child == 0) watchpoint_fork_child(cpu, data);
Pavel Labath1faca6c2016-04-21 15:13:22 +0100174
175 ChildGuard guard(child);
176
177 int status;
178 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
179 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
180 ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
181
Pavel Labathfb082ee2017-01-23 15:41:35 +0000182 if (!is_hw_feature_supported(child, HwFeature::Watchpoint)) {
183 GTEST_LOG_(INFO) << "Skipping test because hardware support is not available.\n";
184 return;
185 }
Pavel Labath1faca6c2016-04-21 15:13:22 +0100186
187 set_watchpoint(child, &data, sizeof data);
188
189 ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
190 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
191 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
192 ASSERT_EQ(SIGTRAP, WSTOPSIG(status)) << "Status was: " << status;
193
194 siginfo_t siginfo;
195 ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
196 ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
197#if defined(__arm__) || defined(__aarch64__)
198 ASSERT_EQ(&data, siginfo.si_addr);
199#endif
200}
201
202static void run_watchpoint_test(unsigned cpu) {
203 run_watchpoint_test_impl<uint8_t>(cpu);
204 run_watchpoint_test_impl<uint16_t>(cpu);
205 run_watchpoint_test_impl<uint32_t>(cpu);
Josh Gaob36efa42016-09-15 13:55:41 -0700206#if defined(__LP64__)
Pavel Labath1faca6c2016-04-21 15:13:22 +0100207 run_watchpoint_test_impl<uint64_t>(cpu);
208#endif
209}
210
211// Test watchpoint API. The test is considered successful if our watchpoints get hit OR the
212// system reports that watchpoint support is not present. We run the test for different
213// watchpoint sizes, while pinning the process to each cpu in turn, for better coverage.
Pavel Labathfb082ee2017-01-23 15:41:35 +0000214TEST(sys_ptrace, watchpoint_stress) {
Pavel Labath1faca6c2016-04-21 15:13:22 +0100215 cpu_set_t available_cpus;
216 ASSERT_EQ(0, sched_getaffinity(0, sizeof available_cpus, &available_cpus));
217
218 for (size_t cpu = 0; cpu < CPU_SETSIZE; ++cpu) {
219 if (!CPU_ISSET(cpu, &available_cpus)) continue;
220 run_watchpoint_test(cpu);
221 }
222}
Pavel Labathfb082ee2017-01-23 15:41:35 +0000223
224static void __attribute__((noinline)) breakpoint_func() {
225 asm volatile("");
226}
227
228static void __attribute__((noreturn)) breakpoint_fork_child() {
229 // Extra precaution: make sure we go away if anything happens to our parent.
230 if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
231 perror("prctl(PR_SET_PDEATHSIG)");
232 _exit(1);
233 }
234
235 if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
236 perror("ptrace(PTRACE_TRACEME)");
237 _exit(2);
238 }
239
240 raise(SIGSTOP); // Synchronize with the tracer, let it set the breakpoint.
241
242 breakpoint_func(); // Now trigger the breakpoint.
243
244 _exit(0);
245}
246
247static void set_breakpoint(pid_t child) {
248 uintptr_t address = uintptr_t(breakpoint_func);
249#if defined(__arm__) || defined(__aarch64__)
250 address &= ~3;
251 const unsigned byte_mask = 0xf;
252 const unsigned enable = 1;
253 const unsigned control = byte_mask << 5 | enable;
254
255#ifdef __arm__
256 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, 1, &address)) << strerror(errno);
257 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, 2, &control)) << strerror(errno);
258#else // aarch64
259 user_hwdebug_state dreg_state;
260 memset(&dreg_state, 0, sizeof dreg_state);
261 dreg_state.dbg_regs[0].addr = reinterpret_cast<uintptr_t>(address);
262 dreg_state.dbg_regs[0].ctrl = control;
263
264 iovec iov;
265 iov.iov_base = &dreg_state;
266 iov.iov_len = offsetof(user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]);
267
268 ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, child, NT_ARM_HW_BREAK, &iov)) << strerror(errno);
269#endif
270#elif defined(__i386__) || defined(__x86_64__)
271 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[0]), address))
272 << strerror(errno);
273 errno = 0;
274 unsigned data = ptrace(PTRACE_PEEKUSER, child, offsetof(user, u_debugreg[7]), nullptr);
275 ASSERT_EQ(0, errno);
276
277 const unsigned size = 0;
278 const unsigned enable = 1;
279 const unsigned type = 0; // Execute
280
281 const unsigned mask = 3 << 18 | 3 << 16 | 1;
282 const unsigned value = size << 18 | type << 16 | enable;
283 data &= mask;
284 data |= value;
285 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[7]), data))
286 << strerror(errno);
287#else
288 (void)child;
Pavel Labathbb9713d2017-01-27 13:04:26 +0000289 (void)address;
Pavel Labathfb082ee2017-01-23 15:41:35 +0000290#endif
291}
292
293// Test hardware breakpoint API. The test is considered successful if the breakpoints get hit OR the
294// system reports that hardware breakpoint support is not present.
295TEST(sys_ptrace, hardware_breakpoint) {
296 pid_t child = fork();
297 ASSERT_NE(-1, child) << strerror(errno);
298 if (child == 0) breakpoint_fork_child();
299
300 ChildGuard guard(child);
301
302 int status;
303 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
304 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
305 ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
306
307 if (!is_hw_feature_supported(child, HwFeature::Breakpoint)) {
308 GTEST_LOG_(INFO) << "Skipping test because hardware support is not available.\n";
309 return;
310 }
311
312 set_breakpoint(child);
313
314 ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
315 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
316 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
317 ASSERT_EQ(SIGTRAP, WSTOPSIG(status)) << "Status was: " << status;
318
319 siginfo_t siginfo;
320 ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
321 ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
322}
Josh Gao5e3fe952017-02-16 14:12:41 -0800323
324class PtraceResumptionTest : public ::testing::Test {
325 public:
326 pid_t worker = -1;
327 PtraceResumptionTest() {
328 }
329
330 ~PtraceResumptionTest() {
331 }
332
333 void AssertDeath(int signo);
334 void Start(std::function<void()> f) {
335 unique_fd worker_pipe_read, worker_pipe_write;
336 int pipefd[2];
337 ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
338 worker_pipe_read.reset(pipefd[0]);
339 worker_pipe_write.reset(pipefd[1]);
340
341 worker = fork();
342 ASSERT_NE(-1, worker);
343 if (worker == 0) {
344 char buf;
345 worker_pipe_write.reset();
346 TEMP_FAILURE_RETRY(read(worker_pipe_read.get(), &buf, sizeof(buf)));
347 exit(0);
348 }
349
350 pid_t tracer = fork();
351 ASSERT_NE(-1, tracer);
352 if (tracer == 0) {
353 f();
354 if (HasFatalFailure()) {
355 exit(1);
356 }
357 exit(0);
358 }
359
360 int result;
361 pid_t rc = waitpid(tracer, &result, 0);
362 ASSERT_EQ(tracer, rc);
363 EXPECT_TRUE(WIFEXITED(result) || WIFSIGNALED(result));
364 if (WIFEXITED(result)) {
365 if (WEXITSTATUS(result) != 0) {
366 FAIL() << "tracer failed";
367 }
368 }
369
370 rc = waitpid(worker, &result, WNOHANG);
371 ASSERT_EQ(0, rc);
372
373 worker_pipe_write.reset();
374
375 rc = waitpid(worker, &result, 0);
376 ASSERT_EQ(worker, rc);
377 EXPECT_TRUE(WIFEXITED(result));
378 EXPECT_EQ(WEXITSTATUS(result), 0);
379 }
380};
381
382static void wait_for_ptrace_stop(pid_t pid) {
383 while (true) {
384 int status;
385 pid_t rc = TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL));
386 if (rc != pid) {
387 abort();
388 }
389 if (WIFSTOPPED(status)) {
390 return;
391 }
392 }
393}
394
395TEST_F(PtraceResumptionTest, seize) {
396 Start([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
397}
398
399TEST_F(PtraceResumptionTest, seize_interrupt) {
400 Start([this]() {
401 ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
402 ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
403 });
404}
405
406TEST_F(PtraceResumptionTest, seize_interrupt_cont) {
407 Start([this]() {
408 ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
409 ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
410 wait_for_ptrace_stop(worker);
411 ASSERT_EQ(0, ptrace(PTRACE_CONT, worker, 0, 0)) << strerror(errno);
412 });
413}