blob: 8e38d0dc2979e877c3b3dab5c8a6acf5194119da [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);
195 err(1, "exec failed");
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
219 if (!WIFSIGNALED(status)) {
220 FAIL() << "crasher didn't terminate via a signal";
221 }
222 ASSERT_EQ(signo, WTERMSIG(status));
223 crasher_pid = -1;
224}
225
226static void ConsumeFd(unique_fd fd, std::string* output) {
227 constexpr size_t read_length = PAGE_SIZE;
228 std::string result;
229
230 while (true) {
231 size_t offset = result.size();
232 result.resize(result.size() + PAGE_SIZE);
233 ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
234 if (rc == -1) {
235 FAIL() << "read failed: " << strerror(errno);
236 } else if (rc == 0) {
237 result.resize(result.size() - PAGE_SIZE);
238 break;
239 }
240
241 result.resize(result.size() - PAGE_SIZE + rc);
242 }
243
244 *output = std::move(result);
245}
246
247TEST_F(CrasherTest, smoke) {
248 int intercept_result;
249 unique_fd output_fd;
250 StartCrasher("SIGSEGV");
251 StartIntercept(&output_fd);
252 FinishCrasher();
253 AssertDeath(SIGSEGV);
254 FinishIntercept(&intercept_result);
255
256 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
257
258 std::string result;
259 ConsumeFd(std::move(output_fd), &result);
260 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
261}
262
263TEST_F(CrasherTest, abort) {
264 int intercept_result;
265 unique_fd output_fd;
266 StartCrasher("abort");
267 StartIntercept(&output_fd);
268 FinishCrasher();
269 AssertDeath(SIGABRT);
270 FinishIntercept(&intercept_result);
271
272 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
273
274 std::string result;
275 ConsumeFd(std::move(output_fd), &result);
276 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
277}
278
279TEST_F(CrasherTest, signal) {
280 int intercept_result;
281 unique_fd output_fd;
282 StartCrasher("abort");
283 StartIntercept(&output_fd);
284
285 // Wait for a bit, or we might end up killing the process before the signal
286 // handler even gets a chance to be registered.
287 std::this_thread::sleep_for(100ms);
288 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
289
290 AssertDeath(SIGSEGV);
291 FinishIntercept(&intercept_result);
292
293 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
294
295 std::string result;
296 ConsumeFd(std::move(output_fd), &result);
297 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
298 ASSERT_MATCH(result, R"(backtrace:)");
299}
300
301TEST_F(CrasherTest, abort_message) {
302 int intercept_result;
303 unique_fd output_fd;
304 StartCrasher("smash-stack");
305 StartIntercept(&output_fd);
306 FinishCrasher();
307 AssertDeath(SIGABRT);
308 FinishIntercept(&intercept_result);
309
310 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
311
312 std::string result;
313 ConsumeFd(std::move(output_fd), &result);
314 ASSERT_MATCH(result, R"(Abort message: 'stack corruption detected \(-fstack-protector\)')");
315}
316
317TEST_F(CrasherTest, intercept_timeout) {
318 int intercept_result;
319 unique_fd output_fd;
320 StartCrasher("abort");
321 StartIntercept(&output_fd);
322
323 // Don't let crasher finish until we timeout.
324 FinishIntercept(&intercept_result);
325
326 ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
327 << intercept_result << ")";
328
329 FinishCrasher();
330 AssertDeath(SIGABRT);
331}
332
333TEST_F(CrasherTest, wait_for_gdb) {
334 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
335 FAIL() << "failed to enable wait_for_gdb";
336 }
337 sleep(1);
338
339 StartCrasher("abort");
340 FinishCrasher();
341
342 int status;
343 ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
344 ASSERT_TRUE(WIFSTOPPED(status));
345 ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
346
347 ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
348
349 AssertDeath(SIGABRT);
350}
351
Josh Gao7c6e3132017-01-22 17:59:02 -0800352// wait_for_gdb shouldn't trigger on manually sent signals.
Josh Gaocbe70cb2016-10-18 18:17:52 -0700353TEST_F(CrasherTest, wait_for_gdb_signal) {
354 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
355 FAIL() << "failed to enable wait_for_gdb";
356 }
357
358 StartCrasher("abort");
Josh Gao7c6e3132017-01-22 17:59:02 -0800359 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV)) << strerror(errno);
360 AssertDeath(SIGSEGV);
Josh Gaocbe70cb2016-10-18 18:17:52 -0700361}
362
363TEST_F(CrasherTest, backtrace) {
364 std::string result;
365 int intercept_result;
366 unique_fd output_fd;
367 StartCrasher("abort");
368 StartIntercept(&output_fd);
369
370 std::this_thread::sleep_for(500ms);
371
372 sigval val;
373 val.sival_int = 1;
374 ASSERT_EQ(0, sigqueue(crasher_pid, DEBUGGER_SIGNAL, val)) << strerror(errno);
375 FinishIntercept(&intercept_result);
376 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
377 ConsumeFd(std::move(output_fd), &result);
378 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(read\+)");
379
380 int status;
381 ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));
382
383 StartIntercept(&output_fd);
384 FinishCrasher();
385 AssertDeath(SIGABRT);
386 FinishIntercept(&intercept_result);
387 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
388 ConsumeFd(std::move(output_fd), &result);
389 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
390}
Josh Gaofca7ca32017-01-23 12:05:35 -0800391
392TEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {
393 StartProcess([]() {
394 prctl(PR_SET_DUMPABLE, 0);
395 volatile char* null = static_cast<char*>(nullptr);
396 *null = '\0';
397 });
398 AssertDeath(SIGSEGV);
399}
400
401TEST_F(CrasherTest, PR_SET_DUMPABLE_0_raise) {
402 StartProcess([]() {
403 prctl(PR_SET_DUMPABLE, 0);
404 raise(SIGUSR1);
405 });
406 AssertDeath(SIGUSR1);
407}