Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: chrome/browser/renderer_host/render_crash_handler_host_linux.cc

Issue 371015: Linux: Catch plugin crashes. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/renderer_host/render_crash_handler_host_linux.h"
6
7 #include <stdint.h>
8 #include <stdlib.h>
9 #include <sys/socket.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #include <string>
14 #include <vector>
15
16 #include "base/eintr_wrapper.h"
17 #include "base/file_path.h"
18 #include "base/format_macros.h"
19 #include "base/linux_util.h"
20 #include "base/logging.h"
21 #include "base/message_loop.h"
22 #include "base/path_service.h"
23 #include "base/rand_util.h"
24 #include "base/string_util.h"
25 #include "breakpad/linux/exception_handler.h"
26 #include "breakpad/linux/linux_dumper.h"
27 #include "breakpad/linux/minidump_writer.h"
28 #include "chrome/app/breakpad_linux.h"
29 #include "chrome/browser/chrome_thread.h"
30 #include "chrome/common/chrome_paths.h"
31
32 // Since RenderCrashHandlerHostLinux is a singleton, it's only destroyed at the
33 // end of the processes lifetime, which is greater in span then the lifetime of
34 // the IO message loop.
35 template<> struct RunnableMethodTraits<RenderCrashHandlerHostLinux> {
36 void RetainCallee(RenderCrashHandlerHostLinux*) { }
37 void ReleaseCallee(RenderCrashHandlerHostLinux*) { }
38 };
39
40 RenderCrashHandlerHostLinux::RenderCrashHandlerHostLinux()
41 : renderer_socket_(-1),
42 browser_socket_(-1) {
43 int fds[2];
44 // We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the renderer from
45 // sending datagrams to other sockets on the system. The sandbox may prevent
46 // the renderer from calling socket() to create new sockets, but it'll still
47 // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send
48 // a datagram to any (abstract) socket on the same system. With
49 // SOCK_SEQPACKET, this is prevented.
50 CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
51 static const int on = 1;
52
53 // Enable passcred on the server end of the socket
54 CHECK(setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) == 0);
55
56 renderer_socket_ = fds[0];
57 browser_socket_ = fds[1];
58
59 ChromeThread::PostTask(
60 ChromeThread::IO, FROM_HERE,
61 NewRunnableMethod(this, &RenderCrashHandlerHostLinux::Init));
62 }
63
64 RenderCrashHandlerHostLinux::~RenderCrashHandlerHostLinux() {
65 HANDLE_EINTR(close(renderer_socket_));
66 HANDLE_EINTR(close(browser_socket_));
67 }
68
69 void RenderCrashHandlerHostLinux::Init() {
70 MessageLoopForIO* ml = MessageLoopForIO::current();
71 CHECK(ml->WatchFileDescriptor(
72 browser_socket_, true /* persistent */,
73 MessageLoopForIO::WATCH_READ,
74 &file_descriptor_watcher_, this));
75 ml->AddDestructionObserver(this);
76 }
77
78 void RenderCrashHandlerHostLinux::OnFileCanWriteWithoutBlocking(int fd) {
79 DCHECK(false);
80 }
81
82 void RenderCrashHandlerHostLinux::OnFileCanReadWithoutBlocking(int fd) {
83 DCHECK_EQ(fd, browser_socket_);
84
85 // A renderer process has crashed and has signaled us by writing a datagram
86 // to the death signal socket. The datagram contains the crash context needed
87 // for writing the minidump as well as a file descriptor and a credentials
88 // block so that they can't lie about their pid.
89
90 // The length of the control message:
91 static const unsigned kControlMsgSize =
92 CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
93 // The length of the regular payload:
94 static const unsigned kCrashContextSize =
95 sizeof(google_breakpad::ExceptionHandler::CrashContext);
96
97 struct msghdr msg = {0};
98 struct iovec iov[4];
99 char crash_context[kCrashContextSize];
100 char guid[kGuidSize + 1];
101 char crash_url[kMaxActiveURLSize + 1];
102 char distro[kDistroSize + 1];
103 char control[kControlMsgSize];
104 const ssize_t expected_msg_size = sizeof(crash_context) + sizeof(guid) +
105 sizeof(crash_url) + sizeof(distro);
106
107 iov[0].iov_base = crash_context;
108 iov[0].iov_len = sizeof(crash_context);
109 iov[1].iov_base = guid;
110 iov[1].iov_len = sizeof(guid);
111 iov[2].iov_base = crash_url;
112 iov[2].iov_len = sizeof(crash_url);
113 iov[3].iov_base = distro;
114 iov[3].iov_len = sizeof(distro);
115 msg.msg_iov = iov;
116 msg.msg_iovlen = 4;
117 msg.msg_control = control;
118 msg.msg_controllen = kControlMsgSize;
119
120 const ssize_t msg_size = HANDLE_EINTR(recvmsg(browser_socket_, &msg, 0));
121 if (msg_size != expected_msg_size) {
122 LOG(ERROR) << "Error reading from death signal socket. Crash dumping"
123 << " is disabled."
124 << " msg_size:" << msg_size
125 << " errno:" << errno;
126 file_descriptor_watcher_.StopWatchingFileDescriptor();
127 return;
128 }
129
130 if (msg.msg_controllen != kControlMsgSize ||
131 msg.msg_flags & ~MSG_TRUNC) {
132 LOG(ERROR) << "Received death signal message with the wrong size;"
133 << " msg.msg_controllen:" << msg.msg_controllen
134 << " msg.msg_flags:" << msg.msg_flags
135 << " kCrashContextSize:" << kCrashContextSize
136 << " kControlMsgSize:" << kControlMsgSize;
137 return;
138 }
139
140 // Walk the control payload an extract the file descriptor and validated pid.
141 pid_t crashing_pid = -1;
142 int signal_fd = -1;
143 for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
144 hdr = CMSG_NXTHDR(&msg, hdr)) {
145 if (hdr->cmsg_level != SOL_SOCKET)
146 continue;
147 if (hdr->cmsg_type == SCM_RIGHTS) {
148 const unsigned len = hdr->cmsg_len -
149 (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
150 DCHECK_EQ(len % sizeof(int), 0u);
151 const unsigned num_fds = len / sizeof(int);
152 if (num_fds > 1 || num_fds == 0) {
153 // A nasty renderer could try and send us too many descriptors and
154 // force a leak.
155 LOG(ERROR) << "Death signal contained too many descriptors;"
156 << " num_fds:" << num_fds;
157 for (unsigned i = 0; i < num_fds; ++i)
158 HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]));
159 return;
160 } else {
161 signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
162 }
163 } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
164 const struct ucred *cred =
165 reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
166 crashing_pid = cred->pid;
167 }
168 }
169
170 if (crashing_pid == -1 || signal_fd == -1) {
171 LOG(ERROR) << "Death signal message didn't contain all expected control"
172 << " messages";
173 if (signal_fd)
174 HANDLE_EINTR(close(signal_fd));
175 return;
176 }
177
178 // Kernel bug workaround (broken in 2.6.30 at least):
179 // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID
180 // namespaces. Thus |crashing_pid| might be garbage from our point of view.
181 // In the future we can remove this workaround, but we have to wait a couple
182 // of years to be sure that it's worked its way out into the world.
183
184 uint64_t inode_number;
185 if (!base::FileDescriptorGetInode(&inode_number, signal_fd)) {
186 LOG(WARNING) << "Failed to get inode number for passed socket";
187 HANDLE_EINTR(close(signal_fd));
188 return;
189 }
190
191 if (!base::FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) {
192 LOG(WARNING) << "Failed to find process holding other end of crash reply "
193 "socket";
194 HANDLE_EINTR(close(signal_fd));
195 return;
196 }
197
198 bool upload = true;
199 FilePath dumps_path("/tmp");
200 if (getenv("CHROME_HEADLESS")) {
201 upload = false;
202 PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path);
203 }
204 const uint64 rand = base::RandUint64();
205 const std::string minidump_filename =
206 StringPrintf("%s/chromium-renderer-minidump-%016" PRIx64 ".dmp",
207 dumps_path.value().c_str(), rand);
208 if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
209 crashing_pid, crash_context,
210 kCrashContextSize)) {
211 LOG(ERROR) << "Failed to write crash dump for pid " << crashing_pid;
212 HANDLE_EINTR(close(signal_fd));
213 }
214
215 // Send the done signal to the renderer: it can exit now.
216 memset(&msg, 0, sizeof(msg));
217 struct iovec done_iov;
218 done_iov.iov_base = const_cast<char*>("\x42");
219 done_iov.iov_len = 1;
220 msg.msg_iov = &done_iov;
221 msg.msg_iovlen = 1;
222
223 HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL));
224 HANDLE_EINTR(close(signal_fd));
225
226 // Sanitize the string data a bit more
227 guid[kGuidSize] = crash_url[kMaxActiveURLSize] = distro[kDistroSize] = 0;
228
229 BreakpadInfo info;
230 info.filename = minidump_filename.c_str();
231 info.process_type = "renderer";
232 info.process_type_length = 8;
233 info.crash_url = crash_url;
234 info.crash_url_length = strlen(crash_url);
235 info.guid = guid;
236 info.guid_length = strlen(guid);
237 info.distro = distro;
238 info.distro_length = strlen(distro);
239 info.upload = upload;
240 HandleCrashDump(info);
241 }
242
243 void RenderCrashHandlerHostLinux::WillDestroyCurrentMessageLoop() {
244 file_descriptor_watcher_.StopWatchingFileDescriptor();
245 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698