blob: 00322eca2f54bd1a1c2728a2dbc51a25f6e9b42e [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 Gaobc055ca2017-03-29 15:01:15 -070020#include <err.h>
Josh Gao5e3fe952017-02-16 14:12:41 -080021#include <fcntl.h>
Pavel Labath1faca6c2016-04-21 15:13:22 +010022#include <sched.h>
23#include <sys/prctl.h>
Josh Gao5e3fe952017-02-16 14:12:41 -080024#include <sys/ptrace.h>
Pavel Labath1faca6c2016-04-21 15:13:22 +010025#include <sys/uio.h>
26#include <sys/user.h>
Josh Gao5e3fe952017-02-16 14:12:41 -080027#include <sys/wait.h>
Pavel Labath1faca6c2016-04-21 15:13:22 +010028#include <unistd.h>
29
Josh Gaobc055ca2017-03-29 15:01:15 -070030#include <chrono>
31#include <thread>
32
Pavel Labath1faca6c2016-04-21 15:13:22 +010033#include <gtest/gtest.h>
34
Pavel Labath3dad8d52017-02-22 18:22:46 +000035#include <android-base/macros.h>
Josh Gao5e3fe952017-02-16 14:12:41 -080036#include <android-base/unique_fd.h>
37
Josh Gaobc055ca2017-03-29 15:01:15 -070038using namespace std::chrono_literals;
39
Josh Gao5e3fe952017-02-16 14:12:41 -080040using android::base::unique_fd;
41
Pavel Labath1faca6c2016-04-21 15:13:22 +010042// Host libc does not define this.
43#ifndef TRAP_HWBKPT
44#define TRAP_HWBKPT 4
45#endif
46
Pavel Labath1faca6c2016-04-21 15:13:22 +010047class ChildGuard {
48 public:
Chih-Hung Hsieh62e3a072016-05-03 12:08:05 -070049 explicit ChildGuard(pid_t pid) : pid(pid) {}
Pavel Labath1faca6c2016-04-21 15:13:22 +010050
51 ~ChildGuard() {
52 kill(pid, SIGKILL);
53 int status;
54 waitpid(pid, &status, 0);
55 }
56
57 private:
58 pid_t pid;
59};
60
Pavel Labathfb082ee2017-01-23 15:41:35 +000061enum class HwFeature { Watchpoint, Breakpoint };
62
63static bool is_hw_feature_supported(pid_t child, HwFeature feature) {
Pavel Labath1faca6c2016-04-21 15:13:22 +010064#if defined(__arm__)
65 long capabilities;
66 long result = ptrace(PTRACE_GETHBPREGS, child, 0, &capabilities);
67 if (result == -1) {
68 EXPECT_EQ(EIO, errno);
69 return false;
70 }
Pavel Labathfb082ee2017-01-23 15:41:35 +000071 switch (feature) {
72 case HwFeature::Watchpoint:
73 return ((capabilities >> 8) & 0xff) > 0;
74 case HwFeature::Breakpoint:
75 return (capabilities & 0xff) > 0;
76 }
Pavel Labath1faca6c2016-04-21 15:13:22 +010077#elif defined(__aarch64__)
78 user_hwdebug_state dreg_state;
79 iovec iov;
80 iov.iov_base = &dreg_state;
81 iov.iov_len = sizeof(dreg_state);
82
Pavel Labathfb082ee2017-01-23 15:41:35 +000083 long result = ptrace(PTRACE_GETREGSET, child,
84 feature == HwFeature::Watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK, &iov);
Pavel Labath1faca6c2016-04-21 15:13:22 +010085 if (result == -1) {
86 EXPECT_EQ(EINVAL, errno);
87 return false;
88 }
89 return (dreg_state.dbg_info & 0xff) > 0;
90#elif defined(__i386__) || defined(__x86_64__)
Pavel Labathfb082ee2017-01-23 15:41:35 +000091 // We assume watchpoints and breakpoints are always supported on x86.
Pavel Labath3dad8d52017-02-22 18:22:46 +000092 UNUSED(child);
93 UNUSED(feature);
Pavel Labath1faca6c2016-04-21 15:13:22 +010094 return true;
95#else
96 // TODO: mips support.
Pavel Labath3dad8d52017-02-22 18:22:46 +000097 UNUSED(child);
98 UNUSED(feature);
Pavel Labath1faca6c2016-04-21 15:13:22 +010099 return false;
100#endif
101}
102
Pavel Labath3dad8d52017-02-22 18:22:46 +0000103static void set_watchpoint(pid_t child, uintptr_t address, size_t size) {
104 ASSERT_EQ(0u, address & 0x7) << "address: " << address;
Pavel Labath1faca6c2016-04-21 15:13:22 +0100105#if defined(__arm__) || defined(__aarch64__)
106 const unsigned byte_mask = (1 << size) - 1;
107 const unsigned type = 2; // Write.
108 const unsigned enable = 1;
109 const unsigned control = byte_mask << 5 | type << 3 | enable;
110
111#ifdef __arm__
112 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, -1, &address)) << strerror(errno);
113 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, -2, &control)) << strerror(errno);
114#else // aarch64
115 user_hwdebug_state dreg_state;
116 memset(&dreg_state, 0, sizeof dreg_state);
Pavel Labath3dad8d52017-02-22 18:22:46 +0000117 dreg_state.dbg_regs[0].addr = address;
Pavel Labath1faca6c2016-04-21 15:13:22 +0100118 dreg_state.dbg_regs[0].ctrl = control;
119
120 iovec iov;
121 iov.iov_base = &dreg_state;
122 iov.iov_len = offsetof(user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]);
123
124 ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, child, NT_ARM_HW_WATCH, &iov)) << strerror(errno);
125#endif
126#elif defined(__i386__) || defined(__x86_64__)
127 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[0]), address)) << strerror(errno);
128 errno = 0;
129 unsigned data = ptrace(PTRACE_PEEKUSER, child, offsetof(user, u_debugreg[7]), nullptr);
130 ASSERT_EQ(0, errno);
131
132 const unsigned size_flag = (size == 8) ? 2 : size - 1;
133 const unsigned enable = 1;
134 const unsigned type = 1; // Write.
135
136 const unsigned mask = 3 << 18 | 3 << 16 | 1;
137 const unsigned value = size_flag << 18 | type << 16 | enable;
138 data &= mask;
139 data |= value;
140 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[7]), data)) << strerror(errno);
141#else
Pavel Labath3dad8d52017-02-22 18:22:46 +0000142 UNUSED(child);
143 UNUSED(address);
144 UNUSED(size);
Pavel Labath1faca6c2016-04-21 15:13:22 +0100145#endif
146}
147
Pavel Labath3dad8d52017-02-22 18:22:46 +0000148template <typename T>
149static void run_watchpoint_test(std::function<void(T&)> child_func, size_t offset, size_t size) {
150 alignas(16) T data{};
Pavel Labath1faca6c2016-04-21 15:13:22 +0100151
152 pid_t child = fork();
153 ASSERT_NE(-1, child) << strerror(errno);
Pavel Labath3dad8d52017-02-22 18:22:46 +0000154 if (child == 0) {
155 // Extra precaution: make sure we go away if anything happens to our parent.
156 if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
157 perror("prctl(PR_SET_PDEATHSIG)");
158 _exit(1);
159 }
160
161 if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
162 perror("ptrace(PTRACE_TRACEME)");
163 _exit(2);
164 }
165
166 child_func(data);
167 _exit(0);
168 }
Pavel Labath1faca6c2016-04-21 15:13:22 +0100169
170 ChildGuard guard(child);
171
172 int status;
173 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
174 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
175 ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
176
Pavel Labathfb082ee2017-01-23 15:41:35 +0000177 if (!is_hw_feature_supported(child, HwFeature::Watchpoint)) {
178 GTEST_LOG_(INFO) << "Skipping test because hardware support is not available.\n";
179 return;
180 }
Pavel Labath1faca6c2016-04-21 15:13:22 +0100181
Pavel Labath3dad8d52017-02-22 18:22:46 +0000182 set_watchpoint(child, uintptr_t(&data) + offset, size);
Pavel Labath1faca6c2016-04-21 15:13:22 +0100183
184 ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
185 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
186 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
187 ASSERT_EQ(SIGTRAP, WSTOPSIG(status)) << "Status was: " << status;
188
189 siginfo_t siginfo;
190 ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
191 ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
192#if defined(__arm__) || defined(__aarch64__)
Pavel Labath3dad8d52017-02-22 18:22:46 +0000193 ASSERT_LE(&data, siginfo.si_addr);
194 ASSERT_GT((&data) + 1, siginfo.si_addr);
Pavel Labath1faca6c2016-04-21 15:13:22 +0100195#endif
196}
197
Pavel Labath3dad8d52017-02-22 18:22:46 +0000198template <typename T>
199static void watchpoint_stress_child(unsigned cpu, T& data) {
200 cpu_set_t cpus;
201 CPU_ZERO(&cpus);
202 CPU_SET(cpu, &cpus);
203 if (sched_setaffinity(0, sizeof cpus, &cpus) == -1) {
204 perror("sched_setaffinity");
205 _exit(3);
206 }
207 raise(SIGSTOP); // Synchronize with the tracer, let it set the watchpoint.
208
209 data = 1; // Now trigger the watchpoint.
210}
211
212template <typename T>
213static void run_watchpoint_stress(size_t cpu) {
214 run_watchpoint_test<T>(std::bind(watchpoint_stress_child<T>, cpu, std::placeholders::_1), 0,
215 sizeof(T));
Pavel Labath1faca6c2016-04-21 15:13:22 +0100216}
217
218// Test watchpoint API. The test is considered successful if our watchpoints get hit OR the
219// system reports that watchpoint support is not present. We run the test for different
220// watchpoint sizes, while pinning the process to each cpu in turn, for better coverage.
Pavel Labathfb082ee2017-01-23 15:41:35 +0000221TEST(sys_ptrace, watchpoint_stress) {
Pavel Labath1faca6c2016-04-21 15:13:22 +0100222 cpu_set_t available_cpus;
223 ASSERT_EQ(0, sched_getaffinity(0, sizeof available_cpus, &available_cpus));
224
225 for (size_t cpu = 0; cpu < CPU_SETSIZE; ++cpu) {
226 if (!CPU_ISSET(cpu, &available_cpus)) continue;
Pavel Labath3dad8d52017-02-22 18:22:46 +0000227
228 run_watchpoint_stress<uint8_t>(cpu);
229 run_watchpoint_stress<uint16_t>(cpu);
230 run_watchpoint_stress<uint32_t>(cpu);
231#if defined(__LP64__)
232 run_watchpoint_stress<uint64_t>(cpu);
233#endif
Pavel Labath1faca6c2016-04-21 15:13:22 +0100234 }
235}
Pavel Labathfb082ee2017-01-23 15:41:35 +0000236
Pavel Labath3dad8d52017-02-22 18:22:46 +0000237struct Uint128_t {
238 uint64_t data[2];
239};
240static void watchpoint_imprecise_child(Uint128_t& data) {
241 raise(SIGSTOP); // Synchronize with the tracer, let it set the watchpoint.
242
243#if defined(__i386__) || defined(__x86_64__)
244 asm volatile("movdqa %%xmm0, %0" : : "m"(data));
245#elif defined(__arm__)
246 asm volatile("stm %0, { r0, r1, r2, r3 }" : : "r"(&data));
247#elif defined(__aarch64__)
248 asm volatile("stp x0, x1, %0" : : "m"(data));
249#elif defined(__mips__)
250// TODO
Pavel Labathfb5a6392017-02-24 10:14:13 +0000251 UNUSED(data);
Pavel Labath3dad8d52017-02-22 18:22:46 +0000252#endif
253}
254
255// Test that the kernel is able to handle the case when the instruction writes
256// to a larger block of memory than the one we are watching. If you see this
257// test fail on arm64, you will likely need to cherry-pick fdfeff0f into your
258// kernel.
259TEST(sys_ptrace, watchpoint_imprecise) {
260 // Make sure we get interrupted in case a buggy kernel does not report the
261 // watchpoint hit correctly.
262 struct sigaction action, oldaction;
263 action.sa_handler = [](int) {};
264 sigemptyset(&action.sa_mask);
265 action.sa_flags = 0;
266 ASSERT_EQ(0, sigaction(SIGALRM, &action, &oldaction)) << strerror(errno);
267 alarm(5);
268
Pavel Labath4a620262017-04-26 11:30:06 +0100269 run_watchpoint_test<Uint128_t>(watchpoint_imprecise_child, 8, sizeof(void*));
Pavel Labath3dad8d52017-02-22 18:22:46 +0000270
271 ASSERT_EQ(0, sigaction(SIGALRM, &oldaction, nullptr)) << strerror(errno);
272}
273
Pavel Labathfb082ee2017-01-23 15:41:35 +0000274static void __attribute__((noinline)) breakpoint_func() {
275 asm volatile("");
276}
277
278static void __attribute__((noreturn)) breakpoint_fork_child() {
279 // Extra precaution: make sure we go away if anything happens to our parent.
280 if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0) == -1) {
281 perror("prctl(PR_SET_PDEATHSIG)");
282 _exit(1);
283 }
284
285 if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) {
286 perror("ptrace(PTRACE_TRACEME)");
287 _exit(2);
288 }
289
290 raise(SIGSTOP); // Synchronize with the tracer, let it set the breakpoint.
291
292 breakpoint_func(); // Now trigger the breakpoint.
293
294 _exit(0);
295}
296
297static void set_breakpoint(pid_t child) {
298 uintptr_t address = uintptr_t(breakpoint_func);
299#if defined(__arm__) || defined(__aarch64__)
300 address &= ~3;
301 const unsigned byte_mask = 0xf;
302 const unsigned enable = 1;
303 const unsigned control = byte_mask << 5 | enable;
304
305#ifdef __arm__
306 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, 1, &address)) << strerror(errno);
307 ASSERT_EQ(0, ptrace(PTRACE_SETHBPREGS, child, 2, &control)) << strerror(errno);
308#else // aarch64
309 user_hwdebug_state dreg_state;
310 memset(&dreg_state, 0, sizeof dreg_state);
311 dreg_state.dbg_regs[0].addr = reinterpret_cast<uintptr_t>(address);
312 dreg_state.dbg_regs[0].ctrl = control;
313
314 iovec iov;
315 iov.iov_base = &dreg_state;
316 iov.iov_len = offsetof(user_hwdebug_state, dbg_regs) + sizeof(dreg_state.dbg_regs[0]);
317
318 ASSERT_EQ(0, ptrace(PTRACE_SETREGSET, child, NT_ARM_HW_BREAK, &iov)) << strerror(errno);
319#endif
320#elif defined(__i386__) || defined(__x86_64__)
321 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[0]), address))
322 << strerror(errno);
323 errno = 0;
324 unsigned data = ptrace(PTRACE_PEEKUSER, child, offsetof(user, u_debugreg[7]), nullptr);
325 ASSERT_EQ(0, errno);
326
327 const unsigned size = 0;
328 const unsigned enable = 1;
329 const unsigned type = 0; // Execute
330
331 const unsigned mask = 3 << 18 | 3 << 16 | 1;
332 const unsigned value = size << 18 | type << 16 | enable;
333 data &= mask;
334 data |= value;
335 ASSERT_EQ(0, ptrace(PTRACE_POKEUSER, child, offsetof(user, u_debugreg[7]), data))
336 << strerror(errno);
337#else
Pavel Labath3dad8d52017-02-22 18:22:46 +0000338 UNUSED(child);
339 UNUSED(address);
Pavel Labathfb082ee2017-01-23 15:41:35 +0000340#endif
341}
342
343// Test hardware breakpoint API. The test is considered successful if the breakpoints get hit OR the
344// system reports that hardware breakpoint support is not present.
345TEST(sys_ptrace, hardware_breakpoint) {
346 pid_t child = fork();
347 ASSERT_NE(-1, child) << strerror(errno);
348 if (child == 0) breakpoint_fork_child();
349
350 ChildGuard guard(child);
351
352 int status;
353 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
354 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
355 ASSERT_EQ(SIGSTOP, WSTOPSIG(status)) << "Status was: " << status;
356
357 if (!is_hw_feature_supported(child, HwFeature::Breakpoint)) {
358 GTEST_LOG_(INFO) << "Skipping test because hardware support is not available.\n";
359 return;
360 }
361
362 set_breakpoint(child);
363
364 ASSERT_EQ(0, ptrace(PTRACE_CONT, child, nullptr, nullptr)) << strerror(errno);
365 ASSERT_EQ(child, waitpid(child, &status, __WALL)) << strerror(errno);
366 ASSERT_TRUE(WIFSTOPPED(status)) << "Status was: " << status;
367 ASSERT_EQ(SIGTRAP, WSTOPSIG(status)) << "Status was: " << status;
368
369 siginfo_t siginfo;
370 ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
371 ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
372}
Josh Gao5e3fe952017-02-16 14:12:41 -0800373
374class PtraceResumptionTest : public ::testing::Test {
375 public:
Josh Gaobc055ca2017-03-29 15:01:15 -0700376 unique_fd worker_pipe_write;
377
Josh Gao5e3fe952017-02-16 14:12:41 -0800378 pid_t worker = -1;
Josh Gaobc055ca2017-03-29 15:01:15 -0700379 pid_t tracer = -1;
380
Josh Gao5e3fe952017-02-16 14:12:41 -0800381 PtraceResumptionTest() {
Josh Gaobc055ca2017-03-29 15:01:15 -0700382 unique_fd worker_pipe_read;
383 int pipefd[2];
384 if (pipe2(pipefd, O_CLOEXEC) != 0) {
385 err(1, "failed to create pipe");
386 }
387
388 worker_pipe_read.reset(pipefd[0]);
389 worker_pipe_write.reset(pipefd[1]);
390
391 worker = fork();
392 if (worker == -1) {
393 err(1, "failed to fork worker");
394 } else if (worker == 0) {
395 char buf;
396 worker_pipe_write.reset();
397 TEMP_FAILURE_RETRY(read(worker_pipe_read.get(), &buf, sizeof(buf)));
398 exit(0);
399 }
Josh Gao5e3fe952017-02-16 14:12:41 -0800400 }
401
402 ~PtraceResumptionTest() {
403 }
404
405 void AssertDeath(int signo);
Josh Gao5e3fe952017-02-16 14:12:41 -0800406
Josh Gaobc055ca2017-03-29 15:01:15 -0700407 void StartTracer(std::function<void()> f) {
408 tracer = fork();
Josh Gao5e3fe952017-02-16 14:12:41 -0800409 ASSERT_NE(-1, tracer);
410 if (tracer == 0) {
411 f();
412 if (HasFatalFailure()) {
413 exit(1);
414 }
415 exit(0);
416 }
Josh Gaobc055ca2017-03-29 15:01:15 -0700417 }
418
419 bool WaitForTracer() {
420 if (tracer == -1) {
421 errx(1, "tracer not started");
422 }
Josh Gao5e3fe952017-02-16 14:12:41 -0800423
424 int result;
425 pid_t rc = waitpid(tracer, &result, 0);
Josh Gaobc055ca2017-03-29 15:01:15 -0700426 if (rc != tracer) {
427 printf("waitpid returned %d (%s)\n", rc, strerror(errno));
428 return false;
429 }
430
431 if (!WIFEXITED(result) && !WIFSIGNALED(result)) {
432 printf("!WIFEXITED && !WIFSIGNALED\n");
433 return false;
434 }
435
Josh Gao5e3fe952017-02-16 14:12:41 -0800436 if (WIFEXITED(result)) {
437 if (WEXITSTATUS(result) != 0) {
Josh Gaobc055ca2017-03-29 15:01:15 -0700438 printf("tracer failed\n");
439 return false;
Josh Gao5e3fe952017-02-16 14:12:41 -0800440 }
441 }
442
Josh Gaobc055ca2017-03-29 15:01:15 -0700443 return true;
444 }
445
446 bool WaitForWorker() {
447 if (worker == -1) {
448 errx(1, "worker not started");
449 }
450
451 int result;
452 pid_t rc = waitpid(worker, &result, WNOHANG);
453 if (rc != 0) {
454 printf("worker exited prematurely\n");
455 return false;
456 }
Josh Gao5e3fe952017-02-16 14:12:41 -0800457
458 worker_pipe_write.reset();
459
460 rc = waitpid(worker, &result, 0);
Josh Gaobc055ca2017-03-29 15:01:15 -0700461 if (rc != worker) {
462 printf("waitpid for worker returned %d (%s)\n", rc, strerror(errno));
463 return false;
464 }
465
466 if (!WIFEXITED(result)) {
467 printf("worker didn't exit\n");
468 return false;
469 }
470
471 if (WEXITSTATUS(result) != 0) {
472 printf("worker exited with status %d\n", WEXITSTATUS(result));
473 return false;
474 }
475
476 return true;
Josh Gao5e3fe952017-02-16 14:12:41 -0800477 }
478};
479
480static void wait_for_ptrace_stop(pid_t pid) {
481 while (true) {
482 int status;
483 pid_t rc = TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL));
484 if (rc != pid) {
485 abort();
486 }
487 if (WIFSTOPPED(status)) {
488 return;
489 }
490 }
491}
492
Josh Gaobc055ca2017-03-29 15:01:15 -0700493TEST_F(PtraceResumptionTest, smoke) {
494 // Make sure that the worker doesn't exit before the tracer stops tracing.
495 StartTracer([this]() {
496 ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
497 ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
498 wait_for_ptrace_stop(worker);
499 std::this_thread::sleep_for(500ms);
500 });
501
502 worker_pipe_write.reset();
503 std::this_thread::sleep_for(250ms);
504
505 int result;
506 ASSERT_EQ(0, waitpid(worker, &result, WNOHANG));
507 ASSERT_TRUE(WaitForTracer());
508 ASSERT_EQ(worker, waitpid(worker, &result, 0));
509}
510
Josh Gao5e3fe952017-02-16 14:12:41 -0800511TEST_F(PtraceResumptionTest, seize) {
Josh Gaobc055ca2017-03-29 15:01:15 -0700512 StartTracer([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
513 ASSERT_TRUE(WaitForTracer());
514 ASSERT_TRUE(WaitForWorker());
Josh Gao5e3fe952017-02-16 14:12:41 -0800515}
516
517TEST_F(PtraceResumptionTest, seize_interrupt) {
Josh Gaobc055ca2017-03-29 15:01:15 -0700518 StartTracer([this]() {
Josh Gao5e3fe952017-02-16 14:12:41 -0800519 ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
520 ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
Josh Gaobc055ca2017-03-29 15:01:15 -0700521 wait_for_ptrace_stop(worker);
Josh Gao5e3fe952017-02-16 14:12:41 -0800522 });
Josh Gaobc055ca2017-03-29 15:01:15 -0700523 ASSERT_TRUE(WaitForTracer());
524 ASSERT_TRUE(WaitForWorker());
Josh Gao5e3fe952017-02-16 14:12:41 -0800525}
526
527TEST_F(PtraceResumptionTest, seize_interrupt_cont) {
Josh Gaobc055ca2017-03-29 15:01:15 -0700528 StartTracer([this]() {
Josh Gao5e3fe952017-02-16 14:12:41 -0800529 ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
530 ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
531 wait_for_ptrace_stop(worker);
532 ASSERT_EQ(0, ptrace(PTRACE_CONT, worker, 0, 0)) << strerror(errno);
533 });
Josh Gaobc055ca2017-03-29 15:01:15 -0700534 ASSERT_TRUE(WaitForTracer());
535 ASSERT_TRUE(WaitForWorker());
536}
537
538TEST_F(PtraceResumptionTest, zombie_seize) {
539 StartTracer([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
540 ASSERT_TRUE(WaitForWorker());
541 ASSERT_TRUE(WaitForTracer());
542}
543
544TEST_F(PtraceResumptionTest, zombie_seize_interrupt) {
545 StartTracer([this]() {
546 ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
547 ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
548 wait_for_ptrace_stop(worker);
549 });
550 ASSERT_TRUE(WaitForWorker());
551 ASSERT_TRUE(WaitForTracer());
552}
553
554TEST_F(PtraceResumptionTest, zombie_seize_interrupt_cont) {
555 StartTracer([this]() {
556 ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
557 ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
558 wait_for_ptrace_stop(worker);
559 ASSERT_EQ(0, ptrace(PTRACE_CONT, worker, 0, 0)) << strerror(errno);
560 });
561 ASSERT_TRUE(WaitForWorker());
562 ASSERT_TRUE(WaitForTracer());
Josh Gao5e3fe952017-02-16 14:12:41 -0800563}