LCOV - code coverage report
Current view: directory - toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation - crash_generation_server.cc (source / functions) Found Hit Coverage
Test: app.info Lines: 204 0 0.0 %
Date: 2012-06-02 Functions: 13 0 0.0 %

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

Generated by: LCOV version 1.7