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 | |
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 | |
78 | CrashGenerationServer::~CrashGenerationServer() |
79 | { |
80 | if (started_) |
81 | Stop(); |
82 | } |
83 | |
84 | bool |
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 |
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 |
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 |
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 |
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 |
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 |
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* |
327 | CrashGenerationServer::ThreadMain(void* arg) |
328 | { |
329 | reinterpret_cast<CrashGenerationServer*>(arg)->Run(); |
330 | return NULL; |
331 | } |
332 | |
333 | } // namespace google_breakpad |
334 | |