Add tests for ptrace resumption behavior.
Add tests that ensure that the kernel behaves properly w.r.t.
resumption of ptraced processes when the tracer dies.
Bug: http://b/34516140
Test: /data/nativetest/bionic-unit-tests/bionic-unit-tests --gtest_filter="PtraceResumption*"
Change-Id: Id35e069a7e5edd6964637dd3f6358ad59db19792
diff --git a/tests/sys_ptrace_test.cpp b/tests/sys_ptrace_test.cpp
index 7483754..8fe7a29 100644
--- a/tests/sys_ptrace_test.cpp
+++ b/tests/sys_ptrace_test.cpp
@@ -17,14 +17,21 @@
#include <sys/ptrace.h>
#include <elf.h>
+#include <fcntl.h>
#include <sched.h>
#include <sys/prctl.h>
+#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/user.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <gtest/gtest.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
// Host libc does not define this.
#ifndef TRAP_HWBKPT
#define TRAP_HWBKPT 4
@@ -313,3 +320,94 @@
ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child, nullptr, &siginfo)) << strerror(errno);
ASSERT_EQ(TRAP_HWBKPT, siginfo.si_code);
}
+
+class PtraceResumptionTest : public ::testing::Test {
+ public:
+ pid_t worker = -1;
+ PtraceResumptionTest() {
+ }
+
+ ~PtraceResumptionTest() {
+ }
+
+ void AssertDeath(int signo);
+ void Start(std::function<void()> f) {
+ unique_fd worker_pipe_read, worker_pipe_write;
+ int pipefd[2];
+ ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
+ worker_pipe_read.reset(pipefd[0]);
+ worker_pipe_write.reset(pipefd[1]);
+
+ worker = fork();
+ ASSERT_NE(-1, worker);
+ if (worker == 0) {
+ char buf;
+ worker_pipe_write.reset();
+ TEMP_FAILURE_RETRY(read(worker_pipe_read.get(), &buf, sizeof(buf)));
+ exit(0);
+ }
+
+ pid_t tracer = fork();
+ ASSERT_NE(-1, tracer);
+ if (tracer == 0) {
+ f();
+ if (HasFatalFailure()) {
+ exit(1);
+ }
+ exit(0);
+ }
+
+ int result;
+ pid_t rc = waitpid(tracer, &result, 0);
+ ASSERT_EQ(tracer, rc);
+ EXPECT_TRUE(WIFEXITED(result) || WIFSIGNALED(result));
+ if (WIFEXITED(result)) {
+ if (WEXITSTATUS(result) != 0) {
+ FAIL() << "tracer failed";
+ }
+ }
+
+ rc = waitpid(worker, &result, WNOHANG);
+ ASSERT_EQ(0, rc);
+
+ worker_pipe_write.reset();
+
+ rc = waitpid(worker, &result, 0);
+ ASSERT_EQ(worker, rc);
+ EXPECT_TRUE(WIFEXITED(result));
+ EXPECT_EQ(WEXITSTATUS(result), 0);
+ }
+};
+
+static void wait_for_ptrace_stop(pid_t pid) {
+ while (true) {
+ int status;
+ pid_t rc = TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL));
+ if (rc != pid) {
+ abort();
+ }
+ if (WIFSTOPPED(status)) {
+ return;
+ }
+ }
+}
+
+TEST_F(PtraceResumptionTest, seize) {
+ Start([this]() { ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno); });
+}
+
+TEST_F(PtraceResumptionTest, seize_interrupt) {
+ Start([this]() {
+ ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
+ ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+ });
+}
+
+TEST_F(PtraceResumptionTest, seize_interrupt_cont) {
+ Start([this]() {
+ ASSERT_EQ(0, ptrace(PTRACE_SEIZE, worker, 0, 0)) << strerror(errno);
+ ASSERT_EQ(0, ptrace(PTRACE_INTERRUPT, worker, 0, 0)) << strerror(errno);
+ wait_for_ptrace_stop(worker);
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, worker, 0, 0)) << strerror(errno);
+ });
+}