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 |