crash_generation_server.cc
1 // Copyright 2010 Google LLC 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google LLC nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #ifdef HAVE_CONFIG_H 30 #include <config.h> // Must come first 31 #endif 32 33 #include <assert.h> 34 #include <dirent.h> 35 #include <fcntl.h> 36 #include <limits.h> 37 #include <poll.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <sys/socket.h> 41 #include <sys/stat.h> 42 #include <sys/types.h> 43 #include <unistd.h> 44 45 #include <vector> 46 47 #include "client/linux/crash_generation/crash_generation_server.h" 48 #include "client/linux/crash_generation/client_info.h" 49 #include "client/linux/handler/exception_handler.h" 50 #include "client/linux/minidump_writer/minidump_writer.h" 51 #include "common/linux/eintr_wrapper.h" 52 #include "common/linux/guid_creator.h" 53 #include "common/linux/safe_readlink.h" 54 55 static const char kCommandQuit = 'x'; 56 57 namespace google_breakpad { 58 59 CrashGenerationServer::CrashGenerationServer( 60 const int listen_fd, 61 OnClientDumpRequestCallback dump_callback, 62 void* dump_context, 63 OnClientExitingCallback exit_callback, 64 void* exit_context, 65 bool generate_dumps, 66 const string* dump_path) : 67 server_fd_(listen_fd), 68 dump_callback_(dump_callback), 69 dump_context_(dump_context), 70 exit_callback_(exit_callback), 71 exit_context_(exit_context), 72 generate_dumps_(generate_dumps), 73 started_(false) 74 { 75 if (dump_path) 76 dump_dir_ = *dump_path; 77 else 78 dump_dir_ = "/tmp"; 79 } 80 81 CrashGenerationServer::~CrashGenerationServer() 82 { 83 if (started_) 84 Stop(); 85 } 86 87 bool 88 CrashGenerationServer::Start() 89 { 90 if (started_ || 0 > server_fd_) 91 return false; 92 93 int control_pipe[2]; 94 if (pipe(control_pipe)) 95 return false; 96 97 if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC)) 98 return false; 99 if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC)) 100 return false; 101 102 if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK)) 103 return false; 104 105 control_pipe_in_ = control_pipe[0]; 106 control_pipe_out_ = control_pipe[1]; 107 108 if (pthread_create(&thread_, NULL, 109 ThreadMain, reinterpret_cast<void*>(this))) 110 return false; 111 112 started_ = true; 113 return true; 114 } 115 116 void 117 CrashGenerationServer::Stop() 118 { 119 assert(pthread_self() != thread_); 120 121 if (!started_) 122 return; 123 124 HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1)); 125 126 void* dummy; 127 pthread_join(thread_, &dummy); 128 129 close(control_pipe_in_); 130 close(control_pipe_out_); 131 132 started_ = false; 133 } 134 135 //static 136 bool 137 CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd) 138 { 139 int fds[2]; 140 141 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) 142 return false; 143 144 static const int on = 1; 145 // Enable passcred on the server end of the socket 146 if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) 147 return false; 148 149 if (fcntl(fds[1], F_SETFL, O_NONBLOCK)) 150 return false; 151 if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) 152 return false; 153 154 *client_fd = fds[0]; 155 *server_fd = fds[1]; 156 return true; 157 } 158 159 // The following methods/functions execute on the server thread 160 161 void 162 CrashGenerationServer::Run() 163 { 164 struct pollfd pollfds[2]; 165 memset(&pollfds, 0, sizeof(pollfds)); 166 167 pollfds[0].fd = server_fd_; 168 pollfds[0].events = POLLIN; 169 170 pollfds[1].fd = control_pipe_in_; 171 pollfds[1].events = POLLIN; 172 173 while (true) { 174 // infinite timeout 175 int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); 176 if (-1 == nevents) { 177 if (EINTR == errno) { 178 continue; 179 } else { 180 return; 181 } 182 } 183 184 if (pollfds[0].revents && !ClientEvent(pollfds[0].revents)) 185 return; 186 187 if (pollfds[1].revents && !ControlEvent(pollfds[1].revents)) 188 return; 189 } 190 } 191 192 bool 193 CrashGenerationServer::ClientEvent(short revents) 194 { 195 if (POLLHUP & revents) 196 return false; 197 assert(POLLIN & revents); 198 199 // A process has crashed and has signaled us by writing a datagram 200 // to the death signal socket. The datagram contains the crash context needed 201 // for writing the minidump as well as a file descriptor and a credentials 202 // block so that they can't lie about their pid. 203 204 // The length of the control message: 205 static const unsigned kControlMsgSize = 206 CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); 207 // The length of the regular payload: 208 static const unsigned kCrashContextSize = 209 sizeof(google_breakpad::ExceptionHandler::CrashContext); 210 211 struct msghdr msg = {0}; 212 struct iovec iov[1]; 213 char crash_context[kCrashContextSize]; 214 char control[kControlMsgSize]; 215 const ssize_t expected_msg_size = sizeof(crash_context); 216 217 iov[0].iov_base = crash_context; 218 iov[0].iov_len = sizeof(crash_context); 219 msg.msg_iov = iov; 220 msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); 221 msg.msg_control = control; 222 msg.msg_controllen = kControlMsgSize; 223 224 const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0)); 225 if (msg_size != expected_msg_size) 226 return true; 227 228 if (msg.msg_controllen != kControlMsgSize || 229 msg.msg_flags & ~MSG_TRUNC) 230 return true; 231 232 // Walk the control payload and extract the file descriptor and validated pid. 233 pid_t crashing_pid = -1; 234 int signal_fd = -1; 235 for (struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); hdr; 236 hdr = CMSG_NXTHDR(&msg, hdr)) { 237 if (hdr->cmsg_level != SOL_SOCKET) 238 continue; 239 if (hdr->cmsg_type == SCM_RIGHTS) { 240 const unsigned len = hdr->cmsg_len - 241 (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); 242 assert(len % sizeof(int) == 0u); 243 const unsigned num_fds = len / sizeof(int); 244 if (num_fds > 1 || num_fds == 0) { 245 // A nasty process could try and send us too many descriptors and 246 // force a leak. 247 for (unsigned i = 0; i < num_fds; ++i) 248 close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]); 249 return true; 250 } else { 251 signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0]; 252 } 253 } else if (hdr->cmsg_type == SCM_CREDENTIALS) { 254 const struct ucred* cred = 255 reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); 256 crashing_pid = cred->pid; 257 } 258 } 259 260 if (crashing_pid == -1 || signal_fd == -1) { 261 if (signal_fd != -1) 262 close(signal_fd); 263 return true; 264 } 265 266 string minidump_filename; 267 if (!MakeMinidumpFilename(minidump_filename)) 268 return true; 269 270 if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), 271 crashing_pid, crash_context, 272 kCrashContextSize)) { 273 close(signal_fd); 274 return true; 275 } 276 277 if (dump_callback_) { 278 ClientInfo info(crashing_pid, this); 279 280 dump_callback_(dump_context_, &info, &minidump_filename); 281 } 282 283 // Send the done signal to the process: it can exit now. 284 // (Closing this will make the child's sys_read unblock and return 0.) 285 close(signal_fd); 286 287 return true; 288 } 289 290 bool 291 CrashGenerationServer::ControlEvent(short revents) 292 { 293 if (POLLHUP & revents) 294 return false; 295 assert(POLLIN & revents); 296 297 char command; 298 if (read(control_pipe_in_, &command, 1)) 299 return false; 300 301 switch (command) { 302 case kCommandQuit: 303 return false; 304 default: 305 assert(0); 306 } 307 308 return true; 309 } 310 311 bool 312 CrashGenerationServer::MakeMinidumpFilename(string& outFilename) 313 { 314 GUID guid; 315 char guidString[kGUIDStringLength+1]; 316 317 if (!(CreateGUID(&guid) 318 && GUIDToString(&guid, guidString, sizeof(guidString)))) 319 return false; 320 321 char path[PATH_MAX]; 322 snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString); 323 324 outFilename = path; 325 return true; 326 } 327 328 // static 329 void* 330 CrashGenerationServer::ThreadMain(void* arg) 331 { 332 reinterpret_cast<CrashGenerationServer*>(arg)->Run(); 333 return NULL; 334 } 335 336 } // namespace google_breakpad