blob: f51b5f25312f0a4c38ad7ca95210e228a1ae3ba2 [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>
20#include <sys/types.h>
21
22#include <chrono>
23#include <regex>
24#include <thread>
25
26#include <android-base/file.h>
27#include <android-base/logging.h>
28#include <android-base/parseint.h>
29#include <android-base/properties.h>
30#include <android-base/strings.h>
31#include <android-base/unique_fd.h>
32#include <cutils/sockets.h>
33#include <debuggerd/handler.h>
34#include <debuggerd/protocol.h>
35#include <debuggerd/util.h>
36#include <gtest/gtest.h>
37
38using namespace std::chrono_literals;
39using android::base::unique_fd;
40
41#if defined(__LP64__)
42#define CRASHER_PATH "/system/xbin/crasher64"
43#define ARCH_SUFFIX "64"
44#else
45#define CRASHER_PATH "/system/xbin/crasher"
46#define ARCH_SUFFIX ""
47#endif
48
49constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb";
50
51#define TIMEOUT(seconds, expr) \
52 [&]() { \
53 struct sigaction old_sigaction; \
54 struct sigaction new_sigaction = {}; \
55 new_sigaction.sa_handler = [](int) {}; \
56 if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
57 err(1, "sigaction failed"); \
58 } \
59 alarm(seconds); \
60 auto value = expr; \
61 int saved_errno = errno; \
62 if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \
63 err(1, "sigaction failed"); \
64 } \
65 alarm(0); \
66 errno = saved_errno; \
67 return value; \
68 }()
69
70#define ASSERT_MATCH(str, pattern) \
71 do { \
72 std::regex r((pattern)); \
73 if (!std::regex_search((str), r)) { \
74 FAIL() << "regex mismatch: expected " << (pattern) << " in: \n" << (str); \
75 } \
76 } while (0)
77
78class CrasherTest : public ::testing::Test {
79 public:
80 pid_t crasher_pid = -1;
81 bool previous_wait_for_gdb;
82 unique_fd crasher_pipe;
83 unique_fd intercept_fd;
84
85 CrasherTest();
86 ~CrasherTest();
87
88 void StartIntercept(unique_fd* output_fd);
89
90 // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.
91 void FinishIntercept(int* result);
92
93 void StartCrasher(const std::string& crash_type);
94 void FinishCrasher();
95 void AssertDeath(int signo);
96};
97
98CrasherTest::CrasherTest() {
99 previous_wait_for_gdb = android::base::GetBoolProperty(kWaitForGdbKey, false);
100 android::base::SetProperty(kWaitForGdbKey, "0");
101}
102
103CrasherTest::~CrasherTest() {
104 if (crasher_pid != -1) {
105 kill(crasher_pid, SIGKILL);
106 int status;
107 waitpid(crasher_pid, &status, WUNTRACED);
108 }
109
110 android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
111}
112
113void CrasherTest::StartIntercept(unique_fd* output_fd) {
114 if (crasher_pid == -1) {
115 FAIL() << "crasher hasn't been started";
116 }
117
118 intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
119 ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
120 if (intercept_fd == -1) {
121 FAIL() << "failed to contact tombstoned: " << strerror(errno);
122 }
123
124 InterceptRequest req = {.pid = crasher_pid };
125
126 unique_fd output_pipe_write;
127 if (!Pipe(output_fd, &output_pipe_write)) {
128 FAIL() << "failed to create output pipe: " << strerror(errno);
129 }
130
131 std::string pipe_size_str;
132 int pipe_buffer_size;
133 if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
134 FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
135 }
136
137 pipe_size_str = android::base::Trim(pipe_size_str);
138
139 if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
140 FAIL() << "failed to parse pipe max size";
141 }
142
143 if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
144 FAIL() << "failed to set pipe size: " << strerror(errno);
145 }
146
147 if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
148 FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
149 }
150}
151
152void CrasherTest::FinishIntercept(int* result) {
153 InterceptResponse response;
154
155 // Timeout for tombstoned intercept is 10 seconds.
156 ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
157 if (rc == -1) {
158 FAIL() << "failed to read response from tombstoned: " << strerror(errno);
159 } else if (rc == 0) {
160 *result = -1;
161 } else if (rc != sizeof(response)) {
162 FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
163 << ", received " << rc;
164 } else {
165 *result = response.success;
166 }
167}
168
169void CrasherTest::StartCrasher(const std::string& crash_type) {
170 std::string type = "wait-" + crash_type;
171
172 unique_fd crasher_read_pipe;
173 if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {
174 FAIL() << "failed to create pipe: " << strerror(errno);
175 }
176
177 crasher_pid = fork();
178 if (crasher_pid == -1) {
179 FAIL() << "fork failed: " << strerror(errno);
180 } else if (crasher_pid == 0) {
181 unique_fd devnull(open("/dev/null", O_WRONLY));
182 dup2(crasher_read_pipe.get(), STDIN_FILENO);
183 dup2(devnull.get(), STDOUT_FILENO);
184 dup2(devnull.get(), STDERR_FILENO);
185 execl(CRASHER_PATH, CRASHER_PATH, type.c_str(), nullptr);
186 err(1, "exec failed");
187 }
188}
189
190void CrasherTest::FinishCrasher() {
191 if (crasher_pipe == -1) {
192 FAIL() << "crasher pipe uninitialized";
193 }
194
195 ssize_t rc = write(crasher_pipe.get(), "\n", 1);
196 if (rc == -1) {
197 FAIL() << "failed to write to crasher pipe: " << strerror(errno);
198 } else if (rc == 0) {
199 FAIL() << "crasher pipe was closed";
200 }
201}
202
203void CrasherTest::AssertDeath(int signo) {
204 int status;
205 pid_t pid = TIMEOUT(5, waitpid(crasher_pid, &status, 0));
206 if (pid != crasher_pid) {
207 FAIL() << "failed to wait for crasher: " << strerror(errno);
208 }
209
210 if (!WIFSIGNALED(status)) {
211 FAIL() << "crasher didn't terminate via a signal";
212 }
213 ASSERT_EQ(signo, WTERMSIG(status));
214 crasher_pid = -1;
215}
216
217static void ConsumeFd(unique_fd fd, std::string* output) {
218 constexpr size_t read_length = PAGE_SIZE;
219 std::string result;
220
221 while (true) {
222 size_t offset = result.size();
223 result.resize(result.size() + PAGE_SIZE);
224 ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
225 if (rc == -1) {
226 FAIL() << "read failed: " << strerror(errno);
227 } else if (rc == 0) {
228 result.resize(result.size() - PAGE_SIZE);
229 break;
230 }
231
232 result.resize(result.size() - PAGE_SIZE + rc);
233 }
234
235 *output = std::move(result);
236}
237
238TEST_F(CrasherTest, smoke) {
239 int intercept_result;
240 unique_fd output_fd;
241 StartCrasher("SIGSEGV");
242 StartIntercept(&output_fd);
243 FinishCrasher();
244 AssertDeath(SIGSEGV);
245 FinishIntercept(&intercept_result);
246
247 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
248
249 std::string result;
250 ConsumeFd(std::move(output_fd), &result);
251 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
252}
253
254TEST_F(CrasherTest, abort) {
255 int intercept_result;
256 unique_fd output_fd;
257 StartCrasher("abort");
258 StartIntercept(&output_fd);
259 FinishCrasher();
260 AssertDeath(SIGABRT);
261 FinishIntercept(&intercept_result);
262
263 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
264
265 std::string result;
266 ConsumeFd(std::move(output_fd), &result);
267 ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
268}
269
270TEST_F(CrasherTest, signal) {
271 int intercept_result;
272 unique_fd output_fd;
273 StartCrasher("abort");
274 StartIntercept(&output_fd);
275
276 // Wait for a bit, or we might end up killing the process before the signal
277 // handler even gets a chance to be registered.
278 std::this_thread::sleep_for(100ms);
279 ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));
280
281 AssertDeath(SIGSEGV);
282 FinishIntercept(&intercept_result);
283
284 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
285
286 std::string result;
287 ConsumeFd(std::move(output_fd), &result);
288 ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 0 \(SI_USER\), fault addr --------)");
289 ASSERT_MATCH(result, R"(backtrace:)");
290}
291
292TEST_F(CrasherTest, abort_message) {
293 int intercept_result;
294 unique_fd output_fd;
295 StartCrasher("smash-stack");
296 StartIntercept(&output_fd);
297 FinishCrasher();
298 AssertDeath(SIGABRT);
299 FinishIntercept(&intercept_result);
300
301 ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
302
303 std::string result;
304 ConsumeFd(std::move(output_fd), &result);
305 ASSERT_MATCH(result, R"(Abort message: 'stack corruption detected \(-fstack-protector\)')");
306}
307
308TEST_F(CrasherTest, intercept_timeout) {
309 int intercept_result;
310 unique_fd output_fd;
311 StartCrasher("abort");
312 StartIntercept(&output_fd);
313
314 // Don't let crasher finish until we timeout.
315 FinishIntercept(&intercept_result);
316
317 ASSERT_NE(1, intercept_result) << "tombstoned reported success? (intercept_result = "
318 << intercept_result << ")";
319
320 FinishCrasher();
321 AssertDeath(SIGABRT);
322}
323
324TEST_F(CrasherTest, wait_for_gdb) {
325 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
326 FAIL() << "failed to enable wait_for_gdb";
327 }
328 sleep(1);
329
330 StartCrasher("abort");
331 FinishCrasher();
332
333 int status;
334 ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
335 ASSERT_TRUE(WIFSTOPPED(status));
336 ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
337
338 ASSERT_EQ(0, kill(crasher_pid, SIGCONT));
339
340 AssertDeath(SIGABRT);
341}
342
343TEST_F(CrasherTest, wait_for_gdb_signal) {
344 if (!android::base::SetProperty(kWaitForGdbKey, "1")) {
345 FAIL() << "failed to enable wait_for_gdb";
346 }
347
348 StartCrasher("abort");
349 ASSERT_EQ(0, kill(crasher_pid, SIGABRT)) << strerror(errno);
350
351 std::this_thread::sleep_for(500ms);
352
353 int status;
354 ASSERT_EQ(crasher_pid, (TIMEOUT(1, waitpid(crasher_pid, &status, WUNTRACED))));
355 ASSERT_TRUE(WIFSTOPPED(status));
356 ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
357
358 ASSERT_EQ(0, kill(crasher_pid, SIGCONT)) << strerror(errno);
359
360 AssertDeath(SIGABRT);
361}
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}