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 close(control_pipe_in_);
127 close(control_pipe_out_);
128
129 started_ = false;
130}
131
132//static
133bool
134CrashGenerationServer::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
158void
159CrashGenerationServer::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
189bool
190CrashGenerationServer::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
287bool
288CrashGenerationServer::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
308bool
309CrashGenerationServer::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
326void*
327CrashGenerationServer::ThreadMain(void* arg)
328{
329 reinterpret_cast<CrashGenerationServer*>(arg)->Run();
330 return NULL;
331}
332
333} // namespace google_breakpad
334