PageRenderTime 46ms CodeModel.GetById 17ms app.highlight 24ms RepoModel.GetById 2ms app.codeStats 0ms

/thirdparty/breakpad/client/linux/crash_generation/crash_generation_server.cc

http://github.com/tomahawk-player/tomahawk
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