blob: fa2838ee043294064eff01bb9f1748d3956c180b [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 Gao460b3362017-03-30 16:40:47 -070081static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
82 intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
83 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
84 if (intercept_fd->get() == -1) {
85 FAIL() << "failed to contact tombstoned: " << strerror(errno);
86 }
87
88 InterceptRequest req = {.pid = target_pid};
89
90 unique_fd output_pipe_write;
91 if (!Pipe(output_fd, &output_pipe_write)) {
92 FAIL() << "failed to create output pipe: " << strerror(errno);
93 }
94
95 std::string pipe_size_str;
96 int pipe_buffer_size;
97 if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
98 FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
99 }
100
101 pipe_size_str = android::base::Trim(pipe_size_str);
102
103 if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
104 FAIL() << "failed to parse pipe max size";
105 }
106
107 if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
108 FAIL() << "failed to set pipe size: " << strerror(errno);
109 }
110
111 if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
112 FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
113 }
114
115 InterceptResponse response;
116 ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
117 if (rc == -1) {
118 FAIL() << "failed to read response from tombstoned: " << strerror(errno);
119 } else if (rc == 0) {
120 FAIL() << "failed to read response from tombstoned (EOF)";
121 } else if (rc != sizeof(response)) {
122 FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
123 << ", received " << rc;
124 }
125
126 ASSERT_EQ(InterceptStatus::kRegistered, response.status);
127}
128
Josh Gaocbe70cb2016-10-18 18:17:52 -0700129class CrasherTest : public ::testing::Test {
130 public:
131 pid_t crasher_pid = -1;
132 bool previous_wait_for_gdb;
133 unique_fd crasher_pipe;
134 unique_fd intercept_fd;
135
136 CrasherTest();
137 ~CrasherTest();
138
139 void StartIntercept(unique_fd* output_fd);
140
141 // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
142 void FinishIntercept(int* result);
143
Josh Gaofca7ca32017-01-23 12:05:35 -0800144 void StartProcess(std::function<void()> function);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700145 void StartCrasher(const std::string& crash_type);
146 void FinishCrasher();
147 void AssertDeath(int signo);
148};
149
150CrasherTest::CrasherTest() {
151 previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
152 android::base::SetProperty(kWaitForGdbKey, "0");
153}
154
155CrasherTest::~CrasherTest() {
156 if (crasher_pid != -1) {
157 kill(crasher_pid, SIGKILL);
158 int status;
159 waitpid(crasher_pid, &status, WUNTRACED);
160 }
161
162 android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
163}
164
165void CrasherTest::StartIntercept(unique_fd* output_fd) {
166 if (crasher_pid == -1) {
167 FAIL() << "crasher hasn't been started";
168 }
169
Josh Gao460b3362017-03-30 16:40:47 -0700170 tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700171}
172
173void CrasherTest::FinishIntercept(int* result) {
174 InterceptResponse response;
175
176 // Timeout for tombstoned intercept is 10 seconds.
177 ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
178 if (rc == -1) {
179 FAIL() << "failed to read response from tombstoned: " << strerror(errno);
180 } else if (rc == 0) {
181 *result = -1;
182 } else if (rc != sizeof(response)) {
183 FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
184 << ", received " << rc;
185 } else {
Josh Gao460b3362017-03-30 16:40:47 -0700186 *result = response.status == InterceptStatus::kStarted ? 1 : 0;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700187 }
188}
189
Josh Gaofca7ca32017-01-23 12:05:35 -0800190void CrasherTest::StartProcess(std::function<void()> function) {
191 unique_fd read_pipe;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700192 unique_fd crasher_read_pipe;
193 if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
194 FAIL() << "failed to create pipe: " << strerror(errno);
195 }
196
197 crasher_pid = fork();
198 if (crasher_pid == -1) {
199 FAIL() << "fork failed: " << strerror(errno);
200 } else if (crasher_pid == 0) {
Josh Gao502cfd22017-02-17 01:39:15 -0800201 char dummy;
202 crasher_pipe.reset();
203 TEMP_FAILURE_RETRY(read(crasher_read_pipe.get(), &dummy, 1));
Josh Gaofca7ca32017-01-23 12:05:35 -0800204 function();
205 _exit(0);
206 }
207}
208
Josh Gaocbe70cb2016-10-18 18:17:52 -0700209void CrasherTest::FinishCrasher() {
210 if (crasher_pipe == -1) {
211 FAIL() << "crasher pipe uninitialized";
212 }
213
214 ssize_t rc = write(crasher_pipe.get(), "\n", 1);
215 if (rc == -1) {
216 FAIL() << "failed to write to crasher pipe: " << strerror(errno);
217 } else if (rc == 0) {
218 FAIL() << "crasher pipe was closed";
219 }
220}
221
222void CrasherTest::AssertDeath(int signo) {
223 int status;
224 pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
225 if (pid != crasher_pid) {
226 FAIL() << "failed to wait for crasher: " << strerror(errno);
227 }
228
Josh Gao7a0ee642017-02-07 13:31:25 -0800229 if (WIFEXITED(status)) {
230 FAIL() << "crasher failed to exec: " << strerror(WEXITSTATUS(status));
231 } else if (!WIFSIGNALED(status)) {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700232 FAIL() << "crasher didn't terminate via a signal";
233 }
234 ASSERT_EQ(signo, WTERMSIG(status));
235 crasher_pid = -1;
236}
237
238static void ConsumeFd(unique_fd fd, std::string* output) {
239 constexpr size_t read_length = PAGE_SIZE;
240 std::string result;
241
242 while (true) {
243 size_t offset = result.size();
244 result.resize(result.size() + PAGE_SIZE);
245 ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
246 if (rc == -1) {
247 FAIL() << "read failed: " << strerror(errno);
248 } else if (rc == 0) {
249 result.resize(result.size() - PAGE_SIZE);
250 break;
251 }
252
253 result.resize(result.size() - PAGE_SIZE + rc);
254 }
255
256 *output = std::move(result);
257}
258
259TEST_F(CrasherTest, smoke) {
260 int intercept_result;
261 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800262 StartProcess([]() {
263 *reinterpret_cast<volatile char*>(0xdead) = '1';
264 });
265
Josh Gaocbe70cb2016-10-18 18:17:52 -0700266 StartIntercept(&output_fd);
267 FinishCrasher();
268 AssertDeath(SIGSEGV);
269 FinishIntercept(&intercept_result);
270
271 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
272
273 std::string result;
274 ConsumeFd(std::move(output_fd), &result);
275 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
276}
277
278TEST_F(CrasherTest, abort) {
279 int intercept_result;
280 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800281 StartProcess([]() {
282 abort();
283 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700284 StartIntercept(&output_fd);
285 FinishCrasher();
286 AssertDeath(SIGABRT);
287 FinishIntercept(&intercept_result);
288
289 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
290
291 std::string result;
292 ConsumeFd(std::move(output_fd), &result);
293 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
294}
295
296TEST_F(CrasherTest, signal) {
297 int intercept_result;
298 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800299 StartProcess([]() {
300 abort();
301 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700302 StartIntercept(&output_fd);
303
304 // Wait for a bit, or we might end up killing the process before the signal
305 // handler even gets a chance to be registered.
306 std::this_thread::sleep_for(100ms);
307 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
308
309 AssertDeath(SIGSEGV);
310 FinishIntercept(&intercept_result);
311
312 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
313
314 std::string result;
315 ConsumeFd(std::move(output_fd), &result);
316 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
317 ASSERT_MATCH(result, R"(backtrace:)");
318}
319
320TEST_F(CrasherTest, abort_message) {
321 int intercept_result;
322 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800323 StartProcess([]() {
324 android_set_abort_message("abort message goes here");
325 abort();
326 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700327 StartIntercept(&output_fd);
328 FinishCrasher();
329 AssertDeath(SIGABRT);
330 FinishIntercept(&intercept_result);
331
332 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
333
334 std::string result;
335 ConsumeFd(std::move(output_fd), &result);
Josh Gao502cfd22017-02-17 01:39:15 -0800336 ASSERT_MATCH(result, R"(Abort message: 'abort message goes here')");
Josh Gaocbe70cb2016-10-18 18:17:52 -0700337}
338
339TEST_F(CrasherTest, intercept_timeout) {
340 int intercept_result;
341 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800342 StartProcess([]() {
343 abort();
344 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700345 StartIntercept(&output_fd);
346
347 // Don't let crasher finish until we timeout.
348 FinishIntercept(&intercept_result);
349
350 ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
351 << intercept_result << ")";
352
353 FinishCrasher();
354 AssertDeath(SIGABRT);
355}
356
357TEST_F(CrasherTest, wait_for_gdb) {
358 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
359 FAIL() << "failed to enable wait_for_gdb";
360 }
361 sleep(1);
362
Josh Gao502cfd22017-02-17 01:39:15 -0800363 StartProcess([]() {
364 abort();
365 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700366 FinishCrasher();
367
368 int status;
369 ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
370 ASSERT_TRUE(WIFSTOPPED(status));
371 ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
372
373 ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
374
375 AssertDeath(SIGABRT);
376}
377
Josh Gao7c6e3132017-01-22 17:59:02 -0800378// wait_for_gdb shouldn't trigger on manually sent signals.
Josh Gaocbe70cb2016-10-18 18:17:52 -0700379TEST_F(CrasherTest, wait_for_gdb_signal) {
380 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
381 FAIL() << "failed to enable wait_for_gdb";
382 }
383
Josh Gao502cfd22017-02-17 01:39:15 -0800384 StartProcess([]() {
385 abort();
386 });
Josh Gao7c6e3132017-01-22 17:59:02 -0800387 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
388 AssertDeath(SIGSEGV);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700389}
390
391TEST_F(CrasherTest, backtrace) {
392 std::string result;
393 int intercept_result;
394 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800395
396 StartProcess([]() {
397 abort();
398 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700399 StartIntercept(&output_fd);
400
401 std::this_thread::sleep_for(500ms);
402
403 sigval val;
404 val.sival_int = 1;
405 ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
406 FinishIntercept(&intercept_result);
407 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
408 ConsumeFd(std::move(output_fd), &result);
409 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
410
411 int status;
412 ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
413
414 StartIntercept(&output_fd);
415 FinishCrasher();
416 AssertDeath(SIGABRT);
417 FinishIntercept(&intercept_result);
418 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
419 ConsumeFd(std::move(output_fd), &result);
420 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
421}
Josh Gaofca7ca32017-01-23 12:05:35 -0800422
423TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
Josh Gao502cfd22017-02-17 01:39:15 -0800424 int intercept_result;
425 unique_fd output_fd;
Josh Gaofca7ca32017-01-23 12:05:35 -0800426 StartProcess([]() {
427 prctl(PR_SET_DUMPABLE, 0);
Josh Gao502cfd22017-02-17 01:39:15 -0800428 abort();
Josh Gaofca7ca32017-01-23 12:05:35 -0800429 });
Josh Gao502cfd22017-02-17 01:39:15 -0800430
431 StartIntercept(&output_fd);
432 FinishCrasher();
433 AssertDeath(SIGABRT);
434 FinishIntercept(&intercept_result);
435
436 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
437
438 std::string result;
439 ConsumeFd(std::move(output_fd), &result);
440 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 -0800441}
442
Josh Gao502cfd22017-02-17 01:39:15 -0800443TEST_F(CrasherTest, capabilities) {
444 ASSERT_EQ(0U, getuid()) << "capability test requires root";
445
Josh Gaofca7ca32017-01-23 12:05:35 -0800446 StartProcess([]() {
Josh Gao502cfd22017-02-17 01:39:15 -0800447 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
448 err(1, "failed to set PR_SET_KEEPCAPS");
449 }
450
451 if (setresuid(1, 1, 1) != 0) {
452 err(1, "setresuid failed");
453 }
454
455 __user_cap_header_struct capheader;
456 __user_cap_data_struct capdata[2];
457 memset(&capheader, 0, sizeof(capheader));
458 memset(&capdata, 0, sizeof(capdata));
459
460 capheader.version = _LINUX_CAPABILITY_VERSION_3;
461 capheader.pid = 0;
462
463 // Turn on every third capability.
464 static_assert(CAP_LAST_CAP > 33, "CAP_LAST_CAP <= 32");
465 for (int i = 0; i < CAP_LAST_CAP; i += 3) {
466 capdata[CAP_TO_INDEX(i)].permitted |= CAP_TO_MASK(i);
467 capdata[CAP_TO_INDEX(i)].effective |= CAP_TO_MASK(i);
468 }
469
470 // Make sure CAP_SYS_PTRACE is off.
471 capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].permitted &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
472 capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
473
474 if (capset(&capheader, &capdata[0]) != 0) {
475 err(1, "capset failed");
476 }
477
478 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) != 0) {
479 err(1, "failed to drop ambient capabilities");
480 }
481
Josh Gaoa5199a92017-04-03 13:18:34 -0700482 pthread_setname_np(pthread_self(), "thread_name");
Josh Gao502cfd22017-02-17 01:39:15 -0800483 raise(SIGSYS);
Josh Gaofca7ca32017-01-23 12:05:35 -0800484 });
Josh Gao502cfd22017-02-17 01:39:15 -0800485
486 unique_fd output_fd;
487 StartIntercept(&output_fd);
488 FinishCrasher();
489 AssertDeath(SIGSYS);
490
491 std::string result;
492 int intercept_result;
493 FinishIntercept(&intercept_result);
494 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
495 ConsumeFd(std::move(output_fd), &result);
Josh Gaoa5199a92017-04-03 13:18:34 -0700496 ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
Josh Gao502cfd22017-02-17 01:39:15 -0800497 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 -0800498}
Josh Gaoc3c8c022017-02-13 16:36:18 -0800499
500TEST(crash_dump, zombie) {
501 pid_t forkpid = fork();
502
503 int pipefd[2];
504 ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
505
506 pid_t rc;
507 int status;
508
509 if (forkpid == 0) {
510 errno = 0;
511 rc = waitpid(-1, &status, WNOHANG | __WALL | __WNOTHREAD);
512 if (rc != -1 || errno != ECHILD) {
513 errx(2, "first waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
514 }
515
516 raise(DEBUGGER_SIGNAL);
517
518 errno = 0;
519 rc = waitpid(-1, &status, __WALL | __WNOTHREAD);
520 if (rc != -1 || errno != ECHILD) {
521 errx(2, "second waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
522 }
523 _exit(0);
524 } else {
525 rc = waitpid(forkpid, &status, 0);
526 ASSERT_EQ(forkpid, rc);
527 ASSERT_TRUE(WIFEXITED(status));
528 ASSERT_EQ(0, WEXITSTATUS(status));
529 }
530}
Josh Gao352a8452017-03-30 16:46:21 -0700531
532TEST(tombstoned, no_notify) {
533 // Do this a few times.
534 for (int i = 0; i < 3; ++i) {
535 pid_t pid = 123'456'789 + i;
536
537 unique_fd intercept_fd, output_fd;
538 tombstoned_intercept(pid, &intercept_fd, &output_fd);
539
540 {
541 unique_fd tombstoned_socket, input_fd;
542 ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
543 ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
544 }
545
546 pid_t read_pid;
547 ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
548 ASSERT_EQ(read_pid, pid);
549 }
550}
551
552TEST(tombstoned, stress) {
553 // Spawn threads to simultaneously do a bunch of failing dumps and a bunch of successful dumps.
554 static constexpr int kDumpCount = 100;
555
556 std::atomic<bool> start(false);
557 std::vector<std::thread> threads;
558 threads.emplace_back([&start]() {
559 while (!start) {
560 continue;
561 }
562
563 // Use a way out of range pid, to avoid stomping on an actual process.
564 pid_t pid_base = 1'000'000;
565
566 for (int dump = 0; dump < kDumpCount; ++dump) {
567 pid_t pid = pid_base + dump;
568
569 unique_fd intercept_fd, output_fd;
570 tombstoned_intercept(pid, &intercept_fd, &output_fd);
571
572 // Pretend to crash, and then immediately close the socket.
573 unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
574 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
575 if (sockfd == -1) {
576 FAIL() << "failed to connect to tombstoned: " << strerror(errno);
577 }
578 TombstonedCrashPacket packet = {};
579 packet.packet_type = CrashPacketType::kDumpRequest;
580 packet.packet.dump_request.pid = pid;
581 if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
582 FAIL() << "failed to write to tombstoned: " << strerror(errno);
583 }
584
585 continue;
586 }
587 });
588
589 threads.emplace_back([&start]() {
590 while (!start) {
591 continue;
592 }
593
594 // Use a way out of range pid, to avoid stomping on an actual process.
595 pid_t pid_base = 2'000'000;
596
597 for (int dump = 0; dump < kDumpCount; ++dump) {
598 pid_t pid = pid_base + dump;
599
600 unique_fd intercept_fd, output_fd;
601 tombstoned_intercept(pid, &intercept_fd, &output_fd);
602
603 {
604 unique_fd tombstoned_socket, input_fd;
605 ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
606 ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
607 tombstoned_notify_completion(tombstoned_socket.get());
608 }
609
610 // TODO: Fix the race that requires this sleep.
611 std::this_thread::sleep_for(50ms);
612
613 pid_t read_pid;
614 ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
615 ASSERT_EQ(read_pid, pid);
616 }
617 });
618
619 start = true;
620
621 for (std::thread& thread : threads) {
622 thread.join();
623 }
624}