xref: /aosp_15_r20/external/google-breakpad/src/client/linux/crash_generation/crash_generation_server.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2010 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 //     * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>  // Must come first
31 #endif
32 
33 #include <assert.h>
34 #include <dirent.h>
35 #include <fcntl.h>
36 #include <limits.h>
37 #include <poll.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <unistd.h>
44 
45 #include <vector>
46 
47 #include "client/linux/crash_generation/crash_generation_server.h"
48 #include "client/linux/crash_generation/client_info.h"
49 #include "client/linux/handler/exception_handler.h"
50 #include "client/linux/minidump_writer/minidump_writer.h"
51 #include "common/linux/eintr_wrapper.h"
52 #include "common/linux/guid_creator.h"
53 #include "common/linux/safe_readlink.h"
54 
55 static const char kCommandQuit = 'x';
56 
57 namespace google_breakpad {
58 
CrashGenerationServer(const int listen_fd,OnClientDumpRequestCallback dump_callback,void * dump_context,OnClientExitingCallback exit_callback,void * exit_context,bool generate_dumps,const string * dump_path)59 CrashGenerationServer::CrashGenerationServer(
60   const int listen_fd,
61   OnClientDumpRequestCallback dump_callback,
62   void* dump_context,
63   OnClientExitingCallback exit_callback,
64   void* exit_context,
65   bool generate_dumps,
66   const string* dump_path) :
67     server_fd_(listen_fd),
68     dump_callback_(dump_callback),
69     dump_context_(dump_context),
70     exit_callback_(exit_callback),
71     exit_context_(exit_context),
72     generate_dumps_(generate_dumps),
73     started_(false)
74 {
75   if (dump_path)
76     dump_dir_ = *dump_path;
77   else
78     dump_dir_ = "/tmp";
79 }
80 
~CrashGenerationServer()81 CrashGenerationServer::~CrashGenerationServer()
82 {
83   if (started_)
84     Stop();
85 }
86 
87 bool
Start()88 CrashGenerationServer::Start()
89 {
90   if (started_ || 0 > server_fd_)
91     return false;
92 
93   int control_pipe[2];
94   if (pipe(control_pipe))
95     return false;
96 
97   if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
98     return false;
99   if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
100     return false;
101 
102   if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
103     return false;
104 
105   control_pipe_in_ = control_pipe[0];
106   control_pipe_out_ = control_pipe[1];
107 
108   if (pthread_create(&thread_, NULL,
109                      ThreadMain, reinterpret_cast<void*>(this)))
110     return false;
111 
112   started_ = true;
113   return true;
114 }
115 
116 void
Stop()117 CrashGenerationServer::Stop()
118 {
119   assert(pthread_self() != thread_);
120 
121   if (!started_)
122     return;
123 
124   HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
125 
126   void* dummy;
127   pthread_join(thread_, &dummy);
128 
129   close(control_pipe_in_);
130   close(control_pipe_out_);
131 
132   started_ = false;
133 }
134 
135 //static
136 bool
CreateReportChannel(int * server_fd,int * client_fd)137 CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
138 {
139   int fds[2];
140 
141   if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
142     return false;
143 
144   static const int on = 1;
145   // Enable passcred on the server end of the socket
146   if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
147     return false;
148 
149   if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
150     return false;
151   if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
152     return false;
153 
154   *client_fd = fds[0];
155   *server_fd = fds[1];
156   return true;
157 }
158 
159 // The following methods/functions execute on the server thread
160 
161 void
Run()162 CrashGenerationServer::Run()
163 {
164   struct pollfd pollfds[2];
165   memset(&pollfds, 0, sizeof(pollfds));
166 
167   pollfds[0].fd = server_fd_;
168   pollfds[0].events = POLLIN;
169 
170   pollfds[1].fd = control_pipe_in_;
171   pollfds[1].events = POLLIN;
172 
173   while (true) {
174     // infinite timeout
175     int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
176     if (-1 == nevents) {
177       if (EINTR == errno) {
178         continue;
179       } else {
180         return;
181       }
182     }
183 
184     if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
185       return;
186 
187     if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
188       return;
189   }
190 }
191 
192 bool
ClientEvent(short revents)193 CrashGenerationServer::ClientEvent(short revents)
194 {
195   if (POLLHUP & revents)
196     return false;
197   assert(POLLIN & revents);
198 
199   // A process has crashed and has signaled us by writing a datagram
200   // to the death signal socket. The datagram contains the crash context needed
201   // for writing the minidump as well as a file descriptor and a credentials
202   // block so that they can't lie about their pid.
203 
204   // The length of the control message:
205   static const unsigned kControlMsgSize =
206       CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
207   // The length of the regular payload:
208   static const unsigned kCrashContextSize =
209       sizeof(google_breakpad::ExceptionHandler::CrashContext);
210 
211   struct msghdr msg = {0};
212   struct iovec iov[1];
213   char crash_context[kCrashContextSize];
214   char control[kControlMsgSize];
215   const ssize_t expected_msg_size = sizeof(crash_context);
216 
217   iov[0].iov_base = crash_context;
218   iov[0].iov_len = sizeof(crash_context);
219   msg.msg_iov = iov;
220   msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
221   msg.msg_control = control;
222   msg.msg_controllen = kControlMsgSize;
223 
224   const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
225   if (msg_size != expected_msg_size)
226     return true;
227 
228   if (msg.msg_controllen != kControlMsgSize ||
229       msg.msg_flags & ~MSG_TRUNC)
230     return true;
231 
232   // Walk the control payload and extract the file descriptor and validated pid.
233   pid_t crashing_pid = -1;
234   int signal_fd = -1;
235   for (struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); hdr;
236        hdr = CMSG_NXTHDR(&msg, hdr)) {
237     if (hdr->cmsg_level != SOL_SOCKET)
238       continue;
239     if (hdr->cmsg_type == SCM_RIGHTS) {
240       const unsigned len = hdr->cmsg_len -
241           (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
242       assert(len % sizeof(int) == 0u);
243       const unsigned num_fds = len / sizeof(int);
244       if (num_fds > 1 || num_fds == 0) {
245         // A nasty process could try and send us too many descriptors and
246         // force a leak.
247         for (unsigned i = 0; i < num_fds; ++i)
248           close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
249         return true;
250       } else {
251         signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
252       }
253     } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
254       const struct ucred* cred =
255           reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
256       crashing_pid = cred->pid;
257     }
258   }
259 
260   if (crashing_pid == -1 || signal_fd == -1) {
261     if (signal_fd != -1)
262       close(signal_fd);
263     return true;
264   }
265 
266   string minidump_filename;
267   if (!MakeMinidumpFilename(minidump_filename))
268     return true;
269 
270   if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
271                                       crashing_pid, crash_context,
272                                       kCrashContextSize)) {
273     close(signal_fd);
274     return true;
275   }
276 
277   if (dump_callback_) {
278     ClientInfo info(crashing_pid, this);
279 
280     dump_callback_(dump_context_, &info, &minidump_filename);
281   }
282 
283   // Send the done signal to the process: it can exit now.
284   // (Closing this will make the child's sys_read unblock and return 0.)
285   close(signal_fd);
286 
287   return true;
288 }
289 
290 bool
ControlEvent(short revents)291 CrashGenerationServer::ControlEvent(short revents)
292 {
293   if (POLLHUP & revents)
294     return false;
295   assert(POLLIN & revents);
296 
297   char command;
298   if (read(control_pipe_in_, &command, 1))
299     return false;
300 
301   switch (command) {
302   case kCommandQuit:
303     return false;
304   default:
305     assert(0);
306   }
307 
308   return true;
309 }
310 
311 bool
MakeMinidumpFilename(string & outFilename)312 CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
313 {
314   GUID guid;
315   char guidString[kGUIDStringLength+1];
316 
317   if (!(CreateGUID(&guid)
318         && GUIDToString(&guid, guidString, sizeof(guidString))))
319     return false;
320 
321   char path[PATH_MAX];
322   snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
323 
324   outFilename = path;
325   return true;
326 }
327 
328 // static
329 void*
ThreadMain(void * arg)330 CrashGenerationServer::ThreadMain(void* arg)
331 {
332   reinterpret_cast<CrashGenerationServer*>(arg)->Run();
333   return NULL;
334 }
335 
336 }  // namespace google_breakpad
337