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
54namespace google_breakpad {
55
56CrashGenerationServer::CrashGenerationServer(
57  const int listen_fd,
58  OnClientDumpRequestCallback dump_callback,
59  void* dump_context,
60  OnClientExitingCallback exit_callback,
61  void* exit_context,
62  bool generate_dumps,
63  const string* dump_path) :
64    server_fd_(listen_fd),
65    dump_callback_(dump_callback),
66    dump_context_(dump_context),
67    exit_callback_(exit_callback),
68    exit_context_(exit_context),
69    generate_dumps_(generate_dumps),
70    started_(false)
71{
72  if (dump_path)
73    dump_dir_ = *dump_path;
74  else
75    dump_dir_ = "/tmp";
76}
77
78CrashGenerationServer::~CrashGenerationServer()
79{
80  if (started_)
81    Stop();
82}
83
84bool
85CrashGenerationServer::Start()
86{
87  if (started_ || 0 > server_fd_)
88    return false;
89
90  int control_pipe[2];
91  if (pipe(control_pipe))
92    return false;
93
94  if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
95    return false;
96  if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
97    return false;
98
99  if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
100    return false;
101
102  control_pipe_in_ = control_pipe[0];
103  control_pipe_out_ = control_pipe[1];
104
105  if (pthread_create(&thread_, NULL,
106                     ThreadMain, reinterpret_cast<void*>(this)))
107    return false;
108
109  started_ = true;
110  return true;
111}
112
113void
114CrashGenerationServer::Stop()
115{
116  assert(pthread_self() != thread_);
117
118  if (!started_)
119    return;
120
121  HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
122
123  void* dummy;
124  pthread_join(thread_, &dummy);
125
126  started_ = false;
127}
128
129//static
130bool
131CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
132{
133  int fds[2];
134
135  if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
136    return false;
137
138  static const int on = 1;
139  // Enable passcred on the server end of the socket
140  if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
141    return false;
142
143  if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
144    return false;
145  if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
146    return false;
147
148  *client_fd = fds[0];
149  *server_fd = fds[1];
150  return true;
151}
152
153// The following methods/functions execute on the server thread
154
155void
156CrashGenerationServer::Run()
157{
158  struct pollfd pollfds[2];
159  memset(&pollfds, 0, sizeof(pollfds));
160
161  pollfds[0].fd = server_fd_;
162  pollfds[0].events = POLLIN;
163
164  pollfds[1].fd = control_pipe_in_;
165  pollfds[1].events = POLLIN;
166
167  while (true) {
168    // infinite timeout
169    int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
170    if (-1 == nevents) {
171      if (EINTR == errno) {
172        continue;
173      } else {
174        return;
175      }
176    }
177
178    if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
179      return;
180
181    if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
182      return;
183  }
184}
185
186bool
187CrashGenerationServer::ClientEvent(short revents)
188{
189  if (POLLHUP & revents)
190    return false;
191  assert(POLLIN & revents);
192
193  // A process has crashed and has signaled us by writing a datagram
194  // to the death signal socket. The datagram contains the crash context needed
195  // for writing the minidump as well as a file descriptor and a credentials
196  // block so that they can't lie about their pid.
197
198  // The length of the control message:
199  static const unsigned kControlMsgSize =
200      CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
201  // The length of the regular payload:
202  static const unsigned kCrashContextSize =
203      sizeof(google_breakpad::ExceptionHandler::CrashContext);
204
205  struct msghdr msg = {0};
206  struct iovec iov[1];
207  char crash_context[kCrashContextSize];
208  char control[kControlMsgSize];
209  const ssize_t expected_msg_size = sizeof(crash_context);
210
211  iov[0].iov_base = crash_context;
212  iov[0].iov_len = sizeof(crash_context);
213  msg.msg_iov = iov;
214  msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
215  msg.msg_control = control;
216  msg.msg_controllen = kControlMsgSize;
217
218  const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
219  if (msg_size != expected_msg_size)
220    return true;
221
222  if (msg.msg_controllen != kControlMsgSize ||
223      msg.msg_flags & ~MSG_TRUNC)
224    return true;
225
226  // Walk the control payload and extract the file descriptor and validated pid.
227  pid_t crashing_pid = -1;
228  int signal_fd = -1;
229  for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
230       hdr = CMSG_NXTHDR(&msg, hdr)) {
231    if (hdr->cmsg_level != SOL_SOCKET)
232      continue;
233    if (hdr->cmsg_type == SCM_RIGHTS) {
234      const unsigned len = hdr->cmsg_len -
235          (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
236      assert(len % sizeof(int) == 0u);
237      const unsigned num_fds = len / sizeof(int);
238      if (num_fds > 1 || num_fds == 0) {
239        // A nasty process could try and send us too many descriptors and
240        // force a leak.
241        for (unsigned i = 0; i < num_fds; ++i)
242          close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
243        return true;
244      } else {
245        signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
246      }
247    } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
248      const struct ucred *cred =
249          reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
250      crashing_pid = cred->pid;
251    }
252  }
253
254  if (crashing_pid == -1 || signal_fd == -1) {
255    if (signal_fd)
256      close(signal_fd);
257    return true;
258  }
259
260  string minidump_filename;
261  if (!MakeMinidumpFilename(minidump_filename))
262    return true;
263
264  if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
265                                      crashing_pid, crash_context,
266                                      kCrashContextSize)) {
267    close(signal_fd);
268    return true;
269  }
270
271  if (dump_callback_) {
272    ClientInfo info(crashing_pid, this);
273
274    dump_callback_(dump_context_, &info, &minidump_filename);
275  }
276
277  // Send the done signal to the process: it can exit now.
278  // (Closing this will make the child's sys_read unblock and return 0.)
279  close(signal_fd);
280
281  return true;
282}
283
284bool
285CrashGenerationServer::ControlEvent(short revents)
286{
287  if (POLLHUP & revents)
288    return false;
289  assert(POLLIN & revents);
290
291  char command;
292  if (read(control_pipe_in_, &command, 1))
293    return false;
294
295  switch (command) {
296  case kCommandQuit:
297    return false;
298  default:
299    assert(0);
300  }
301
302  return true;
303}
304
305bool
306CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
307{
308  GUID guid;
309  char guidString[kGUIDStringLength+1];
310
311  if (!(CreateGUID(&guid)
312        && GUIDToString(&guid, guidString, sizeof(guidString))))
313    return false;
314
315  char path[PATH_MAX];
316  snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
317
318  outFilename = path;
319  return true;
320}
321
322// static
323void*
324CrashGenerationServer::ThreadMain(void *arg)
325{
326  reinterpret_cast<CrashGenerationServer*>(arg)->Run();
327  return NULL;
328}
329
330}  // namespace google_breakpad
331