OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/renderer_host/render_crash_handler_host_linux.h" | 5 #include "chrome/browser/renderer_host/render_crash_handler_host_linux.h" |
6 | 6 |
| 7 #include <dirent.h> |
7 #include <stdint.h> | 8 #include <stdint.h> |
| 9 #include <sys/socket.h> |
| 10 #include <sys/types.h> |
| 11 #include <sys/uio.h> |
| 12 #include <unistd.h> |
8 | 13 |
9 #include <unistd.h> | 14 #include <vector> |
10 #include <sys/uio.h> | |
11 #include <sys/socket.h> | |
12 | 15 |
13 #include "base/eintr_wrapper.h" | 16 #include "base/eintr_wrapper.h" |
14 #include "base/format_macros.h" | 17 #include "base/format_macros.h" |
15 #include "base/logging.h" | 18 #include "base/logging.h" |
16 #include "base/message_loop.h" | 19 #include "base/message_loop.h" |
17 #include "base/rand_util.h" | 20 #include "base/rand_util.h" |
18 #include "base/string_util.h" | 21 #include "base/string_util.h" |
19 #include "breakpad/linux/exception_handler.h" | 22 #include "breakpad/linux/exception_handler.h" |
20 #include "breakpad/linux/linux_dumper.h" | 23 #include "breakpad/linux/linux_dumper.h" |
21 #include "breakpad/linux/minidump_writer.h" | 24 #include "breakpad/linux/minidump_writer.h" |
22 #include "chrome/app/breakpad_linux.h" | 25 #include "chrome/app/breakpad_linux.h" |
23 #include "chrome/browser/chrome_thread.h" | 26 #include "chrome/browser/chrome_thread.h" |
24 | 27 |
| 28 // expected prefix of the target of the /proc/self/fd/%d link for a socket |
| 29 static const char kSocketLinkPrefix[] = "socket:["; |
| 30 |
| 31 // Parse a symlink in /proc/pid/fd/$x and return the inode number of the |
| 32 // socket. |
| 33 // inode_out: (output) set to the inode number on success |
| 34 // path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor) |
| 35 // log: if true, log messages about failure details |
| 36 static bool ProcPathGetInode(unsigned* inode_out, const char* path, |
| 37 bool log = false) { |
| 38 char buf[256]; |
| 39 const ssize_t n = readlink(path, buf, sizeof(buf) - 1); |
| 40 if (n == -1) { |
| 41 if (log) { |
| 42 LOG(WARNING) << "Failed to read the inode number for a socket from /proc" |
| 43 "(" << errno << ")"; |
| 44 } |
| 45 return false; |
| 46 } |
| 47 buf[n] = 0; |
| 48 |
| 49 if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { |
| 50 if (log) { |
| 51 LOG(WARNING) << "The descriptor passed from the crashing process wasn't a" |
| 52 " UNIX domain socket."; |
| 53 } |
| 54 return false; |
| 55 } |
| 56 |
| 57 char *endptr; |
| 58 const unsigned long int inode_ul = |
| 59 strtoul(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); |
| 60 if (*endptr != ']') |
| 61 return false; |
| 62 |
| 63 if (inode_ul == ULONG_MAX || inode_ul > UINT_MAX) { |
| 64 if (log) { |
| 65 LOG(WARNING) << "Failed to parse a socket's inode number: the number was " |
| 66 "too large. Please report this bug: " << buf; |
| 67 } |
| 68 return false; |
| 69 } |
| 70 |
| 71 *inode_out = inode_ul; |
| 72 return true; |
| 73 } |
| 74 |
| 75 // Return the inode number for the UNIX domain socket |fd|. |
| 76 static bool FileDescriptorGetInode(unsigned* inode_out, int fd) { |
| 77 char path[256]; |
| 78 if (snprintf(path, sizeof(path), "/proc/self/fd/%d", fd) < 0) |
| 79 return false; |
| 80 |
| 81 return ProcPathGetInode(inode_out, path, true); |
| 82 } |
| 83 |
| 84 // Find the process which holds the given socket, named by inode number. If |
| 85 // multiple processes hold the socket, this function returns false. |
| 86 static bool FindProcessHoldingSocket(pid_t* pid_out, unsigned socket_inode) { |
| 87 bool already_found = false; |
| 88 |
| 89 DIR* proc = opendir("/proc"); |
| 90 if (!proc) { |
| 91 LOG(WARNING) << "Cannot open /proc"; |
| 92 return false; |
| 93 } |
| 94 |
| 95 std::vector<pid_t> pids; |
| 96 |
| 97 struct dirent* dent; |
| 98 while ((dent = readdir(proc))) { |
| 99 char *endptr; |
| 100 const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); |
| 101 if (pid_ul == ULONG_MAX || *endptr) |
| 102 continue; |
| 103 pids.push_back(pid_ul); |
| 104 } |
| 105 closedir(proc); |
| 106 |
| 107 for (std::vector<pid_t>::const_iterator |
| 108 i = pids.begin(); i != pids.end(); ++i) { |
| 109 const pid_t current_pid = *i; |
| 110 char buf[256]; |
| 111 if (snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid) < 0) |
| 112 continue; |
| 113 DIR* fd = opendir(buf); |
| 114 if (!fd) |
| 115 continue; |
| 116 |
| 117 while ((dent = readdir(fd))) { |
| 118 if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, |
| 119 dent->d_name) < 0) { |
| 120 continue; |
| 121 } |
| 122 |
| 123 unsigned fd_inode; |
| 124 if (ProcPathGetInode(&fd_inode, buf)) { |
| 125 if (fd_inode == socket_inode) { |
| 126 if (already_found) { |
| 127 closedir(fd); |
| 128 return false; |
| 129 } |
| 130 |
| 131 already_found = true; |
| 132 *pid_out = current_pid; |
| 133 break; |
| 134 } |
| 135 } |
| 136 } |
| 137 |
| 138 closedir(fd); |
| 139 } |
| 140 |
| 141 return already_found; |
| 142 } |
| 143 |
25 // Since RenderCrashHandlerHostLinux is a singleton, it's only destroyed at the | 144 // Since RenderCrashHandlerHostLinux is a singleton, it's only destroyed at the |
26 // end of the processes lifetime, which is greater in span then the lifetime of | 145 // end of the processes lifetime, which is greater in span then the lifetime of |
27 // the IO message loop. | 146 // the IO message loop. |
28 template<> struct RunnableMethodTraits<RenderCrashHandlerHostLinux> { | 147 template<> struct RunnableMethodTraits<RenderCrashHandlerHostLinux> { |
29 static void RetainCallee(RenderCrashHandlerHostLinux*) { } | 148 static void RetainCallee(RenderCrashHandlerHostLinux*) { } |
30 static void ReleaseCallee(RenderCrashHandlerHostLinux*) { } | 149 static void ReleaseCallee(RenderCrashHandlerHostLinux*) { } |
31 }; | 150 }; |
32 | 151 |
33 RenderCrashHandlerHostLinux::RenderCrashHandlerHostLinux() | 152 RenderCrashHandlerHostLinux::RenderCrashHandlerHostLinux() |
34 : renderer_socket_(-1), | 153 : renderer_socket_(-1), |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
154 } | 273 } |
155 | 274 |
156 if (crashing_pid == -1 || signal_fd == -1) { | 275 if (crashing_pid == -1 || signal_fd == -1) { |
157 LOG(ERROR) << "Death signal message didn't contain all expected control" | 276 LOG(ERROR) << "Death signal message didn't contain all expected control" |
158 << " messages"; | 277 << " messages"; |
159 if (signal_fd) | 278 if (signal_fd) |
160 HANDLE_EINTR(close(signal_fd)); | 279 HANDLE_EINTR(close(signal_fd)); |
161 return; | 280 return; |
162 } | 281 } |
163 | 282 |
| 283 // Kernel bug workaround (broken in 2.6.30 at least): |
| 284 // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID |
| 285 // namespaces. Thus |crashing_pid| might be garbage from our point of view. |
| 286 // In the future we can remove this workaround, but we have to wait a couple |
| 287 // of years to be sure that it's worked its way out into the world. |
| 288 |
| 289 unsigned inode_number; |
| 290 if (!FileDescriptorGetInode(&inode_number, signal_fd)) { |
| 291 LOG(WARNING) << "Failed to get inode number for passed socket"; |
| 292 HANDLE_EINTR(close(signal_fd)); |
| 293 return; |
| 294 } |
| 295 |
| 296 if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { |
| 297 LOG(WARNING) << "Failed to find process holding other end of crash reply " |
| 298 "socket"; |
| 299 HANDLE_EINTR(close(signal_fd)); |
| 300 return; |
| 301 } |
| 302 |
164 const uint64 rand = base::RandUint64(); | 303 const uint64 rand = base::RandUint64(); |
165 const std::string minidump_filename = | 304 const std::string minidump_filename = |
166 StringPrintf("/tmp/chromium-renderer-minidump-%016" PRIx64 ".dmp", rand); | 305 StringPrintf("/tmp/chromium-renderer-minidump-%016" PRIx64 ".dmp", rand); |
167 if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), | 306 if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), |
168 crashing_pid, context, | 307 crashing_pid, context, |
169 kCrashContextSize)) { | 308 kCrashContextSize)) { |
170 LOG(ERROR) << "Failed to write crash dump for pid " << crashing_pid; | 309 LOG(ERROR) << "Failed to write crash dump for pid " << crashing_pid; |
171 HANDLE_EINTR(close(signal_fd)); | 310 HANDLE_EINTR(close(signal_fd)); |
172 } | 311 } |
173 | 312 |
174 // Send the done signal to the renderer: it can exit now. | 313 // Send the done signal to the renderer: it can exit now. |
175 memset(&msg, 0, sizeof(msg)); | 314 memset(&msg, 0, sizeof(msg)); |
176 iov.iov_base = const_cast<char*>("\x42"); | 315 iov.iov_base = const_cast<char*>("\x42"); |
177 iov.iov_len = 1; | 316 iov.iov_len = 1; |
178 msg.msg_iov = &iov; | 317 msg.msg_iov = &iov; |
179 msg.msg_iovlen = 1; | 318 msg.msg_iovlen = 1; |
180 | 319 |
181 HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); | 320 HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); |
182 HANDLE_EINTR(close(signal_fd)); | 321 HANDLE_EINTR(close(signal_fd)); |
183 | 322 |
184 UploadCrashDump(minidump_filename.c_str(), | 323 UploadCrashDump(minidump_filename.c_str(), |
185 "renderer", 8, | 324 "renderer", 8, |
186 crash_url, crash_url_len, | 325 crash_url, crash_url_len, |
187 guid, kGuidSize); | 326 guid, kGuidSize); |
188 } | 327 } |
189 | 328 |
190 void RenderCrashHandlerHostLinux::WillDestroyCurrentMessageLoop() { | 329 void RenderCrashHandlerHostLinux::WillDestroyCurrentMessageLoop() { |
191 file_descriptor_watcher_.StopWatchingFileDescriptor(); | 330 file_descriptor_watcher_.StopWatchingFileDescriptor(); |
192 } | 331 } |
OLD | NEW |