blob: 568879ed566b769ab6f28f3c6a6d4139cc1ef27b [file] [log] [blame]
Josh Gaocbe70cb2016-10-18 18:17:52 -07001/*
2 * Copyright 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 <err.h>
18#include <fcntl.h>
19#include <unistd.h>
Josh Gao502cfd22017-02-17 01:39:15 -080020#include <sys/capability.h>
Josh Gaofca7ca32017-01-23 12:05:35 -080021#include <sys/prctl.h>
Josh Gaocbe70cb2016-10-18 18:17:52 -070022#include <sys/types.h>
23
24#include <chrono>
25#include <regex>
26#include <thread>
27
Josh Gao502cfd22017-02-17 01:39:15 -080028#include <android/set_abort_message.h>
29
Josh Gaocbe70cb2016-10-18 18:17:52 -070030#include <android-base/file.h>
31#include <android-base/logging.h>
32#include <android-base/parseint.h>
33#include <android-base/properties.h>
34#include <android-base/strings.h>
35#include <android-base/unique_fd.h>
36#include <cutils/sockets.h>
37#include <debuggerd/handler.h>
38#include <debuggerd/protocol.h>
Josh Gao352a8452017-03-30 16:46:21 -070039#include <debuggerd/tombstoned.h>
Josh Gaocbe70cb2016-10-18 18:17:52 -070040#include <debuggerd/util.h>
41#include <gtest/gtest.h>
42
43using namespace std::chrono_literals;
44using android::base::unique_fd;
45
46#if defined(__LP64__)
Josh Gaocbe70cb2016-10-18 18:17:52 -070047#define ARCH_SUFFIX "64"
48#else
Josh Gaocbe70cb2016-10-18 18:17:52 -070049#define ARCH_SUFFIX ""
50#endif
51
52constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
53
54#define TIMEOUT(seconds, expr) \
55 [&]() { \
56 struct sigaction old_sigaction; \
57 struct sigaction new_sigaction = {}; \
58 new_sigaction.sa_handler = [](int) {}; \
59 if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
60 err(1, "sigaction failed"); \
61 } \
62 alarm(seconds); \
63 auto value = expr; \
64 int saved_errno = errno; \
65 if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
66 err(1, "sigaction failed"); \
67 } \
68 alarm(0); \
69 errno = saved_errno; \
70 return value; \
71 }()
72
73#define ASSERT_MATCH(str, pattern) \
74 do { \
75 std::regex r((pattern)); \
76 if (!std::regex_search((str), r)) { \
77 FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
78 } \
79 } while (0)
80
Josh Gaoe06f2a42017-04-27 16:50:38 -070081#define ASSERT_NOT_MATCH(str, pattern) \
82 do { \
83 std::regex r((pattern)); \
84 if (std::regex_search((str), r)) { \
85 FAIL() << "regex mismatch: expected to not find " << (pattern) << " in: \n" << (str); \
86 } \
87 } while (0)
88
Josh Gao460b3362017-03-30 16:40:47 -070089static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
90 intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
91 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
92 if (intercept_fd->get() == -1) {
93 FAIL() << "failed to contact tombstoned: " << strerror(errno);
94 }
95
96 InterceptRequest req = {.pid = target_pid};
97
98 unique_fd output_pipe_write;
99 if (!Pipe(output_fd, &output_pipe_write)) {
100 FAIL() << "failed to create output pipe: " << strerror(errno);
101 }
102
103 std::string pipe_size_str;
104 int pipe_buffer_size;
105 if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
106 FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
107 }
108
109 pipe_size_str = android::base::Trim(pipe_size_str);
110
111 if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
112 FAIL() << "failed to parse pipe max size";
113 }
114
115 if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
116 FAIL() << "failed to set pipe size: " << strerror(errno);
117 }
118
119 if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
120 FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
121 }
122
123 InterceptResponse response;
124 ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
125 if (rc == -1) {
126 FAIL() << "failed to read response from tombstoned: " << strerror(errno);
127 } else if (rc == 0) {
128 FAIL() << "failed to read response from tombstoned (EOF)";
129 } else if (rc != sizeof(response)) {
130 FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
131 << ", received " << rc;
132 }
133
134 ASSERT_EQ(InterceptStatus::kRegistered, response.status);
135}
136
Josh Gaocbe70cb2016-10-18 18:17:52 -0700137class CrasherTest : public ::testing::Test {
138 public:
139 pid_t crasher_pid = -1;
140 bool previous_wait_for_gdb;
141 unique_fd crasher_pipe;
142 unique_fd intercept_fd;
143
144 CrasherTest();
145 ~CrasherTest();
146
147 void StartIntercept(unique_fd* output_fd);
148
149 // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
150 void FinishIntercept(int* result);
151
Josh Gaofca7ca32017-01-23 12:05:35 -0800152 void StartProcess(std::function<void()> function);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700153 void StartCrasher(const std::string& crash_type);
154 void FinishCrasher();
155 void AssertDeath(int signo);
156};
157
158CrasherTest::CrasherTest() {
159 previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
160 android::base::SetProperty(kWaitForGdbKey, "0");
161}
162
163CrasherTest::~CrasherTest() {
164 if (crasher_pid != -1) {
165 kill(crasher_pid, SIGKILL);
166 int status;
167 waitpid(crasher_pid, &status, WUNTRACED);
168 }
169
170 android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
171}
172
173void CrasherTest::StartIntercept(unique_fd* output_fd) {
174 if (crasher_pid == -1) {
175 FAIL() << "crasher hasn't been started";
176 }
177
Josh Gao460b3362017-03-30 16:40:47 -0700178 tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700179}
180
181void CrasherTest::FinishIntercept(int* result) {
182 InterceptResponse response;
183
184 // Timeout for tombstoned intercept is 10 seconds.
185 ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
186 if (rc == -1) {
187 FAIL() << "failed to read response from tombstoned: " << strerror(errno);
188 } else if (rc == 0) {
189 *result = -1;
190 } else if (rc != sizeof(response)) {
191 FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
192 << ", received " << rc;
193 } else {
Josh Gao460b3362017-03-30 16:40:47 -0700194 *result = response.status == InterceptStatus::kStarted ? 1 : 0;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700195 }
196}
197
Josh Gaofca7ca32017-01-23 12:05:35 -0800198void CrasherTest::StartProcess(std::function<void()> function) {
199 unique_fd read_pipe;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700200 unique_fd crasher_read_pipe;
201 if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
202 FAIL() << "failed to create pipe: " << strerror(errno);
203 }
204
205 crasher_pid = fork();
206 if (crasher_pid == -1) {
207 FAIL() << "fork failed: " << strerror(errno);
208 } else if (crasher_pid == 0) {
Josh Gao502cfd22017-02-17 01:39:15 -0800209 char dummy;
210 crasher_pipe.reset();
211 TEMP_FAILURE_RETRY(read(crasher_read_pipe.get(), &dummy, 1));
Josh Gaofca7ca32017-01-23 12:05:35 -0800212 function();
213 _exit(0);
214 }
215}
216
Josh Gaocbe70cb2016-10-18 18:17:52 -0700217void CrasherTest::FinishCrasher() {
218 if (crasher_pipe == -1) {
219 FAIL() << "crasher pipe uninitialized";
220 }
221
222 ssize_t rc = write(crasher_pipe.get(), "\n", 1);
223 if (rc == -1) {
224 FAIL() << "failed to write to crasher pipe: " << strerror(errno);
225 } else if (rc == 0) {
226 FAIL() << "crasher pipe was closed";
227 }
228}
229
230void CrasherTest::AssertDeath(int signo) {
231 int status;
232 pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
233 if (pid != crasher_pid) {
234 FAIL() << "failed to wait for crasher: " << strerror(errno);
235 }
236
Josh Gaoe06f2a42017-04-27 16:50:38 -0700237 if (signo == 0) {
238 ASSERT_TRUE(WIFEXITED(status));
239 ASSERT_EQ(0, WEXITSTATUS(signo));
240 } else {
241 ASSERT_FALSE(WIFEXITED(status));
242 ASSERT_TRUE(WIFSIGNALED(status)) << "crasher didn't terminate via a signal";
243 ASSERT_EQ(signo, WTERMSIG(status));
Josh Gaocbe70cb2016-10-18 18:17:52 -0700244 }
Josh Gaocbe70cb2016-10-18 18:17:52 -0700245 crasher_pid = -1;
246}
247
248static void ConsumeFd(unique_fd fd, std::string* output) {
249 constexpr size_t read_length = PAGE_SIZE;
250 std::string result;
251
252 while (true) {
253 size_t offset = result.size();
254 result.resize(result.size() + PAGE_SIZE);
255 ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
256 if (rc == -1) {
257 FAIL() << "read failed: " << strerror(errno);
258 } else if (rc == 0) {
259 result.resize(result.size() - PAGE_SIZE);
260 break;
261 }
262
263 result.resize(result.size() - PAGE_SIZE + rc);
264 }
265
266 *output = std::move(result);
267}
268
269TEST_F(CrasherTest, smoke) {
270 int intercept_result;
271 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800272 StartProcess([]() {
273 *reinterpret_cast<volatile char*>(0xdead) = '1';
274 });
275
Josh Gaocbe70cb2016-10-18 18:17:52 -0700276 StartIntercept(&output_fd);
277 FinishCrasher();
278 AssertDeath(SIGSEGV);
279 FinishIntercept(&intercept_result);
280
281 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
282
283 std::string result;
284 ConsumeFd(std::move(output_fd), &result);
285 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
286}
287
288TEST_F(CrasherTest, abort) {
289 int intercept_result;
290 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800291 StartProcess([]() {
292 abort();
293 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700294 StartIntercept(&output_fd);
295 FinishCrasher();
296 AssertDeath(SIGABRT);
297 FinishIntercept(&intercept_result);
298
299 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
300
301 std::string result;
302 ConsumeFd(std::move(output_fd), &result);
303 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
304}
305
306TEST_F(CrasherTest, signal) {
307 int intercept_result;
308 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800309 StartProcess([]() {
310 abort();
311 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700312 StartIntercept(&output_fd);
313
314 // Wait for a bit, or we might end up killing the process before the signal
315 // handler even gets a chance to be registered.
316 std::this_thread::sleep_for(100ms);
317 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
318
319 AssertDeath(SIGSEGV);
320 FinishIntercept(&intercept_result);
321
322 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
323
324 std::string result;
325 ConsumeFd(std::move(output_fd), &result);
326 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
327 ASSERT_MATCH(result, R"(backtrace:)");
328}
329
330TEST_F(CrasherTest, abort_message) {
331 int intercept_result;
332 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800333 StartProcess([]() {
334 android_set_abort_message("abort message goes here");
335 abort();
336 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700337 StartIntercept(&output_fd);
338 FinishCrasher();
339 AssertDeath(SIGABRT);
340 FinishIntercept(&intercept_result);
341
342 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
343
344 std::string result;
345 ConsumeFd(std::move(output_fd), &result);
Josh Gao502cfd22017-02-17 01:39:15 -0800346 ASSERT_MATCH(result, R"(Abort message: 'abort message goes here')");
Josh Gaocbe70cb2016-10-18 18:17:52 -0700347}
348
Josh Gaoe06f2a42017-04-27 16:50:38 -0700349TEST_F(CrasherTest, abort_message_backtrace) {
350 int intercept_result;
351 unique_fd output_fd;
352 StartProcess([]() {
353 android_set_abort_message("not actually aborting");
354 raise(DEBUGGER_SIGNAL);
355 exit(0);
356 });
357 StartIntercept(&output_fd);
358 FinishCrasher();
359 AssertDeath(0);
360 FinishIntercept(&intercept_result);
361
362 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
363
364 std::string result;
365 ConsumeFd(std::move(output_fd), &result);
366 ASSERT_NOT_MATCH(result, R"(Abort message:)");
367}
368
Josh Gaocbe70cb2016-10-18 18:17:52 -0700369TEST_F(CrasherTest, intercept_timeout) {
370 int intercept_result;
371 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800372 StartProcess([]() {
373 abort();
374 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700375 StartIntercept(&output_fd);
376
377 // Don't let crasher finish until we timeout.
378 FinishIntercept(&intercept_result);
379
380 ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
381 << intercept_result << ")";
382
383 FinishCrasher();
384 AssertDeath(SIGABRT);
385}
386
387TEST_F(CrasherTest, wait_for_gdb) {
388 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
389 FAIL() << "failed to enable wait_for_gdb";
390 }
391 sleep(1);
392
Josh Gao502cfd22017-02-17 01:39:15 -0800393 StartProcess([]() {
394 abort();
395 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700396 FinishCrasher();
397
398 int status;
399 ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
400 ASSERT_TRUE(WIFSTOPPED(status));
401 ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
402
403 ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
404
405 AssertDeath(SIGABRT);
406}
407
Josh Gao7c6e3132017-01-22 17:59:02 -0800408// wait_for_gdb shouldn't trigger on manually sent signals.
Josh Gaocbe70cb2016-10-18 18:17:52 -0700409TEST_F(CrasherTest, wait_for_gdb_signal) {
410 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
411 FAIL() << "failed to enable wait_for_gdb";
412 }
413
Josh Gao502cfd22017-02-17 01:39:15 -0800414 StartProcess([]() {
415 abort();
416 });
Josh Gao7c6e3132017-01-22 17:59:02 -0800417 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
418 AssertDeath(SIGSEGV);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700419}
420
421TEST_F(CrasherTest, backtrace) {
422 std::string result;
423 int intercept_result;
424 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800425
426 StartProcess([]() {
427 abort();
428 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700429 StartIntercept(&output_fd);
430
431 std::this_thread::sleep_for(500ms);
432
433 sigval val;
434 val.sival_int = 1;
435 ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
436 FinishIntercept(&intercept_result);
437 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
438 ConsumeFd(std::move(output_fd), &result);
439 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
440
441 int status;
442 ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
443
444 StartIntercept(&output_fd);
445 FinishCrasher();
446 AssertDeath(SIGABRT);
447 FinishIntercept(&intercept_result);
448 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
449 ConsumeFd(std::move(output_fd), &result);
450 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
451}
Josh Gaofca7ca32017-01-23 12:05:35 -0800452
453TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
Josh Gao502cfd22017-02-17 01:39:15 -0800454 int intercept_result;
455 unique_fd output_fd;
Josh Gaofca7ca32017-01-23 12:05:35 -0800456 StartProcess([]() {
457 prctl(PR_SET_DUMPABLE, 0);
Josh Gao502cfd22017-02-17 01:39:15 -0800458 abort();
Josh Gaofca7ca32017-01-23 12:05:35 -0800459 });
Josh Gao502cfd22017-02-17 01:39:15 -0800460
461 StartIntercept(&output_fd);
462 FinishCrasher();
463 AssertDeath(SIGABRT);
464 FinishIntercept(&intercept_result);
465
466 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
467
468 std::string result;
469 ConsumeFd(std::move(output_fd), &result);
470 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
Josh Gaofca7ca32017-01-23 12:05:35 -0800471}
472
Josh Gao502cfd22017-02-17 01:39:15 -0800473TEST_F(CrasherTest, capabilities) {
474 ASSERT_EQ(0U, getuid()) << "capability test requires root";
475
Josh Gaofca7ca32017-01-23 12:05:35 -0800476 StartProcess([]() {
Josh Gao502cfd22017-02-17 01:39:15 -0800477 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
478 err(1, "failed to set PR_SET_KEEPCAPS");
479 }
480
481 if (setresuid(1, 1, 1) != 0) {
482 err(1, "setresuid failed");
483 }
484
485 __user_cap_header_struct capheader;
486 __user_cap_data_struct capdata[2];
487 memset(&capheader, 0, sizeof(capheader));
488 memset(&capdata, 0, sizeof(capdata));
489
490 capheader.version = _LINUX_CAPABILITY_VERSION_3;
491 capheader.pid = 0;
492
493 // Turn on every third capability.
494 static_assert(CAP_LAST_CAP > 33, "CAP_LAST_CAP <= 32");
495 for (int i = 0; i < CAP_LAST_CAP; i += 3) {
496 capdata[CAP_TO_INDEX(i)].permitted |= CAP_TO_MASK(i);
497 capdata[CAP_TO_INDEX(i)].effective |= CAP_TO_MASK(i);
498 }
499
500 // Make sure CAP_SYS_PTRACE is off.
501 capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].permitted &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
502 capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
503
504 if (capset(&capheader, &capdata[0]) != 0) {
505 err(1, "capset failed");
506 }
507
508 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) != 0) {
509 err(1, "failed to drop ambient capabilities");
510 }
511
Josh Gaoa5199a92017-04-03 13:18:34 -0700512 pthread_setname_np(pthread_self(), "thread_name");
Josh Gao502cfd22017-02-17 01:39:15 -0800513 raise(SIGSYS);
Josh Gaofca7ca32017-01-23 12:05:35 -0800514 });
Josh Gao502cfd22017-02-17 01:39:15 -0800515
516 unique_fd output_fd;
517 StartIntercept(&output_fd);
518 FinishCrasher();
519 AssertDeath(SIGSYS);
520
521 std::string result;
522 int intercept_result;
523 FinishIntercept(&intercept_result);
524 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
525 ConsumeFd(std::move(output_fd), &result);
Josh Gaoa5199a92017-04-03 13:18:34 -0700526 ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
Josh Gao502cfd22017-02-17 01:39:15 -0800527 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
Josh Gaofca7ca32017-01-23 12:05:35 -0800528}
Josh Gaoc3c8c022017-02-13 16:36:18 -0800529
530TEST(crash_dump, zombie) {
531 pid_t forkpid = fork();
532
Josh Gaoc3c8c022017-02-13 16:36:18 -0800533 pid_t rc;
534 int status;
535
536 if (forkpid == 0) {
537 errno = 0;
538 rc = waitpid(-1, &status, WNOHANG | __WALL | __WNOTHREAD);
539 if (rc != -1 || errno != ECHILD) {
540 errx(2, "first waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
541 }
542
543 raise(DEBUGGER_SIGNAL);
544
545 errno = 0;
546 rc = waitpid(-1, &status, __WALL | __WNOTHREAD);
547 if (rc != -1 || errno != ECHILD) {
548 errx(2, "second waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
549 }
550 _exit(0);
551 } else {
552 rc = waitpid(forkpid, &status, 0);
553 ASSERT_EQ(forkpid, rc);
554 ASSERT_TRUE(WIFEXITED(status));
555 ASSERT_EQ(0, WEXITSTATUS(status));
556 }
557}
Josh Gao352a8452017-03-30 16:46:21 -0700558
559TEST(tombstoned, no_notify) {
560 // Do this a few times.
561 for (int i = 0; i < 3; ++i) {
562 pid_t pid = 123'456'789 + i;
563
564 unique_fd intercept_fd, output_fd;
565 tombstoned_intercept(pid, &intercept_fd, &output_fd);
566
567 {
568 unique_fd tombstoned_socket, input_fd;
569 ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
570 ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
571 }
572
573 pid_t read_pid;
574 ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
575 ASSERT_EQ(read_pid, pid);
576 }
577}
578
579TEST(tombstoned, stress) {
580 // Spawn threads to simultaneously do a bunch of failing dumps and a bunch of successful dumps.
581 static constexpr int kDumpCount = 100;
582
583 std::atomic<bool> start(false);
584 std::vector<std::thread> threads;
585 threads.emplace_back([&start]() {
586 while (!start) {
587 continue;
588 }
589
590 // Use a way out of range pid, to avoid stomping on an actual process.
591 pid_t pid_base = 1'000'000;
592
593 for (int dump = 0; dump < kDumpCount; ++dump) {
594 pid_t pid = pid_base + dump;
595
596 unique_fd intercept_fd, output_fd;
597 tombstoned_intercept(pid, &intercept_fd, &output_fd);
598
599 // Pretend to crash, and then immediately close the socket.
600 unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
601 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
602 if (sockfd == -1) {
603 FAIL() << "failed to connect to tombstoned: " << strerror(errno);
604 }
605 TombstonedCrashPacket packet = {};
606 packet.packet_type = CrashPacketType::kDumpRequest;
607 packet.packet.dump_request.pid = pid;
608 if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
609 FAIL() << "failed to write to tombstoned: " << strerror(errno);
610 }
611
612 continue;
613 }
614 });
615
616 threads.emplace_back([&start]() {
617 while (!start) {
618 continue;
619 }
620
621 // Use a way out of range pid, to avoid stomping on an actual process.
622 pid_t pid_base = 2'000'000;
623
624 for (int dump = 0; dump < kDumpCount; ++dump) {
625 pid_t pid = pid_base + dump;
626
627 unique_fd intercept_fd, output_fd;
628 tombstoned_intercept(pid, &intercept_fd, &output_fd);
629
630 {
631 unique_fd tombstoned_socket, input_fd;
632 ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
633 ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
634 tombstoned_notify_completion(tombstoned_socket.get());
635 }
636
637 // TODO: Fix the race that requires this sleep.
638 std::this_thread::sleep_for(50ms);
639
640 pid_t read_pid;
641 ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
642 ASSERT_EQ(read_pid, pid);
643 }
644 });
645
646 start = true;
647
648 for (std::thread& thread : threads) {
649 thread.join();
650 }
651}