| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009, Google Inc. | |
| 2 // All rights reserved. | |
| 3 // | |
| 4 // Redistribution and use in source and binary forms, with or without | |
| 5 // modification, are permitted provided that the following conditions are | |
| 6 // met: | |
| 7 // | |
| 8 // * Redistributions of source code must retain the above copyright | |
| 9 // notice, this list of conditions and the following disclaimer. | |
| 10 // * Redistributions in binary form must reproduce the above | |
| 11 // copyright notice, this list of conditions and the following disclaimer | |
| 12 // in the documentation and/or other materials provided with the | |
| 13 // distribution. | |
| 14 // * Neither the name of Google Inc. nor the names of its | |
| 15 // contributors may be used to endorse or promote products derived from | |
| 16 // this software without specific prior written permission. | |
| 17 // | |
| 18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 | |
| 30 #include <string> | |
| 31 | |
| 32 #include <stdint.h> | |
| 33 #include <unistd.h> | |
| 34 #include <signal.h> | |
| 35 #include <sys/poll.h> | |
| 36 #include <sys/socket.h> | |
| 37 #include <sys/uio.h> | |
| 38 | |
| 39 #include "base/eintr_wrapper.h" | |
| 40 #include "breakpad/linux/exception_handler.h" | |
| 41 #include "breakpad/linux/minidump_writer.h" | |
| 42 #include "breakpad/linux/linux_libc_support.h" | |
| 43 #include "breakpad/linux/linux_syscall_support.h" | |
| 44 #include "testing/gtest/include/gtest/gtest.h" | |
| 45 | |
| 46 using namespace google_breakpad; | |
| 47 | |
| 48 static void sigchld_handler(int signo) { } | |
| 49 | |
| 50 class ExceptionHandlerTest : public ::testing::Test { | |
| 51 protected: | |
| 52 void SetUp() { | |
| 53 // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. | |
| 54 struct sigaction sa; | |
| 55 memset(&sa, 0, sizeof(sa)); | |
| 56 sa.sa_handler = sigchld_handler; | |
| 57 ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1); | |
| 58 } | |
| 59 | |
| 60 void TearDown() { | |
| 61 sigaction(SIGCHLD, &old_action, NULL); | |
| 62 } | |
| 63 | |
| 64 struct sigaction old_action; | |
| 65 }; | |
| 66 | |
| 67 TEST(ExceptionHandlerTest, Simple) { | |
| 68 ExceptionHandler handler("/tmp", NULL, NULL, NULL, true); | |
| 69 } | |
| 70 | |
| 71 static bool DoneCallback(const char* dump_path, | |
| 72 const char* minidump_id, | |
| 73 void* context, | |
| 74 bool succeeded) { | |
| 75 if (!succeeded) | |
| 76 return succeeded; | |
| 77 | |
| 78 int fd = (intptr_t) context; | |
| 79 uint32_t len = my_strlen(minidump_id); | |
| 80 HANDLE_EINTR(sys_write(fd, &len, sizeof(len))); | |
| 81 HANDLE_EINTR(sys_write(fd, minidump_id, len)); | |
| 82 sys_close(fd); | |
| 83 | |
| 84 return true; | |
| 85 } | |
| 86 | |
| 87 TEST(ExceptionHandlerTest, ChildCrash) { | |
| 88 int fds[2]; | |
| 89 ASSERT_NE(pipe(fds), -1); | |
| 90 | |
| 91 const pid_t child = fork(); | |
| 92 if (child == 0) { | |
| 93 close(fds[0]); | |
| 94 ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1], | |
| 95 true); | |
| 96 *reinterpret_cast<int*>(NULL) = 0; | |
| 97 } | |
| 98 close(fds[1]); | |
| 99 | |
| 100 int status; | |
| 101 ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); | |
| 102 ASSERT_TRUE(WIFSIGNALED(status)); | |
| 103 ASSERT_EQ(WTERMSIG(status), SIGSEGV); | |
| 104 | |
| 105 struct pollfd pfd; | |
| 106 memset(&pfd, 0, sizeof(pfd)); | |
| 107 pfd.fd = fds[0]; | |
| 108 pfd.events = POLLIN | POLLERR; | |
| 109 | |
| 110 const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); | |
| 111 ASSERT_EQ(r, 1); | |
| 112 ASSERT_TRUE(pfd.revents & POLLIN); | |
| 113 | |
| 114 uint32_t len; | |
| 115 ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len)); | |
| 116 ASSERT_LT(len, 2048); | |
| 117 char* filename = reinterpret_cast<char*>(malloc(len + 1)); | |
| 118 ASSERT_EQ(read(fds[0], filename, len), len); | |
| 119 filename[len] = 0; | |
| 120 close(fds[0]); | |
| 121 | |
| 122 const std::string minidump_filename = std::string("/tmp/") + filename + | |
| 123 ".dmp"; | |
| 124 | |
| 125 struct stat st; | |
| 126 ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); | |
| 127 ASSERT_GT(st.st_size, 0u); | |
| 128 unlink(minidump_filename.c_str()); | |
| 129 } | |
| 130 | |
| 131 static const unsigned kControlMsgSize = | |
| 132 CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); | |
| 133 | |
| 134 static bool | |
| 135 CrashHandler(const void* crash_context, size_t crash_context_size, | |
| 136 void* context) { | |
| 137 const int fd = (intptr_t) context; | |
| 138 int fds[2]; | |
| 139 pipe(fds); | |
| 140 | |
| 141 struct kernel_msghdr msg = {0}; | |
| 142 struct kernel_iovec iov; | |
| 143 iov.iov_base = const_cast<void*>(crash_context); | |
| 144 iov.iov_len = crash_context_size; | |
| 145 | |
| 146 msg.msg_iov = &iov; | |
| 147 msg.msg_iovlen = 1; | |
| 148 char cmsg[kControlMsgSize]; | |
| 149 memset(cmsg, 0, kControlMsgSize); | |
| 150 msg.msg_control = cmsg; | |
| 151 msg.msg_controllen = sizeof(cmsg); | |
| 152 | |
| 153 struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); | |
| 154 hdr->cmsg_level = SOL_SOCKET; | |
| 155 hdr->cmsg_type = SCM_RIGHTS; | |
| 156 hdr->cmsg_len = CMSG_LEN(sizeof(int)); | |
| 157 *((int*) CMSG_DATA(hdr)) = fds[1]; | |
| 158 hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr); | |
| 159 hdr->cmsg_level = SOL_SOCKET; | |
| 160 hdr->cmsg_type = SCM_CREDENTIALS; | |
| 161 hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred)); | |
| 162 struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); | |
| 163 cred->uid = getuid(); | |
| 164 cred->gid = getgid(); | |
| 165 cred->pid = getpid(); | |
| 166 | |
| 167 HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)); | |
| 168 sys_close(fds[1]); | |
| 169 | |
| 170 char b; | |
| 171 HANDLE_EINTR(sys_read(fds[0], &b, 1)); | |
| 172 | |
| 173 return true; | |
| 174 } | |
| 175 | |
| 176 TEST(ExceptionHandlerTest, ExternalDumper) { | |
| 177 int fds[2]; | |
| 178 ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); | |
| 179 static const int on = 1; | |
| 180 setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); | |
| 181 setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); | |
| 182 | |
| 183 const pid_t child = fork(); | |
| 184 if (child == 0) { | |
| 185 close(fds[0]); | |
| 186 ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true); | |
| 187 handler.set_crash_handler(CrashHandler); | |
| 188 *reinterpret_cast<int*>(NULL) = 0; | |
| 189 } | |
| 190 | |
| 191 close(fds[1]); | |
| 192 struct msghdr msg = {0}; | |
| 193 struct iovec iov; | |
| 194 static const unsigned kCrashContextSize = | |
| 195 sizeof(ExceptionHandler::CrashContext); | |
| 196 char context[kCrashContextSize]; | |
| 197 char control[kControlMsgSize]; | |
| 198 iov.iov_base = context; | |
| 199 iov.iov_len = kCrashContextSize; | |
| 200 msg.msg_iov = &iov; | |
| 201 msg.msg_iovlen = 1; | |
| 202 msg.msg_control = control; | |
| 203 msg.msg_controllen = kControlMsgSize; | |
| 204 | |
| 205 const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); | |
| 206 ASSERT_EQ(n, kCrashContextSize); | |
| 207 ASSERT_EQ(msg.msg_controllen, kControlMsgSize); | |
| 208 ASSERT_EQ(msg.msg_flags, 0); | |
| 209 | |
| 210 pid_t crashing_pid = -1; | |
| 211 int signal_fd = -1; | |
| 212 for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; | |
| 213 hdr = CMSG_NXTHDR(&msg, hdr)) { | |
| 214 if (hdr->cmsg_level != SOL_SOCKET) | |
| 215 continue; | |
| 216 if (hdr->cmsg_type == SCM_RIGHTS) { | |
| 217 const unsigned len = hdr->cmsg_len - | |
| 218 (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); | |
| 219 ASSERT_EQ(len, sizeof(int)); | |
| 220 signal_fd = *((int *) CMSG_DATA(hdr)); | |
| 221 } else if (hdr->cmsg_type == SCM_CREDENTIALS) { | |
| 222 const struct ucred *cred = | |
| 223 reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); | |
| 224 crashing_pid = cred->pid; | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 ASSERT_NE(crashing_pid, -1); | |
| 229 ASSERT_NE(signal_fd, -1); | |
| 230 | |
| 231 char templ[] = "/tmp/exception-handler-unittest-XXXXXX"; | |
| 232 mktemp(templ); | |
| 233 ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context, kCrashContextSize)); | |
| 234 static const char b = 0; | |
| 235 HANDLE_EINTR(write(signal_fd, &b, 1)); | |
| 236 | |
| 237 int status; | |
| 238 ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); | |
| 239 ASSERT_TRUE(WIFSIGNALED(status)); | |
| 240 ASSERT_EQ(WTERMSIG(status), SIGSEGV); | |
| 241 | |
| 242 struct stat st; | |
| 243 ASSERT_EQ(stat(templ, &st), 0); | |
| 244 ASSERT_GT(st.st_size, 0u); | |
| 245 unlink(templ); | |
| 246 } | |
| OLD | NEW |