Line data Source code
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 "linux/crash_generation/crash_generation_server.h"
45 : #include "linux/crash_generation/client_info.h"
46 : #include "linux/handler/exception_handler.h"
47 : #include "linux/handler/guid_generator.h"
48 : #include "linux/minidump_writer/minidump_writer.h"
49 : #include "common/linux/eintr_wrapper.h"
50 : #include "common/linux/safe_readlink.h"
51 :
52 : static const char kCommandQuit = 'x';
53 :
54 : namespace google_breakpad {
55 :
56 0 : 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 0 : 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 0 : started_(false)
71 : {
72 0 : if (dump_path)
73 0 : dump_dir_ = *dump_path;
74 : else
75 0 : dump_dir_ = "/tmp";
76 0 : }
77 :
78 0 : CrashGenerationServer::~CrashGenerationServer()
79 : {
80 0 : if (started_)
81 0 : Stop();
82 0 : }
83 :
84 : bool
85 0 : CrashGenerationServer::Start()
86 : {
87 0 : if (started_ || 0 > server_fd_)
88 0 : return false;
89 :
90 : int control_pipe[2];
91 0 : if (pipe(control_pipe))
92 0 : return false;
93 :
94 0 : if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
95 0 : return false;
96 0 : if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
97 0 : return false;
98 :
99 0 : if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
100 0 : return false;
101 :
102 0 : control_pipe_in_ = control_pipe[0];
103 0 : control_pipe_out_ = control_pipe[1];
104 :
105 0 : if (pthread_create(&thread_, NULL,
106 : ThreadMain, reinterpret_cast<void*>(this)))
107 0 : return false;
108 :
109 0 : started_ = true;
110 0 : return true;
111 : }
112 :
113 : void
114 0 : CrashGenerationServer::Stop()
115 : {
116 0 : assert(pthread_self() != thread_);
117 :
118 0 : if (!started_)
119 0 : return;
120 :
121 0 : HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
122 :
123 : void* dummy;
124 0 : pthread_join(thread_, &dummy);
125 :
126 0 : close(control_pipe_in_);
127 0 : close(control_pipe_out_);
128 :
129 0 : started_ = false;
130 : }
131 :
132 : //static
133 : bool
134 0 : CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
135 : {
136 : int fds[2];
137 :
138 0 : if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
139 0 : return false;
140 :
141 : static const int on = 1;
142 : // Enable passcred on the server end of the socket
143 0 : if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
144 0 : return false;
145 :
146 0 : if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
147 0 : return false;
148 0 : if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
149 0 : return false;
150 :
151 0 : *client_fd = fds[0];
152 0 : *server_fd = fds[1];
153 0 : return true;
154 : }
155 :
156 : // The following methods/functions execute on the server thread
157 :
158 : void
159 0 : CrashGenerationServer::Run()
160 : {
161 : struct pollfd pollfds[2];
162 0 : memset(&pollfds, 0, sizeof(pollfds));
163 :
164 0 : pollfds[0].fd = server_fd_;
165 0 : pollfds[0].events = POLLIN;
166 :
167 0 : pollfds[1].fd = control_pipe_in_;
168 0 : pollfds[1].events = POLLIN;
169 :
170 : while (true) {
171 : // infinite timeout
172 0 : int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
173 0 : if (-1 == nevents) {
174 0 : if (EINTR == errno) {
175 0 : continue;
176 : } else {
177 0 : return;
178 : }
179 : }
180 :
181 0 : if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
182 0 : return;
183 :
184 0 : if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
185 0 : return;
186 0 : }
187 : }
188 :
189 : bool
190 0 : CrashGenerationServer::ClientEvent(short revents)
191 : {
192 0 : if (POLLHUP & revents)
193 0 : return false;
194 0 : 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 0 : struct msghdr msg = {0};
209 : struct iovec iov[1];
210 : char crash_context[kCrashContextSize];
211 : char control[kControlMsgSize];
212 0 : const ssize_t expected_msg_size = sizeof(crash_context);
213 :
214 0 : iov[0].iov_base = crash_context;
215 0 : iov[0].iov_len = sizeof(crash_context);
216 0 : msg.msg_iov = iov;
217 0 : msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
218 0 : msg.msg_control = control;
219 0 : msg.msg_controllen = kControlMsgSize;
220 :
221 0 : const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
222 0 : if (msg_size != expected_msg_size)
223 0 : return true;
224 :
225 0 : if (msg.msg_controllen != kControlMsgSize ||
226 0 : msg.msg_flags & ~MSG_TRUNC)
227 0 : return true;
228 :
229 : // Walk the control payload and extract the file descriptor and validated pid.
230 0 : pid_t crashing_pid = -1;
231 0 : int signal_fd = -1;
232 0 : for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
233 : hdr = CMSG_NXTHDR(&msg, hdr)) {
234 0 : if (hdr->cmsg_level != SOL_SOCKET)
235 0 : continue;
236 0 : if (hdr->cmsg_type == SCM_RIGHTS) {
237 0 : const unsigned len = hdr->cmsg_len -
238 0 : (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
239 0 : assert(len % sizeof(int) == 0u);
240 0 : const unsigned num_fds = len / sizeof(int);
241 0 : 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 0 : for (unsigned i = 0; i < num_fds; ++i)
245 0 : close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
246 0 : return true;
247 : } else {
248 0 : signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
249 : }
250 0 : } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
251 : const struct ucred *cred =
252 0 : reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
253 0 : crashing_pid = cred->pid;
254 : }
255 : }
256 :
257 0 : if (crashing_pid == -1 || signal_fd == -1) {
258 0 : if (signal_fd)
259 0 : close(signal_fd);
260 0 : return true;
261 : }
262 :
263 0 : string minidump_filename;
264 0 : if (!MakeMinidumpFilename(minidump_filename))
265 0 : return true;
266 :
267 0 : if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
268 : crashing_pid, crash_context,
269 : kCrashContextSize)) {
270 0 : close(signal_fd);
271 0 : return true;
272 : }
273 :
274 0 : if (dump_callback_) {
275 0 : ClientInfo info(crashing_pid, this);
276 :
277 0 : 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 0 : close(signal_fd);
283 :
284 0 : return true;
285 : }
286 :
287 : bool
288 0 : CrashGenerationServer::ControlEvent(short revents)
289 : {
290 0 : if (POLLHUP & revents)
291 0 : return false;
292 0 : assert(POLLIN & revents);
293 :
294 : char command;
295 0 : if (read(control_pipe_in_, &command, 1))
296 0 : return false;
297 :
298 0 : switch (command) {
299 : case kCommandQuit:
300 0 : return false;
301 : default:
302 0 : assert(0);
303 : }
304 :
305 : return true;
306 : }
307 :
308 : bool
309 0 : CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
310 : {
311 : GUID guid;
312 : char guidString[kGUIDStringLength+1];
313 :
314 0 : if (!(CreateGUID(&guid)
315 0 : && GUIDToString(&guid, guidString, sizeof(guidString))))
316 0 : return false;
317 :
318 : char path[PATH_MAX];
319 0 : snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
320 :
321 0 : outFilename = path;
322 0 : return true;
323 : }
324 :
325 : // static
326 : void*
327 0 : CrashGenerationServer::ThreadMain(void *arg)
328 : {
329 0 : reinterpret_cast<CrashGenerationServer*>(arg)->Run();
330 0 : return NULL;
331 : }
332 :
333 : } // namespace google_breakpad
|