| 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> | |
| 8 #include <stdint.h> | 7 #include <stdint.h> |
| 9 #include <string.h> | 8 #include <stdlib.h> |
| 10 #include <sys/socket.h> | 9 #include <sys/socket.h> |
| 11 #include <sys/types.h> | 10 #include <sys/types.h> |
| 12 #include <sys/uio.h> | |
| 13 #include <stdlib.h> | |
| 14 #include <unistd.h> | 11 #include <unistd.h> |
| 15 | 12 |
| 16 #include <string> | 13 #include <string> |
| 17 #include <vector> | 14 #include <vector> |
| 18 | 15 |
| 19 #include "base/eintr_wrapper.h" | 16 #include "base/eintr_wrapper.h" |
| 20 #include "base/file_path.h" | 17 #include "base/file_path.h" |
| 21 #include "base/format_macros.h" | 18 #include "base/format_macros.h" |
| 19 #include "base/linux_util.h" |
| 22 #include "base/logging.h" | 20 #include "base/logging.h" |
| 23 #include "base/message_loop.h" | 21 #include "base/message_loop.h" |
| 24 #include "base/path_service.h" | 22 #include "base/path_service.h" |
| 25 #include "base/rand_util.h" | 23 #include "base/rand_util.h" |
| 26 #include "base/string_util.h" | 24 #include "base/string_util.h" |
| 27 #include "breakpad/linux/exception_handler.h" | 25 #include "breakpad/linux/exception_handler.h" |
| 28 #include "breakpad/linux/linux_dumper.h" | 26 #include "breakpad/linux/linux_dumper.h" |
| 29 #include "breakpad/linux/minidump_writer.h" | 27 #include "breakpad/linux/minidump_writer.h" |
| 30 #include "chrome/app/breakpad_linux.h" | 28 #include "chrome/app/breakpad_linux.h" |
| 31 #include "chrome/browser/chrome_thread.h" | 29 #include "chrome/browser/chrome_thread.h" |
| 32 #include "chrome/common/chrome_paths.h" | 30 #include "chrome/common/chrome_paths.h" |
| 33 | 31 |
| 34 // expected prefix of the target of the /proc/self/fd/%d link for a socket | |
| 35 static const char kSocketLinkPrefix[] = "socket:["; | |
| 36 | |
| 37 // Parse a symlink in /proc/pid/fd/$x and return the inode number of the | |
| 38 // socket. | |
| 39 // inode_out: (output) set to the inode number on success | |
| 40 // path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor) | |
| 41 // log: if true, log messages about failure details | |
| 42 static bool ProcPathGetInode(uint64_t* inode_out, const char* path, | |
| 43 bool log = false) { | |
| 44 char buf[256]; | |
| 45 const ssize_t n = readlink(path, buf, sizeof(buf) - 1); | |
| 46 if (n == -1) { | |
| 47 if (log) { | |
| 48 LOG(WARNING) << "Failed to read the inode number for a socket from /proc" | |
| 49 "(" << errno << ")"; | |
| 50 } | |
| 51 return false; | |
| 52 } | |
| 53 buf[n] = 0; | |
| 54 | |
| 55 if (memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { | |
| 56 if (log) { | |
| 57 LOG(WARNING) << "The descriptor passed from the crashing process wasn't a" | |
| 58 " UNIX domain socket."; | |
| 59 } | |
| 60 return false; | |
| 61 } | |
| 62 | |
| 63 char *endptr; | |
| 64 const unsigned long long int inode_ul = | |
| 65 strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); | |
| 66 if (*endptr != ']') | |
| 67 return false; | |
| 68 | |
| 69 if (inode_ul == ULLONG_MAX) { | |
| 70 if (log) { | |
| 71 LOG(WARNING) << "Failed to parse a socket's inode number: the number was " | |
| 72 "too large. Please report this bug: " << buf; | |
| 73 } | |
| 74 return false; | |
| 75 } | |
| 76 | |
| 77 *inode_out = inode_ul; | |
| 78 return true; | |
| 79 } | |
| 80 | |
| 81 // Return the inode number for the UNIX domain socket |fd|. | |
| 82 static bool FileDescriptorGetInode(uint64_t* inode_out, int fd) { | |
| 83 char path[256]; | |
| 84 if (snprintf(path, sizeof(path), "/proc/self/fd/%d", fd) < 0) | |
| 85 return false; | |
| 86 | |
| 87 return ProcPathGetInode(inode_out, path, true); | |
| 88 } | |
| 89 | |
| 90 // Find the process which holds the given socket, named by inode number. If | |
| 91 // multiple processes hold the socket, this function returns false. | |
| 92 static bool FindProcessHoldingSocket(pid_t* pid_out, uint64_t socket_inode) { | |
| 93 bool already_found = false; | |
| 94 | |
| 95 DIR* proc = opendir("/proc"); | |
| 96 if (!proc) { | |
| 97 LOG(WARNING) << "Cannot open /proc"; | |
| 98 return false; | |
| 99 } | |
| 100 | |
| 101 std::vector<pid_t> pids; | |
| 102 | |
| 103 struct dirent* dent; | |
| 104 while ((dent = readdir(proc))) { | |
| 105 char *endptr; | |
| 106 const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); | |
| 107 if (pid_ul == ULONG_MAX || *endptr) | |
| 108 continue; | |
| 109 pids.push_back(pid_ul); | |
| 110 } | |
| 111 closedir(proc); | |
| 112 | |
| 113 for (std::vector<pid_t>::const_iterator | |
| 114 i = pids.begin(); i != pids.end(); ++i) { | |
| 115 const pid_t current_pid = *i; | |
| 116 char buf[256]; | |
| 117 if (snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid) < 0) | |
| 118 continue; | |
| 119 DIR* fd = opendir(buf); | |
| 120 if (!fd) | |
| 121 continue; | |
| 122 | |
| 123 while ((dent = readdir(fd))) { | |
| 124 if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, | |
| 125 dent->d_name) < 0) { | |
| 126 continue; | |
| 127 } | |
| 128 | |
| 129 uint64_t fd_inode; | |
| 130 if (ProcPathGetInode(&fd_inode, buf)) { | |
| 131 if (fd_inode == socket_inode) { | |
| 132 if (already_found) { | |
| 133 closedir(fd); | |
| 134 return false; | |
| 135 } | |
| 136 | |
| 137 already_found = true; | |
| 138 *pid_out = current_pid; | |
| 139 break; | |
| 140 } | |
| 141 } | |
| 142 } | |
| 143 | |
| 144 closedir(fd); | |
| 145 } | |
| 146 | |
| 147 return already_found; | |
| 148 } | |
| 149 | |
| 150 // Since RenderCrashHandlerHostLinux is a singleton, it's only destroyed at the | 32 // Since RenderCrashHandlerHostLinux is a singleton, it's only destroyed at the |
| 151 // end of the processes lifetime, which is greater in span then the lifetime of | 33 // end of the processes lifetime, which is greater in span then the lifetime of |
| 152 // the IO message loop. | 34 // the IO message loop. |
| 153 template<> struct RunnableMethodTraits<RenderCrashHandlerHostLinux> { | 35 template<> struct RunnableMethodTraits<RenderCrashHandlerHostLinux> { |
| 154 void RetainCallee(RenderCrashHandlerHostLinux*) { } | 36 void RetainCallee(RenderCrashHandlerHostLinux*) { } |
| 155 void ReleaseCallee(RenderCrashHandlerHostLinux*) { } | 37 void ReleaseCallee(RenderCrashHandlerHostLinux*) { } |
| 156 }; | 38 }; |
| 157 | 39 |
| 158 RenderCrashHandlerHostLinux::RenderCrashHandlerHostLinux() | 40 RenderCrashHandlerHostLinux::RenderCrashHandlerHostLinux() |
| 159 : renderer_socket_(-1), | 41 : renderer_socket_(-1), |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 293 return; | 175 return; |
| 294 } | 176 } |
| 295 | 177 |
| 296 // Kernel bug workaround (broken in 2.6.30 at least): | 178 // Kernel bug workaround (broken in 2.6.30 at least): |
| 297 // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID | 179 // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID |
| 298 // namespaces. Thus |crashing_pid| might be garbage from our point of view. | 180 // namespaces. Thus |crashing_pid| might be garbage from our point of view. |
| 299 // In the future we can remove this workaround, but we have to wait a couple | 181 // In the future we can remove this workaround, but we have to wait a couple |
| 300 // of years to be sure that it's worked its way out into the world. | 182 // of years to be sure that it's worked its way out into the world. |
| 301 | 183 |
| 302 uint64_t inode_number; | 184 uint64_t inode_number; |
| 303 if (!FileDescriptorGetInode(&inode_number, signal_fd)) { | 185 if (!base::FileDescriptorGetInode(&inode_number, signal_fd)) { |
| 304 LOG(WARNING) << "Failed to get inode number for passed socket"; | 186 LOG(WARNING) << "Failed to get inode number for passed socket"; |
| 305 HANDLE_EINTR(close(signal_fd)); | 187 HANDLE_EINTR(close(signal_fd)); |
| 306 return; | 188 return; |
| 307 } | 189 } |
| 308 | 190 |
| 309 if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { | 191 if (!base::FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { |
| 310 LOG(WARNING) << "Failed to find process holding other end of crash reply " | 192 LOG(WARNING) << "Failed to find process holding other end of crash reply " |
| 311 "socket"; | 193 "socket"; |
| 312 HANDLE_EINTR(close(signal_fd)); | 194 HANDLE_EINTR(close(signal_fd)); |
| 313 return; | 195 return; |
| 314 } | 196 } |
| 315 | 197 |
| 316 bool upload = true; | 198 bool upload = true; |
| 317 FilePath dumps_path("/tmp"); | 199 FilePath dumps_path("/tmp"); |
| 318 if (getenv("CHROME_HEADLESS")) { | 200 if (getenv("CHROME_HEADLESS")) { |
| 319 upload = false; | 201 upload = false; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 354 info.guid_length = strlen(guid); | 236 info.guid_length = strlen(guid); |
| 355 info.distro = distro; | 237 info.distro = distro; |
| 356 info.distro_length = strlen(distro); | 238 info.distro_length = strlen(distro); |
| 357 info.upload = upload; | 239 info.upload = upload; |
| 358 HandleCrashDump(info); | 240 HandleCrashDump(info); |
| 359 } | 241 } |
| 360 | 242 |
| 361 void RenderCrashHandlerHostLinux::WillDestroyCurrentMessageLoop() { | 243 void RenderCrashHandlerHostLinux::WillDestroyCurrentMessageLoop() { |
| 362 file_descriptor_watcher_.StopWatchingFileDescriptor(); | 244 file_descriptor_watcher_.StopWatchingFileDescriptor(); |
| 363 } | 245 } |
| OLD | NEW |