• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/crash/browser/crash_handler_host_linux.h"
6 
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <sys/socket.h>
10 #include <sys/syscall.h>
11 #include <unistd.h>
12 
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/files/scoped_file.h"
18 #include "base/format_macros.h"
19 #include "base/linux_util.h"
20 #include "base/logging.h"
21 #include "base/message_loop/message_loop.h"
22 #include "base/path_service.h"
23 #include "base/posix/eintr_wrapper.h"
24 #include "base/rand_util.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/threading/thread.h"
28 #include "breakpad/src/client/linux/handler/exception_handler.h"
29 #include "breakpad/src/client/linux/minidump_writer/linux_dumper.h"
30 #include "breakpad/src/client/linux/minidump_writer/minidump_writer.h"
31 #include "components/crash/app/breakpad_linux_impl.h"
32 #include "content/public/browser/browser_thread.h"
33 
34 #if defined(OS_ANDROID) && !defined(__LP64__)
35 #include <sys/linux-syscalls.h>
36 
37 #define SYS_read __NR_read
38 #endif
39 
40 using content::BrowserThread;
41 using google_breakpad::ExceptionHandler;
42 
43 namespace breakpad {
44 
45 namespace {
46 
47 const size_t kNumFDs = 1;
48 // The length of the control message:
49 const size_t kControlMsgSize =
50     CMSG_SPACE(kNumFDs * sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
51 // The length of the regular payload:
52 const size_t kCrashContextSize = sizeof(ExceptionHandler::CrashContext);
53 
54 // Handles the crash dump and frees the allocated BreakpadInfo struct.
CrashDumpTask(CrashHandlerHostLinux * handler,scoped_ptr<BreakpadInfo> info)55 void CrashDumpTask(CrashHandlerHostLinux* handler,
56                    scoped_ptr<BreakpadInfo> info) {
57   if (handler->IsShuttingDown() && info->upload) {
58     base::DeleteFile(base::FilePath(info->filename), false);
59 #if defined(ADDRESS_SANITIZER)
60     base::DeleteFile(base::FilePath(info->log_filename), false);
61 #endif
62     return;
63   }
64 
65   HandleCrashDump(*info);
66   delete[] info->filename;
67 #if defined(ADDRESS_SANITIZER)
68   delete[] info->log_filename;
69   delete[] info->asan_report_str;
70 #endif
71   delete[] info->process_type;
72   delete[] info->distro;
73   delete info->crash_keys;
74 }
75 
76 }  // namespace
77 
78 // Since instances of CrashHandlerHostLinux are leaked, they are only destroyed
79 // at the end of the processes lifetime, which is greater in span than the
80 // lifetime of the IO message loop. Thus, all calls to base::Bind() use
81 // non-refcounted pointers.
82 
CrashHandlerHostLinux(const std::string & process_type,const base::FilePath & dumps_path,bool upload)83 CrashHandlerHostLinux::CrashHandlerHostLinux(const std::string& process_type,
84                                              const base::FilePath& dumps_path,
85                                              bool upload)
86     : process_type_(process_type),
87       dumps_path_(dumps_path),
88       upload_(upload),
89       shutting_down_(false),
90       worker_pool_token_(BrowserThread::GetBlockingPool()->GetSequenceToken()) {
91   int fds[2];
92   // We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the process from
93   // sending datagrams to other sockets on the system. The sandbox may prevent
94   // the process from calling socket() to create new sockets, but it'll still
95   // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send
96   // a datagram to any (abstract) socket on the same system. With
97   // SOCK_SEQPACKET, this is prevented.
98   CHECK_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
99   static const int on = 1;
100 
101   // Enable passcred on the server end of the socket
102   CHECK_EQ(0, setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)));
103 
104   process_socket_ = fds[0];
105   browser_socket_ = fds[1];
106 
107   BrowserThread::PostTask(
108       BrowserThread::IO, FROM_HERE,
109       base::Bind(&CrashHandlerHostLinux::Init, base::Unretained(this)));
110 }
111 
~CrashHandlerHostLinux()112 CrashHandlerHostLinux::~CrashHandlerHostLinux() {
113   close(process_socket_);
114   close(browser_socket_);
115 }
116 
StartUploaderThread()117 void CrashHandlerHostLinux::StartUploaderThread() {
118   uploader_thread_.reset(
119       new base::Thread(process_type_ + "_crash_uploader"));
120   uploader_thread_->Start();
121 }
122 
Init()123 void CrashHandlerHostLinux::Init() {
124   base::MessageLoopForIO* ml = base::MessageLoopForIO::current();
125   CHECK(ml->WatchFileDescriptor(
126       browser_socket_, true /* persistent */,
127       base::MessageLoopForIO::WATCH_READ,
128       &file_descriptor_watcher_, this));
129   ml->AddDestructionObserver(this);
130 }
131 
OnFileCanWriteWithoutBlocking(int fd)132 void CrashHandlerHostLinux::OnFileCanWriteWithoutBlocking(int fd) {
133   NOTREACHED();
134 }
135 
OnFileCanReadWithoutBlocking(int fd)136 void CrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) {
137   DCHECK_EQ(browser_socket_, fd);
138 
139   // A process has crashed and has signaled us by writing a datagram
140   // to the death signal socket. The datagram contains the crash context needed
141   // for writing the minidump as well as a file descriptor and a credentials
142   // block so that they can't lie about their pid.
143   //
144   // The message sender is in components/crash/app/breakpad_linux.cc.
145 
146   struct msghdr msg = {0};
147   struct iovec iov[kCrashIovSize];
148 
149   scoped_ptr<char[]> crash_context(new char[kCrashContextSize]);
150 #if defined(ADDRESS_SANITIZER)
151   scoped_ptr<char[]> asan_report(new char[kMaxAsanReportSize + 1]);
152 #endif
153 
154   scoped_ptr<CrashKeyStorage> crash_keys(new CrashKeyStorage);
155   google_breakpad::SerializedNonAllocatingMap* serialized_crash_keys;
156   size_t crash_keys_size = crash_keys->Serialize(
157       const_cast<const google_breakpad::SerializedNonAllocatingMap**>(
158           &serialized_crash_keys));
159 
160   char* tid_buf_addr = NULL;
161   int tid_fd = -1;
162   uint64_t uptime;
163   size_t oom_size;
164   char control[kControlMsgSize];
165   const ssize_t expected_msg_size =
166       kCrashContextSize +
167       sizeof(tid_buf_addr) + sizeof(tid_fd) +
168       sizeof(uptime) +
169 #if defined(ADDRESS_SANITIZER)
170       kMaxAsanReportSize + 1 +
171 #endif
172       sizeof(oom_size) +
173       crash_keys_size;
174   iov[0].iov_base = crash_context.get();
175   iov[0].iov_len = kCrashContextSize;
176   iov[1].iov_base = &tid_buf_addr;
177   iov[1].iov_len = sizeof(tid_buf_addr);
178   iov[2].iov_base = &tid_fd;
179   iov[2].iov_len = sizeof(tid_fd);
180   iov[3].iov_base = &uptime;
181   iov[3].iov_len = sizeof(uptime);
182   iov[4].iov_base = &oom_size;
183   iov[4].iov_len = sizeof(oom_size);
184   iov[5].iov_base = serialized_crash_keys;
185   iov[5].iov_len = crash_keys_size;
186 #if !defined(ADDRESS_SANITIZER)
187   COMPILE_ASSERT(5 == kCrashIovSize - 1, Incorrect_Number_Of_Iovec_Members);
188 #else
189   iov[6].iov_base = asan_report.get();
190   iov[6].iov_len = kMaxAsanReportSize + 1;
191   COMPILE_ASSERT(6 == kCrashIovSize - 1, Incorrect_Number_Of_Iovec_Members);
192 #endif
193   msg.msg_iov = iov;
194   msg.msg_iovlen = kCrashIovSize;
195   msg.msg_control = control;
196   msg.msg_controllen = kControlMsgSize;
197 
198   const ssize_t msg_size = HANDLE_EINTR(recvmsg(browser_socket_, &msg, 0));
199   if (msg_size < 0) {
200     LOG(ERROR) << "Error reading from death signal socket. Crash dumping"
201                << " is disabled."
202                << " msg_size:" << msg_size
203                << " errno:" << errno;
204     file_descriptor_watcher_.StopWatchingFileDescriptor();
205     return;
206   }
207   const bool bad_message = (msg_size != expected_msg_size ||
208                             msg.msg_controllen != kControlMsgSize ||
209                             msg.msg_flags & ~MSG_TRUNC);
210   base::ScopedFD signal_fd;
211   pid_t crashing_pid = -1;
212   if (msg.msg_controllen > 0) {
213     // Walk the control payload and extract the file descriptor and
214     // validated pid.
215     for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
216          hdr = CMSG_NXTHDR(&msg, hdr)) {
217       if (hdr->cmsg_level != SOL_SOCKET)
218         continue;
219       if (hdr->cmsg_type == SCM_RIGHTS) {
220         const size_t len = hdr->cmsg_len -
221             (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
222         DCHECK_EQ(0U, len % sizeof(int));
223         const size_t num_fds = len / sizeof(int);
224         if (num_fds != kNumFDs) {
225           // A nasty process could try and send us too many descriptors and
226           // force a leak.
227           LOG(ERROR) << "Death signal contained wrong number of descriptors;"
228                      << " num_fds:" << num_fds;
229           for (size_t i = 0; i < num_fds; ++i)
230             close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
231           return;
232         }
233         DCHECK(!signal_fd.is_valid());
234         int fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
235         DCHECK_GE(fd, 0);  // The kernel should never send a negative fd.
236         signal_fd.reset(fd);
237       } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
238         DCHECK_EQ(-1, crashing_pid);
239         const struct ucred *cred =
240             reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
241         crashing_pid = cred->pid;
242       }
243     }
244   }
245 
246   if (bad_message) {
247     LOG(ERROR) << "Received death signal message with the wrong size;"
248                << " msg.msg_controllen:" << msg.msg_controllen
249                << " msg.msg_flags:" << msg.msg_flags
250                << " kCrashContextSize:" << kCrashContextSize
251                << " kControlMsgSize:" << kControlMsgSize;
252     return;
253   }
254   if (crashing_pid == -1 || !signal_fd.is_valid()) {
255     LOG(ERROR) << "Death signal message didn't contain all expected control"
256                << " messages";
257     return;
258   }
259 
260   // The crashing TID set inside the compromised context via
261   // sys_gettid() in ExceptionHandler::HandleSignal might be wrong (if
262   // the kernel supports PID namespacing) and may need to be
263   // translated.
264   //
265   // We expect the crashing thread to be in sys_read(), waiting for us to
266   // write to |signal_fd|. Most newer kernels where we have the different pid
267   // namespaces also have /proc/[pid]/syscall, so we can look through
268   // |actual_crashing_pid|'s thread group and find the thread that's in the
269   // read syscall with the right arguments.
270 
271   std::string expected_syscall_data;
272   // /proc/[pid]/syscall is formatted as follows:
273   // syscall_number arg1 ... arg6 sp pc
274   // but we just check syscall_number through arg3.
275   base::StringAppendF(&expected_syscall_data, "%d 0x%x %p 0x1 ",
276                       SYS_read, tid_fd, tid_buf_addr);
277   bool syscall_supported = false;
278   pid_t crashing_tid =
279       base::FindThreadIDWithSyscall(crashing_pid,
280                                     expected_syscall_data,
281                                     &syscall_supported);
282   if (crashing_tid == -1) {
283     // We didn't find the thread we want. Maybe it didn't reach
284     // sys_read() yet or the thread went away.  We'll just take a
285     // guess here and assume the crashing thread is the thread group
286     // leader.  If procfs syscall is not supported by the kernel, then
287     // we assume the kernel also does not support TID namespacing and
288     // trust the TID passed by the crashing process.
289     LOG(WARNING) << "Could not translate tid - assuming crashing thread is "
290         "thread group leader; syscall_supported=" << syscall_supported;
291     crashing_tid = crashing_pid;
292   }
293 
294   ExceptionHandler::CrashContext* bad_context =
295       reinterpret_cast<ExceptionHandler::CrashContext*>(crash_context.get());
296   bad_context->tid = crashing_tid;
297 
298   scoped_ptr<BreakpadInfo> info(new BreakpadInfo);
299 
300   info->fd = -1;
301   info->process_type_length = process_type_.length();
302   // Freed in CrashDumpTask().
303   char* process_type_str = new char[info->process_type_length + 1];
304   process_type_.copy(process_type_str, info->process_type_length);
305   process_type_str[info->process_type_length] = '\0';
306   info->process_type = process_type_str;
307 
308   // Memory released from scoped_ptrs below are also freed in CrashDumpTask().
309   info->crash_keys = crash_keys.release();
310 #if defined(ADDRESS_SANITIZER)
311   asan_report[kMaxAsanReportSize] = '\0';
312   info->asan_report_str = asan_report.release();
313   info->asan_report_length = strlen(info->asan_report_str);
314 #endif
315 
316   info->process_start_time = uptime;
317   info->oom_size = oom_size;
318 #if defined(OS_ANDROID)
319   // Nothing gets uploaded in android.
320   info->upload = false;
321 #else
322   info->upload = upload_;
323 #endif
324 
325 
326   BrowserThread::GetBlockingPool()->PostSequencedWorkerTask(
327       worker_pool_token_,
328       FROM_HERE,
329       base::Bind(&CrashHandlerHostLinux::WriteDumpFile,
330                  base::Unretained(this),
331                  base::Passed(&info),
332                  base::Passed(&crash_context),
333                  crashing_pid,
334                  signal_fd.release()));
335 }
336 
WriteDumpFile(scoped_ptr<BreakpadInfo> info,scoped_ptr<char[]> crash_context,pid_t crashing_pid,int signal_fd)337 void CrashHandlerHostLinux::WriteDumpFile(scoped_ptr<BreakpadInfo> info,
338                                           scoped_ptr<char[]> crash_context,
339                                           pid_t crashing_pid,
340                                           int signal_fd) {
341   DCHECK(BrowserThread::GetBlockingPool()->IsRunningSequenceOnCurrentThread(
342       worker_pool_token_));
343 
344   // Set |info->distro| here because base::GetLinuxDistro() needs to run on a
345   // blocking thread.
346   std::string distro = base::GetLinuxDistro();
347   info->distro_length = distro.length();
348   // Freed in CrashDumpTask().
349   char* distro_str = new char[info->distro_length + 1];
350   distro.copy(distro_str, info->distro_length);
351   distro_str[info->distro_length] = '\0';
352   info->distro = distro_str;
353 
354   base::FilePath dumps_path("/tmp");
355   PathService::Get(base::DIR_TEMP, &dumps_path);
356   if (!info->upload)
357     dumps_path = dumps_path_;
358   const std::string minidump_filename =
359       base::StringPrintf("%s/chromium-%s-minidump-%016" PRIx64 ".dmp",
360                          dumps_path.value().c_str(),
361                          process_type_.c_str(),
362                          base::RandUint64());
363 
364   if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
365                                       kMaxMinidumpFileSize,
366                                       crashing_pid,
367                                       crash_context.get(),
368                                       kCrashContextSize,
369                                       google_breakpad::MappingList(),
370                                       google_breakpad::AppMemoryList())) {
371     LOG(ERROR) << "Failed to write crash dump for pid " << crashing_pid;
372   }
373 #if defined(ADDRESS_SANITIZER)
374   // Create a temporary file holding the AddressSanitizer report.
375   const base::FilePath log_path =
376       base::FilePath(minidump_filename).ReplaceExtension("log");
377   base::WriteFile(log_path, info->asan_report_str, info->asan_report_length);
378 #endif
379 
380   // Freed in CrashDumpTask().
381   char* minidump_filename_str = new char[minidump_filename.length() + 1];
382   minidump_filename.copy(minidump_filename_str, minidump_filename.length());
383   minidump_filename_str[minidump_filename.length()] = '\0';
384   info->filename = minidump_filename_str;
385 #if defined(ADDRESS_SANITIZER)
386   // Freed in CrashDumpTask().
387   char* minidump_log_filename_str = new char[minidump_filename.length() + 1];
388   minidump_filename.copy(minidump_log_filename_str, minidump_filename.length());
389   memcpy(minidump_log_filename_str + minidump_filename.length() - 3, "log", 3);
390   minidump_log_filename_str[minidump_filename.length()] = '\0';
391   info->log_filename = minidump_log_filename_str;
392 #endif
393   info->pid = crashing_pid;
394 
395   BrowserThread::PostTask(
396       BrowserThread::IO, FROM_HERE,
397       base::Bind(&CrashHandlerHostLinux::QueueCrashDumpTask,
398                  base::Unretained(this),
399                  base::Passed(&info),
400                  signal_fd));
401 }
402 
QueueCrashDumpTask(scoped_ptr<BreakpadInfo> info,int signal_fd)403 void CrashHandlerHostLinux::QueueCrashDumpTask(scoped_ptr<BreakpadInfo> info,
404                                                int signal_fd) {
405   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
406 
407   // Send the done signal to the process: it can exit now.
408   struct msghdr msg = {0};
409   struct iovec done_iov;
410   done_iov.iov_base = const_cast<char*>("\x42");
411   done_iov.iov_len = 1;
412   msg.msg_iov = &done_iov;
413   msg.msg_iovlen = 1;
414 
415   HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL));
416   close(signal_fd);
417 
418   uploader_thread_->message_loop()->PostTask(
419       FROM_HERE,
420       base::Bind(&CrashDumpTask, base::Unretained(this), base::Passed(&info)));
421 }
422 
WillDestroyCurrentMessageLoop()423 void CrashHandlerHostLinux::WillDestroyCurrentMessageLoop() {
424   file_descriptor_watcher_.StopWatchingFileDescriptor();
425 
426   // If we are quitting and there are crash dumps in the queue, turn them into
427   // no-ops.
428   shutting_down_ = true;
429   uploader_thread_->Stop();
430 }
431 
IsShuttingDown() const432 bool CrashHandlerHostLinux::IsShuttingDown() const {
433   return shutting_down_;
434 }
435 
436 }  // namespace breakpad
437