blob: 1a27f3f6ae10a44bc010d62ccd84f510d4986747 [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>
39#include <debuggerd/util.h>
40#include <gtest/gtest.h>
41
42using namespace std::chrono_literals;
43using android::base::unique_fd;
44
45#if defined(__LP64__)
Josh Gaocbe70cb2016-10-18 18:17:52 -070046#define ARCH_SUFFIX "64"
47#else
Josh Gaocbe70cb2016-10-18 18:17:52 -070048#define ARCH_SUFFIX ""
49#endif
50
51constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
52
53#define TIMEOUT(seconds, expr) \
54 [&]() { \
55 struct sigaction old_sigaction; \
56 struct sigaction new_sigaction = {}; \
57 new_sigaction.sa_handler = [](int) {}; \
58 if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
59 err(1, "sigaction failed"); \
60 } \
61 alarm(seconds); \
62 auto value = expr; \
63 int saved_errno = errno; \
64 if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
65 err(1, "sigaction failed"); \
66 } \
67 alarm(0); \
68 errno = saved_errno; \
69 return value; \
70 }()
71
72#define ASSERT_MATCH(str, pattern) \
73 do { \
74 std::regex r((pattern)); \
75 if (!std::regex_search((str), r)) { \
76 FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
77 } \
78 } while (0)
79
80class CrasherTest : public ::testing::Test {
81 public:
82 pid_t crasher_pid = -1;
83 bool previous_wait_for_gdb;
84 unique_fd crasher_pipe;
85 unique_fd intercept_fd;
86
87 CrasherTest();
88 ~CrasherTest();
89
90 void StartIntercept(unique_fd* output_fd);
91
92 // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
93 void FinishIntercept(int* result);
94
Josh Gaofca7ca32017-01-23 12:05:35 -080095 void StartProcess(std::function<void()> function);
Josh Gaocbe70cb2016-10-18 18:17:52 -070096 void StartCrasher(const std::string& crash_type);
97 void FinishCrasher();
98 void AssertDeath(int signo);
99};
100
101CrasherTest::CrasherTest() {
102 previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
103 android::base::SetProperty(kWaitForGdbKey, "0");
104}
105
106CrasherTest::~CrasherTest() {
107 if (crasher_pid != -1) {
108 kill(crasher_pid, SIGKILL);
109 int status;
110 waitpid(crasher_pid, &status, WUNTRACED);
111 }
112
113 android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
114}
115
116void CrasherTest::StartIntercept(unique_fd* output_fd) {
117 if (crasher_pid == -1) {
118 FAIL() << "crasher hasn't been started";
119 }
120
121 intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
122 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
123 if (intercept_fd == -1) {
124 FAIL() << "failed to contact tombstoned: " << strerror(errno);
125 }
126
127 InterceptRequest req = {.pid = crasher_pid };
128
129 unique_fd output_pipe_write;
130 if (!Pipe(output_fd, &output_pipe_write)) {
131 FAIL() << "failed to create output pipe: " << strerror(errno);
132 }
133
134 std::string pipe_size_str;
135 int pipe_buffer_size;
136 if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
137 FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
138 }
139
140 pipe_size_str = android::base::Trim(pipe_size_str);
141
142 if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
143 FAIL() << "failed to parse pipe max size";
144 }
145
146 if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
147 FAIL() << "failed to set pipe size: " << strerror(errno);
148 }
149
150 if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
151 FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
152 }
153}
154
155void CrasherTest::FinishIntercept(int* result) {
156 InterceptResponse response;
157
158 // Timeout for tombstoned intercept is 10 seconds.
159 ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
160 if (rc == -1) {
161 FAIL() << "failed to read response from tombstoned: " << strerror(errno);
162 } else if (rc == 0) {
163 *result = -1;
164 } else if (rc != sizeof(response)) {
165 FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
166 << ", received " << rc;
167 } else {
168 *result = response.success;
169 }
170}
171
Josh Gaofca7ca32017-01-23 12:05:35 -0800172void CrasherTest::StartProcess(std::function<void()> function) {
173 unique_fd read_pipe;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700174 unique_fd crasher_read_pipe;
175 if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
176 FAIL() << "failed to create pipe: " << strerror(errno);
177 }
178
179 crasher_pid = fork();
180 if (crasher_pid == -1) {
181 FAIL() << "fork failed: " << strerror(errno);
182 } else if (crasher_pid == 0) {
Josh Gao502cfd22017-02-17 01:39:15 -0800183 char dummy;
184 crasher_pipe.reset();
185 TEMP_FAILURE_RETRY(read(crasher_read_pipe.get(), &dummy, 1));
Josh Gaofca7ca32017-01-23 12:05:35 -0800186 function();
187 _exit(0);
188 }
189}
190
Josh Gaocbe70cb2016-10-18 18:17:52 -0700191void CrasherTest::FinishCrasher() {
192 if (crasher_pipe == -1) {
193 FAIL() << "crasher pipe uninitialized";
194 }
195
196 ssize_t rc = write(crasher_pipe.get(), "\n", 1);
197 if (rc == -1) {
198 FAIL() << "failed to write to crasher pipe: " << strerror(errno);
199 } else if (rc == 0) {
200 FAIL() << "crasher pipe was closed";
201 }
202}
203
204void CrasherTest::AssertDeath(int signo) {
205 int status;
206 pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
207 if (pid != crasher_pid) {
208 FAIL() << "failed to wait for crasher: " << strerror(errno);
209 }
210
Josh Gao7a0ee642017-02-07 13:31:25 -0800211 if (WIFEXITED(status)) {
212 FAIL() << "crasher failed to exec: " << strerror(WEXITSTATUS(status));
213 } else if (!WIFSIGNALED(status)) {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700214 FAIL() << "crasher didn't terminate via a signal";
215 }
216 ASSERT_EQ(signo, WTERMSIG(status));
217 crasher_pid = -1;
218}
219
220static void ConsumeFd(unique_fd fd, std::string* output) {
221 constexpr size_t read_length = PAGE_SIZE;
222 std::string result;
223
224 while (true) {
225 size_t offset = result.size();
226 result.resize(result.size() + PAGE_SIZE);
227 ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
228 if (rc == -1) {
229 FAIL() << "read failed: " << strerror(errno);
230 } else if (rc == 0) {
231 result.resize(result.size() - PAGE_SIZE);
232 break;
233 }
234
235 result.resize(result.size() - PAGE_SIZE + rc);
236 }
237
238 *output = std::move(result);
239}
240
241TEST_F(CrasherTest, smoke) {
242 int intercept_result;
243 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800244 StartProcess([]() {
245 *reinterpret_cast<volatile char*>(0xdead) = '1';
246 });
247
Josh Gaocbe70cb2016-10-18 18:17:52 -0700248 StartIntercept(&output_fd);
249 FinishCrasher();
250 AssertDeath(SIGSEGV);
251 FinishIntercept(&intercept_result);
252
253 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
254
255 std::string result;
256 ConsumeFd(std::move(output_fd), &result);
257 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
258}
259
260TEST_F(CrasherTest, abort) {
261 int intercept_result;
262 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800263 StartProcess([]() {
264 abort();
265 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700266 StartIntercept(&output_fd);
267 FinishCrasher();
268 AssertDeath(SIGABRT);
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"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
276}
277
278TEST_F(CrasherTest, signal) {
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
286 // Wait for a bit, or we might end up killing the process before the signal
287 // handler even gets a chance to be registered.
288 std::this_thread::sleep_for(100ms);
289 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
290
291 AssertDeath(SIGSEGV);
292 FinishIntercept(&intercept_result);
293
294 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
295
296 std::string result;
297 ConsumeFd(std::move(output_fd), &result);
298 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
299 ASSERT_MATCH(result, R"(backtrace:)");
300}
301
302TEST_F(CrasherTest, abort_message) {
303 int intercept_result;
304 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800305 StartProcess([]() {
306 android_set_abort_message("abort message goes here");
307 abort();
308 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700309 StartIntercept(&output_fd);
310 FinishCrasher();
311 AssertDeath(SIGABRT);
312 FinishIntercept(&intercept_result);
313
314 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
315
316 std::string result;
317 ConsumeFd(std::move(output_fd), &result);
Josh Gao502cfd22017-02-17 01:39:15 -0800318 ASSERT_MATCH(result, R"(Abort message: 'abort message goes here')");
Josh Gaocbe70cb2016-10-18 18:17:52 -0700319}
320
321TEST_F(CrasherTest, intercept_timeout) {
322 int intercept_result;
323 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800324 StartProcess([]() {
325 abort();
326 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700327 StartIntercept(&output_fd);
328
329 // Don't let crasher finish until we timeout.
330 FinishIntercept(&intercept_result);
331
332 ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
333 << intercept_result << ")";
334
335 FinishCrasher();
336 AssertDeath(SIGABRT);
337}
338
339TEST_F(CrasherTest, wait_for_gdb) {
340 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
341 FAIL() << "failed to enable wait_for_gdb";
342 }
343 sleep(1);
344
Josh Gao502cfd22017-02-17 01:39:15 -0800345 StartProcess([]() {
346 abort();
347 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700348 FinishCrasher();
349
350 int status;
351 ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
352 ASSERT_TRUE(WIFSTOPPED(status));
353 ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
354
355 ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
356
357 AssertDeath(SIGABRT);
358}
359
Josh Gao7c6e3132017-01-22 17:59:02 -0800360// wait_for_gdb shouldn't trigger on manually sent signals.
Josh Gaocbe70cb2016-10-18 18:17:52 -0700361TEST_F(CrasherTest, wait_for_gdb_signal) {
362 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
363 FAIL() << "failed to enable wait_for_gdb";
364 }
365
Josh Gao502cfd22017-02-17 01:39:15 -0800366 StartProcess([]() {
367 abort();
368 });
Josh Gao7c6e3132017-01-22 17:59:02 -0800369 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
370 AssertDeath(SIGSEGV);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700371}
372
373TEST_F(CrasherTest, backtrace) {
374 std::string result;
375 int intercept_result;
376 unique_fd output_fd;
Josh Gao502cfd22017-02-17 01:39:15 -0800377
378 StartProcess([]() {
379 abort();
380 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700381 StartIntercept(&output_fd);
382
383 std::this_thread::sleep_for(500ms);
384
385 sigval val;
386 val.sival_int = 1;
387 ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
388 FinishIntercept(&intercept_result);
389 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
390 ConsumeFd(std::move(output_fd), &result);
391 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
392
393 int status;
394 ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
395
396 StartIntercept(&output_fd);
397 FinishCrasher();
398 AssertDeath(SIGABRT);
399 FinishIntercept(&intercept_result);
400 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
401 ConsumeFd(std::move(output_fd), &result);
402 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
403}
Josh Gaofca7ca32017-01-23 12:05:35 -0800404
405TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
Josh Gao502cfd22017-02-17 01:39:15 -0800406 int intercept_result;
407 unique_fd output_fd;
Josh Gaofca7ca32017-01-23 12:05:35 -0800408 StartProcess([]() {
409 prctl(PR_SET_DUMPABLE, 0);
Josh Gao502cfd22017-02-17 01:39:15 -0800410 abort();
Josh Gaofca7ca32017-01-23 12:05:35 -0800411 });
Josh Gao502cfd22017-02-17 01:39:15 -0800412
413 StartIntercept(&output_fd);
414 FinishCrasher();
415 AssertDeath(SIGABRT);
416 FinishIntercept(&intercept_result);
417
418 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
419
420 std::string result;
421 ConsumeFd(std::move(output_fd), &result);
422 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 -0800423}
424
Josh Gao502cfd22017-02-17 01:39:15 -0800425TEST_F(CrasherTest, capabilities) {
426 ASSERT_EQ(0U, getuid()) << "capability test requires root";
427
Josh Gaofca7ca32017-01-23 12:05:35 -0800428 StartProcess([]() {
Josh Gao502cfd22017-02-17 01:39:15 -0800429 if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {
430 err(1, "failed to set PR_SET_KEEPCAPS");
431 }
432
433 if (setresuid(1, 1, 1) != 0) {
434 err(1, "setresuid failed");
435 }
436
437 __user_cap_header_struct capheader;
438 __user_cap_data_struct capdata[2];
439 memset(&capheader, 0, sizeof(capheader));
440 memset(&capdata, 0, sizeof(capdata));
441
442 capheader.version = _LINUX_CAPABILITY_VERSION_3;
443 capheader.pid = 0;
444
445 // Turn on every third capability.
446 static_assert(CAP_LAST_CAP > 33, "CAP_LAST_CAP <= 32");
447 for (int i = 0; i < CAP_LAST_CAP; i += 3) {
448 capdata[CAP_TO_INDEX(i)].permitted |= CAP_TO_MASK(i);
449 capdata[CAP_TO_INDEX(i)].effective |= CAP_TO_MASK(i);
450 }
451
452 // Make sure CAP_SYS_PTRACE is off.
453 capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].permitted &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
454 capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));
455
456 if (capset(&capheader, &capdata[0]) != 0) {
457 err(1, "capset failed");
458 }
459
460 if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) != 0) {
461 err(1, "failed to drop ambient capabilities");
462 }
463
464 raise(SIGSYS);
Josh Gaofca7ca32017-01-23 12:05:35 -0800465 });
Josh Gao502cfd22017-02-17 01:39:15 -0800466
467 unique_fd output_fd;
468 StartIntercept(&output_fd);
469 FinishCrasher();
470 AssertDeath(SIGSYS);
471
472 std::string result;
473 int intercept_result;
474 FinishIntercept(&intercept_result);
475 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
476 ConsumeFd(std::move(output_fd), &result);
477 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 -0800478}
Josh Gaoc3c8c022017-02-13 16:36:18 -0800479
480TEST(crash_dump, zombie) {
481 pid_t forkpid = fork();
482
483 int pipefd[2];
484 ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
485
486 pid_t rc;
487 int status;
488
489 if (forkpid == 0) {
490 errno = 0;
491 rc = waitpid(-1, &status, WNOHANG | __WALL | __WNOTHREAD);
492 if (rc != -1 || errno != ECHILD) {
493 errx(2, "first waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
494 }
495
496 raise(DEBUGGER_SIGNAL);
497
498 errno = 0;
499 rc = waitpid(-1, &status, __WALL | __WNOTHREAD);
500 if (rc != -1 || errno != ECHILD) {
501 errx(2, "second waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
502 }
503 _exit(0);
504 } else {
505 rc = waitpid(forkpid, &status, 0);
506 ASSERT_EQ(forkpid, rc);
507 ASSERT_TRUE(WIFEXITED(status));
508 ASSERT_EQ(0, WEXITSTATUS(status));
509 }
510}