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