/thirdparty/breakpad/client/linux/crash_generation/crash_generation_server.cc
C++ | 467 lines | 332 code | 82 blank | 53 comment | 83 complexity | b1601509de111f3e739fba3ffb38e1be MD5 | raw file
1// Copyright (c) 2010 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 <assert.h> 31#include <dirent.h> 32#include <fcntl.h> 33#include <limits.h> 34#include <poll.h> 35#include <stdio.h> 36#include <string.h> 37#include <sys/socket.h> 38#include <sys/stat.h> 39#include <sys/types.h> 40#include <unistd.h> 41 42#include <vector> 43 44#include "client/linux/crash_generation/crash_generation_server.h" 45#include "client/linux/crash_generation/client_info.h" 46#include "client/linux/handler/exception_handler.h" 47#include "client/linux/minidump_writer/minidump_writer.h" 48#include "common/linux/eintr_wrapper.h" 49#include "common/linux/guid_creator.h" 50#include "common/linux/safe_readlink.h" 51 52static const char kCommandQuit = 'x'; 53 54static bool 55GetInodeForFileDescriptor(ino_t* inode_out, int fd) 56{ 57 assert(inode_out); 58 59 struct stat buf; 60 if (fstat(fd, &buf) < 0) 61 return false; 62 63 if (!S_ISSOCK(buf.st_mode)) 64 return false; 65 66 *inode_out = buf.st_ino; 67 return true; 68} 69 70// expected prefix of the target of the /proc/self/fd/%d link for a socket 71static const char kSocketLinkPrefix[] = "socket:["; 72 73// Parse a symlink in /proc/pid/fd/$x and return the inode number of the 74// socket. 75// inode_out: (output) set to the inode number on success 76// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor) 77static bool 78GetInodeForProcPath(ino_t* inode_out, const char* path) 79{ 80 assert(inode_out); 81 assert(path); 82 83 char buf[PATH_MAX]; 84 if (!google_breakpad::SafeReadLink(path, buf)) { 85 return false; 86 } 87 88 if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { 89 return false; 90 } 91 92 char* endptr; 93 const u_int64_t inode_ul = 94 strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); 95 if (*endptr != ']') 96 return false; 97 98 if (inode_ul == ULLONG_MAX) { 99 return false; 100 } 101 102 *inode_out = inode_ul; 103 return true; 104} 105 106static bool 107FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) 108{ 109 assert(pid_out); 110 bool already_found = false; 111 112 DIR* proc = opendir("/proc"); 113 if (!proc) { 114 return false; 115 } 116 117 std::vector<pid_t> pids; 118 119 struct dirent* dent; 120 while ((dent = readdir(proc))) { 121 char* endptr; 122 const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); 123 if (pid_ul == ULONG_MAX || '\0' != *endptr) 124 continue; 125 pids.push_back(pid_ul); 126 } 127 closedir(proc); 128 129 for (std::vector<pid_t>::const_iterator 130 i = pids.begin(); i != pids.end(); ++i) { 131 const pid_t current_pid = *i; 132 char buf[PATH_MAX]; 133 snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); 134 DIR* fd = opendir(buf); 135 if (!fd) 136 continue; 137 138 while ((dent = readdir(fd))) { 139 if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, 140 dent->d_name) >= static_cast<int>(sizeof(buf))) { 141 continue; 142 } 143 144 ino_t fd_inode; 145 if (GetInodeForProcPath(&fd_inode, buf) 146 && fd_inode == socket_inode) { 147 if (already_found) { 148 closedir(fd); 149 return false; 150 } 151 152 already_found = true; 153 *pid_out = current_pid; 154 break; 155 } 156 } 157 158 closedir(fd); 159 } 160 161 return already_found; 162} 163 164namespace google_breakpad { 165 166CrashGenerationServer::CrashGenerationServer( 167 const int listen_fd, 168 OnClientDumpRequestCallback dump_callback, 169 void* dump_context, 170 OnClientExitingCallback exit_callback, 171 void* exit_context, 172 bool generate_dumps, 173 const std::string* dump_path) : 174 server_fd_(listen_fd), 175 dump_callback_(dump_callback), 176 dump_context_(dump_context), 177 exit_callback_(exit_callback), 178 exit_context_(exit_context), 179 generate_dumps_(generate_dumps), 180 started_(false) 181{ 182 if (dump_path) 183 dump_dir_ = *dump_path; 184 else 185 dump_dir_ = "/tmp"; 186} 187 188CrashGenerationServer::~CrashGenerationServer() 189{ 190 if (started_) 191 Stop(); 192} 193 194bool 195CrashGenerationServer::Start() 196{ 197 if (started_ || 0 > server_fd_) 198 return false; 199 200 int control_pipe[2]; 201 if (pipe(control_pipe)) 202 return false; 203 204 if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC)) 205 return false; 206 if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC)) 207 return false; 208 209 if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK)) 210 return false; 211 212 control_pipe_in_ = control_pipe[0]; 213 control_pipe_out_ = control_pipe[1]; 214 215 if (pthread_create(&thread_, NULL, 216 ThreadMain, reinterpret_cast<void*>(this))) 217 return false; 218 219 started_ = true; 220 return true; 221} 222 223void 224CrashGenerationServer::Stop() 225{ 226 assert(pthread_self() != thread_); 227 228 if (!started_) 229 return; 230 231 HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1)); 232 233 void* dummy; 234 pthread_join(thread_, &dummy); 235 236 started_ = false; 237} 238 239//static 240bool 241CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd) 242{ 243 int fds[2]; 244 245 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) 246 return false; 247 248 static const int on = 1; 249 // Enable passcred on the server end of the socket 250 if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) 251 return false; 252 253 if (fcntl(fds[1], F_SETFL, O_NONBLOCK)) 254 return false; 255 if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) 256 return false; 257 258 *client_fd = fds[0]; 259 *server_fd = fds[1]; 260 return true; 261} 262 263// The following methods/functions execute on the server thread 264 265void 266CrashGenerationServer::Run() 267{ 268 struct pollfd pollfds[2]; 269 memset(&pollfds, 0, sizeof(pollfds)); 270 271 pollfds[0].fd = server_fd_; 272 pollfds[0].events = POLLIN; 273 274 pollfds[1].fd = control_pipe_in_; 275 pollfds[1].events = POLLIN; 276 277 while (true) { 278 // infinite timeout 279 int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); 280 if (-1 == nevents) { 281 if (EINTR == errno) { 282 continue; 283 } else { 284 return; 285 } 286 } 287 288 if (pollfds[0].revents && !ClientEvent(pollfds[0].revents)) 289 return; 290 291 if (pollfds[1].revents && !ControlEvent(pollfds[1].revents)) 292 return; 293 } 294} 295 296bool 297CrashGenerationServer::ClientEvent(short revents) 298{ 299 if (POLLHUP & revents) 300 return false; 301 assert(POLLIN & revents); 302 303 // A process has crashed and has signaled us by writing a datagram 304 // to the death signal socket. The datagram contains the crash context needed 305 // for writing the minidump as well as a file descriptor and a credentials 306 // block so that they can't lie about their pid. 307 308 // The length of the control message: 309 static const unsigned kControlMsgSize = 310 CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); 311 // The length of the regular payload: 312 static const unsigned kCrashContextSize = 313 sizeof(google_breakpad::ExceptionHandler::CrashContext); 314 315 struct msghdr msg = {0}; 316 struct iovec iov[1]; 317 char crash_context[kCrashContextSize]; 318 char control[kControlMsgSize]; 319 const ssize_t expected_msg_size = sizeof(crash_context); 320 321 iov[0].iov_base = crash_context; 322 iov[0].iov_len = sizeof(crash_context); 323 msg.msg_iov = iov; 324 msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); 325 msg.msg_control = control; 326 msg.msg_controllen = kControlMsgSize; 327 328 const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0)); 329 if (msg_size != expected_msg_size) 330 return true; 331 332 if (msg.msg_controllen != kControlMsgSize || 333 msg.msg_flags & ~MSG_TRUNC) 334 return true; 335 336 // Walk the control payload and extract the file descriptor and validated pid. 337 pid_t crashing_pid = -1; 338 int signal_fd = -1; 339 for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; 340 hdr = CMSG_NXTHDR(&msg, hdr)) { 341 if (hdr->cmsg_level != SOL_SOCKET) 342 continue; 343 if (hdr->cmsg_type == SCM_RIGHTS) { 344 const unsigned len = hdr->cmsg_len - 345 (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); 346 assert(len % sizeof(int) == 0u); 347 const unsigned num_fds = len / sizeof(int); 348 if (num_fds > 1 || num_fds == 0) { 349 // A nasty process could try and send us too many descriptors and 350 // force a leak. 351 for (unsigned i = 0; i < num_fds; ++i) 352 HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i])); 353 return true; 354 } else { 355 signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0]; 356 } 357 } else if (hdr->cmsg_type == SCM_CREDENTIALS) { 358 const struct ucred *cred = 359 reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); 360 crashing_pid = cred->pid; 361 } 362 } 363 364 if (crashing_pid == -1 || signal_fd == -1) { 365 if (signal_fd) 366 HANDLE_EINTR(close(signal_fd)); 367 return true; 368 } 369 370 // Kernel bug workaround (broken in 2.6.30 at least): 371 // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID 372 // namespaces. Thus |crashing_pid| might be garbage from our point of view. 373 // In the future we can remove this workaround, but we have to wait a couple 374 // of years to be sure that it's worked its way out into the world. 375 376 ino_t inode_number; 377 if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) { 378 HANDLE_EINTR(close(signal_fd)); 379 return true; 380 } 381 382 if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { 383 HANDLE_EINTR(close(signal_fd)); 384 return true; 385 } 386 387 std::string minidump_filename; 388 if (!MakeMinidumpFilename(minidump_filename)) 389 return true; 390 391 if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), 392 crashing_pid, crash_context, 393 kCrashContextSize)) { 394 HANDLE_EINTR(close(signal_fd)); 395 return true; 396 } 397 398 if (dump_callback_) { 399 ClientInfo info; 400 401 info.crash_server_ = this; 402 info.pid_ = crashing_pid; 403 404 dump_callback_(dump_context_, &info, &minidump_filename); 405 } 406 407 // Send the done signal to the process: it can exit now. 408 memset(&msg, 0, sizeof(msg)); 409 struct iovec done_iov; 410 done_iov.iov_base = const_cast<char*>("\x42"); 411 done_iov.iov_len = 1; 412 msg.msg_iov = &done_iov; 413 msg.msg_iovlen = 1; 414 415 HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); 416 HANDLE_EINTR(close(signal_fd)); 417 418 return true; 419} 420 421bool 422CrashGenerationServer::ControlEvent(short revents) 423{ 424 if (POLLHUP & revents) 425 return false; 426 assert(POLLIN & revents); 427 428 char command; 429 if (read(control_pipe_in_, &command, 1)) 430 return false; 431 432 switch (command) { 433 case kCommandQuit: 434 return false; 435 default: 436 assert(0); 437 } 438 439 return true; 440} 441 442bool 443CrashGenerationServer::MakeMinidumpFilename(std::string& outFilename) 444{ 445 GUID guid; 446 char guidString[kGUIDStringLength+1]; 447 448 if (!(CreateGUID(&guid) 449 && GUIDToString(&guid, guidString, sizeof(guidString)))) 450 return false; 451 452 char path[PATH_MAX]; 453 snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString); 454 455 outFilename = path; 456 return true; 457} 458 459// static 460void* 461CrashGenerationServer::ThreadMain(void *arg) 462{ 463 reinterpret_cast<CrashGenerationServer*>(arg)->Run(); 464 return NULL; 465} 466 467} // namespace google_breakpad