blob: e22d6a9d33f4478f4744bed672f569449ade4c33 [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 Gaofca7ca32017-01-23 12:05:35 -080020#include <sys/prctl.h>
Josh Gaocbe70cb2016-10-18 18:17:52 -070021#include <sys/types.h>
22
23#include <chrono>
24#include <regex>
25#include <thread>
26
27#include <android-base/file.h>
28#include <android-base/logging.h>
29#include <android-base/parseint.h>
30#include <android-base/properties.h>
31#include <android-base/strings.h>
32#include <android-base/unique_fd.h>
33#include <cutils/sockets.h>
34#include <debuggerd/handler.h>
35#include <debuggerd/protocol.h>
36#include <debuggerd/util.h>
37#include <gtest/gtest.h>
38
39using namespace std::chrono_literals;
40using android::base::unique_fd;
41
42#if defined(__LP64__)
Josh Gaoa7d7eb62017-02-07 13:13:48 -080043#define CRASHER_PATH "/system/bin/crasher64"
Josh Gaocbe70cb2016-10-18 18:17:52 -070044#define ARCH_SUFFIX "64"
45#else
Josh Gaoa7d7eb62017-02-07 13:13:48 -080046#define CRASHER_PATH "/system/bin/crasher"
Josh Gaocbe70cb2016-10-18 18:17:52 -070047#define ARCH_SUFFIX ""
48#endif
49
50constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
51
52#define TIMEOUT(seconds, expr) \
53 [&]() { \
54 struct sigaction old_sigaction; \
55 struct sigaction new_sigaction = {}; \
56 new_sigaction.sa_handler = [](int) {}; \
57 if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
58 err(1, "sigaction failed"); \
59 } \
60 alarm(seconds); \
61 auto value = expr; \
62 int saved_errno = errno; \
63 if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
64 err(1, "sigaction failed"); \
65 } \
66 alarm(0); \
67 errno = saved_errno; \
68 return value; \
69 }()
70
71#define ASSERT_MATCH(str, pattern) \
72 do { \
73 std::regex r((pattern)); \
74 if (!std::regex_search((str), r)) { \
75 FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
76 } \
77 } while (0)
78
79class CrasherTest : public ::testing::Test {
80 public:
81 pid_t crasher_pid = -1;
82 bool previous_wait_for_gdb;
83 unique_fd crasher_pipe;
84 unique_fd intercept_fd;
85
86 CrasherTest();
87 ~CrasherTest();
88
89 void StartIntercept(unique_fd* output_fd);
90
91 // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
92 void FinishIntercept(int* result);
93
Josh Gaofca7ca32017-01-23 12:05:35 -080094 void StartProcess(std::function<void()> function);
Josh Gaocbe70cb2016-10-18 18:17:52 -070095 void StartCrasher(const std::string& crash_type);
96 void FinishCrasher();
97 void AssertDeath(int signo);
98};
99
100CrasherTest::CrasherTest() {
101 previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
102 android::base::SetProperty(kWaitForGdbKey, "0");
103}
104
105CrasherTest::~CrasherTest() {
106 if (crasher_pid != -1) {
107 kill(crasher_pid, SIGKILL);
108 int status;
109 waitpid(crasher_pid, &status, WUNTRACED);
110 }
111
112 android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
113}
114
115void CrasherTest::StartIntercept(unique_fd* output_fd) {
116 if (crasher_pid == -1) {
117 FAIL() << "crasher hasn't been started";
118 }
119
120 intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
121 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
122 if (intercept_fd == -1) {
123 FAIL() << "failed to contact tombstoned: " << strerror(errno);
124 }
125
126 InterceptRequest req = {.pid = crasher_pid };
127
128 unique_fd output_pipe_write;
129 if (!Pipe(output_fd, &output_pipe_write)) {
130 FAIL() << "failed to create output pipe: " << strerror(errno);
131 }
132
133 std::string pipe_size_str;
134 int pipe_buffer_size;
135 if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
136 FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
137 }
138
139 pipe_size_str = android::base::Trim(pipe_size_str);
140
141 if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
142 FAIL() << "failed to parse pipe max size";
143 }
144
145 if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
146 FAIL() << "failed to set pipe size: " << strerror(errno);
147 }
148
149 if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
150 FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
151 }
152}
153
154void CrasherTest::FinishIntercept(int* result) {
155 InterceptResponse response;
156
157 // Timeout for tombstoned intercept is 10 seconds.
158 ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
159 if (rc == -1) {
160 FAIL() << "failed to read response from tombstoned: " << strerror(errno);
161 } else if (rc == 0) {
162 *result = -1;
163 } else if (rc != sizeof(response)) {
164 FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
165 << ", received " << rc;
166 } else {
167 *result = response.success;
168 }
169}
170
Josh Gaofca7ca32017-01-23 12:05:35 -0800171void CrasherTest::StartProcess(std::function<void()> function) {
172 unique_fd read_pipe;
Josh Gaocbe70cb2016-10-18 18:17:52 -0700173 unique_fd crasher_read_pipe;
174 if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
175 FAIL() << "failed to create pipe: " << strerror(errno);
176 }
177
178 crasher_pid = fork();
179 if (crasher_pid == -1) {
180 FAIL() << "fork failed: " << strerror(errno);
181 } else if (crasher_pid == 0) {
182 unique_fd devnull(open("/dev/null", O_WRONLY));
183 dup2(crasher_read_pipe.get(), STDIN_FILENO);
184 dup2(devnull.get(), STDOUT_FILENO);
185 dup2(devnull.get(), STDERR_FILENO);
Josh Gaofca7ca32017-01-23 12:05:35 -0800186 function();
187 _exit(0);
188 }
189}
190
191void CrasherTest::StartCrasher(const std::string& crash_type) {
192 std::string type = "wait-" + crash_type;
193 StartProcess([type]() {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700194 execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
Josh Gao7a0ee642017-02-07 13:31:25 -0800195 exit(errno);
Josh Gaofca7ca32017-01-23 12:05:35 -0800196 });
Josh Gaocbe70cb2016-10-18 18:17:52 -0700197}
198
199void CrasherTest::FinishCrasher() {
200 if (crasher_pipe == -1) {
201 FAIL() << "crasher pipe uninitialized";
202 }
203
204 ssize_t rc = write(crasher_pipe.get(), "\n", 1);
205 if (rc == -1) {
206 FAIL() << "failed to write to crasher pipe: " << strerror(errno);
207 } else if (rc == 0) {
208 FAIL() << "crasher pipe was closed";
209 }
210}
211
212void CrasherTest::AssertDeath(int signo) {
213 int status;
214 pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
215 if (pid != crasher_pid) {
216 FAIL() << "failed to wait for crasher: " << strerror(errno);
217 }
218
Josh Gao7a0ee642017-02-07 13:31:25 -0800219 if (WIFEXITED(status)) {
220 FAIL() << "crasher failed to exec: " << strerror(WEXITSTATUS(status));
221 } else if (!WIFSIGNALED(status)) {
Josh Gaocbe70cb2016-10-18 18:17:52 -0700222 FAIL() << "crasher didn't terminate via a signal";
223 }
224 ASSERT_EQ(signo, WTERMSIG(status));
225 crasher_pid = -1;
226}
227
228static void ConsumeFd(unique_fd fd, std::string* output) {
229 constexpr size_t read_length = PAGE_SIZE;
230 std::string result;
231
232 while (true) {
233 size_t offset = result.size();
234 result.resize(result.size() + PAGE_SIZE);
235 ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
236 if (rc == -1) {
237 FAIL() << "read failed: " << strerror(errno);
238 } else if (rc == 0) {
239 result.resize(result.size() - PAGE_SIZE);
240 break;
241 }
242
243 result.resize(result.size() - PAGE_SIZE + rc);
244 }
245
246 *output = std::move(result);
247}
248
249TEST_F(CrasherTest, smoke) {
250 int intercept_result;
251 unique_fd output_fd;
252 StartCrasher("SIGSEGV");
253 StartIntercept(&output_fd);
254 FinishCrasher();
255 AssertDeath(SIGSEGV);
256 FinishIntercept(&intercept_result);
257
258 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
259
260 std::string result;
261 ConsumeFd(std::move(output_fd), &result);
262 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
263}
264
265TEST_F(CrasherTest, abort) {
266 int intercept_result;
267 unique_fd output_fd;
268 StartCrasher("abort");
269 StartIntercept(&output_fd);
270 FinishCrasher();
271 AssertDeath(SIGABRT);
272 FinishIntercept(&intercept_result);
273
274 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
275
276 std::string result;
277 ConsumeFd(std::move(output_fd), &result);
278 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
279}
280
281TEST_F(CrasherTest, signal) {
282 int intercept_result;
283 unique_fd output_fd;
284 StartCrasher("abort");
285 StartIntercept(&output_fd);
286
287 // Wait for a bit, or we might end up killing the process before the signal
288 // handler even gets a chance to be registered.
289 std::this_thread::sleep_for(100ms);
290 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
291
292 AssertDeath(SIGSEGV);
293 FinishIntercept(&intercept_result);
294
295 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
296
297 std::string result;
298 ConsumeFd(std::move(output_fd), &result);
299 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
300 ASSERT_MATCH(result, R"(backtrace:)");
301}
302
303TEST_F(CrasherTest, abort_message) {
304 int intercept_result;
305 unique_fd output_fd;
306 StartCrasher("smash-stack");
307 StartIntercept(&output_fd);
308 FinishCrasher();
309 AssertDeath(SIGABRT);
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"(Abort message: 'stack corruption detected \(-fstack-protector\)')");
317}
318
319TEST_F(CrasherTest, intercept_timeout) {
320 int intercept_result;
321 unique_fd output_fd;
322 StartCrasher("abort");
323 StartIntercept(&output_fd);
324
325 // Don't let crasher finish until we timeout.
326 FinishIntercept(&intercept_result);
327
328 ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
329 << intercept_result << ")";
330
331 FinishCrasher();
332 AssertDeath(SIGABRT);
333}
334
335TEST_F(CrasherTest, wait_for_gdb) {
336 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
337 FAIL() << "failed to enable wait_for_gdb";
338 }
339 sleep(1);
340
341 StartCrasher("abort");
342 FinishCrasher();
343
344 int status;
345 ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
346 ASSERT_TRUE(WIFSTOPPED(status));
347 ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
348
349 ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
350
351 AssertDeath(SIGABRT);
352}
353
Josh Gao7c6e3132017-01-22 17:59:02 -0800354// wait_for_gdb shouldn't trigger on manually sent signals.
Josh Gaocbe70cb2016-10-18 18:17:52 -0700355TEST_F(CrasherTest, wait_for_gdb_signal) {
356 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
357 FAIL() << "failed to enable wait_for_gdb";
358 }
359
360 StartCrasher("abort");
Josh Gao7c6e3132017-01-22 17:59:02 -0800361 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
362 AssertDeath(SIGSEGV);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700363}
364
365TEST_F(CrasherTest, backtrace) {
366 std::string result;
367 int intercept_result;
368 unique_fd output_fd;
369 StartCrasher("abort");
370 StartIntercept(&output_fd);
371
372 std::this_thread::sleep_for(500ms);
373
374 sigval val;
375 val.sival_int = 1;
376 ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
377 FinishIntercept(&intercept_result);
378 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
379 ConsumeFd(std::move(output_fd), &result);
380 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
381
382 int status;
383 ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
384
385 StartIntercept(&output_fd);
386 FinishCrasher();
387 AssertDeath(SIGABRT);
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]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
392}
Josh Gaofca7ca32017-01-23 12:05:35 -0800393
394TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
395 StartProcess([]() {
396 prctl(PR_SET_DUMPABLE, 0);
397 volatile char* null = static_cast<char*>(nullptr);
398 *null = '\0';
399 });
400 AssertDeath(SIGSEGV);
401}
402
403TEST_F(CrasherTest, PR_SET_DUMPABLE_0_raise) {
404 StartProcess([]() {
405 prctl(PR_SET_DUMPABLE, 0);
406 raise(SIGUSR1);
407 });
408 AssertDeath(SIGUSR1);
409}
Josh Gaoc3c8c022017-02-13 16:36:18 -0800410
411TEST(crash_dump, zombie) {
412 pid_t forkpid = fork();
413
414 int pipefd[2];
415 ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
416
417 pid_t rc;
418 int status;
419
420 if (forkpid == 0) {
421 errno = 0;
422 rc = waitpid(-1, &status, WNOHANG | __WALL | __WNOTHREAD);
423 if (rc != -1 || errno != ECHILD) {
424 errx(2, "first waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
425 }
426
427 raise(DEBUGGER_SIGNAL);
428
429 errno = 0;
430 rc = waitpid(-1, &status, __WALL | __WNOTHREAD);
431 if (rc != -1 || errno != ECHILD) {
432 errx(2, "second waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
433 }
434 _exit(0);
435 } else {
436 rc = waitpid(forkpid, &status, 0);
437 ASSERT_EQ(forkpid, rc);
438 ASSERT_TRUE(WIFEXITED(status));
439 ASSERT_EQ(0, WEXITSTATUS(status));
440 }
441}