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

Side by Side Diff: chrome/app/breakpad_linux.cc

Issue 9838033: Upstream native crash handling changes for Android. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: breakpad gyp changes Created 8 years, 8 months 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) 2012 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 // For linux_syscall_support.h. This makes it safe to call embedded system
6 // calls when in seccomp mode.
7 #define SYS_SYSCALL_ENTRYPOINT "playground$syscallEntryPoint"
8
9 #include "chrome/app/breakpad_linux.h"
10
11 #include <fcntl.h>
12 #include <poll.h>
13 #include <stdlib.h>
14 #include <sys/socket.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <sys/uio.h>
19 #include <time.h>
20 #include <unistd.h>
21
22 #include <algorithm>
23 #include <string>
24
25 #include "base/command_line.h"
26 #include "base/eintr_wrapper.h"
27 #include "base/file_path.h"
28 #include "base/global_descriptors_posix.h"
29 #include "base/linux_util.h"
30 #include "base/path_service.h"
31 #include "base/string_util.h"
32 #include "breakpad/src/client/linux/handler/exception_handler.h"
33 #include "breakpad/src/client/linux/minidump_writer/directory_reader.h"
34 #include "breakpad/src/common/linux/linux_libc_support.h"
35 #include "breakpad/src/common/memory.h"
36 #include "chrome/browser/crash_upload_list.h"
37 #include "chrome/common/child_process_logging.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/chrome_version_info_posix.h"
41 #include "chrome/common/env_vars.h"
42 #include "chrome/common/logging_chrome.h"
43 #include "content/common/chrome_descriptors.h"
44 #include "seccompsandbox/linux_syscall_support.h"
45
46 #ifndef PR_SET_PTRACER
47 #define PR_SET_PTRACER 0x59616d61
48 #endif
49
50 // Some versions of gcc are prone to warn about unused return values. In cases
51 // where we either a) know the call cannot fail, or b) there is nothing we
52 // can do when a call fails, we mark the return code as ignored. This avoids
53 // spurious compiler warnings.
54 #define IGNORE_RET(x) do { if (x); } while (0)
55
56 static const char kUploadURL[] =
57 "https://clients2.google.com/cr/report";
58
59 static bool g_is_crash_reporter_enabled = false;
60 static uint64_t g_process_start_time = 0;
61 static char* g_crash_log_path = NULL;
62 static google_breakpad::ExceptionHandler* g_breakpad = NULL;
63
64 // Writes the value |v| as 16 hex characters to the memory pointed at by
65 // |output|.
66 static void write_uint64_hex(char* output, uint64_t v) {
67 static const char hextable[] = "0123456789abcdef";
68
69 for (int i = 15; i >= 0; --i) {
70 output[i] = hextable[v & 15];
71 v >>= 4;
72 }
73 }
74
75 // The following helper functions are for calculating uptime.
76
77 // Converts a struct timeval to milliseconds.
78 static uint64_t timeval_to_ms(struct timeval *tv) {
79 uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t.
80 ret *= 1000;
81 ret += tv->tv_usec / 1000;
82 return ret;
83 }
84
85 // Converts a struct timeval to milliseconds.
86 static uint64_t kernel_timeval_to_ms(struct kernel_timeval *tv) {
87 uint64_t ret = tv->tv_sec; // Avoid overflow by explicitly using a uint64_t.
88 ret *= 1000;
89 ret += tv->tv_usec / 1000;
90 return ret;
91 }
92
93 // uint64_t version of my_int_len() from
94 // breakpad/src/common/linux/linux_libc_support.h. Return the length of the
95 // given, non-negative integer when expressed in base 10.
96 static unsigned my_uint64_len(uint64_t i) {
97 if (!i)
98 return 1;
99
100 unsigned len = 0;
101 while (i) {
102 len++;
103 i /= 10;
104 }
105
106 return len;
107 }
108
109 // uint64_t version of my_itos() from
110 // breakpad/src/common/linux/linux_libc_support.h. Convert a non-negative
111 // integer to a string (not null-terminated).
112 static void my_uint64tos(char* output, uint64_t i, unsigned i_len) {
113 for (unsigned index = i_len; index; --index, i /= 10)
114 output[index - 1] = '0' + (i % 10);
115 }
116
117 namespace {
118
119 // MIME substrings.
120 static const char g_rn[] = "\r\n";
121 static const char g_form_data_msg[] = "Content-Disposition: form-data; name=\"";
122 static const char g_quote_msg[] = "\"";
123 static const char g_dashdash_msg[] = "--";
124 static const char g_dump_msg[] = "upload_file_minidump\"; filename=\"dump\"";
125 static const char g_content_type_msg[] =
126 "Content-Type: application/octet-stream";
127
128 // MimeWriter manages an iovec for writing MIMEs to a file.
129 class MimeWriter {
130 public:
131 static const int kIovCapacity = 30;
132 static const size_t kMaxCrashChunkSize = 64;
133
134 MimeWriter(int fd, const char* const mime_boundary);
135 ~MimeWriter();
136
137 // Append boundary.
138 void AddBoundary();
139
140 // Append end of file boundary.
141 void AddEnd();
142
143 // Append key/value pair with specified sizes.
144 void AddPairData(const char* msg_type,
145 size_t msg_type_size,
146 const char* msg_data,
147 size_t msg_data_size);
148
149 // Append key/value pair.
150 void AddPairString(const char* msg_type,
151 const char* msg_data) {
152 AddPairData(msg_type, my_strlen(msg_type), msg_data, my_strlen(msg_data));
153 }
154
155 // Append key/value pair, splitting value into chunks no larger than
156 // |chunk_size|. |chunk_size| cannot be greater than |kMaxCrashChunkSize|.
157 // The msg_type string will have a counter suffix to distinguish each chunk.
158 void AddPairDataInChunks(const char* msg_type,
159 size_t msg_type_size,
160 const char* msg_data,
161 size_t msg_data_size,
162 size_t chunk_size,
163 bool strip_trailing_spaces);
164
165 // Add binary file dump. Currently this is only done once, so the name is
166 // fixed.
167 void AddFileDump(uint8_t* file_data,
168 size_t file_size);
169
170 // Flush any pending iovecs to the output file.
171 void Flush() {
172 IGNORE_RET(sys_writev(fd_, iov_, iov_index_));
173 iov_index_ = 0;
174 }
175
176 private:
177 void AddItem(const void* base, size_t size);
178 // Minor performance trade-off for easier-to-maintain code.
179 void AddString(const char* str) {
180 AddItem(str, my_strlen(str));
181 }
182 void AddItemWithoutTrailingSpaces(const void* base, size_t size);
183
184 struct kernel_iovec iov_[kIovCapacity];
185 int iov_index_;
186
187 // Output file descriptor.
188 int fd_;
189
190 const char* const mime_boundary_;
191
192 DISALLOW_COPY_AND_ASSIGN(MimeWriter);
193 };
194
195 MimeWriter::MimeWriter(int fd, const char* const mime_boundary)
196 : iov_index_(0),
197 fd_(fd),
198 mime_boundary_(mime_boundary) {
199 }
200
201 MimeWriter::~MimeWriter() {
202 }
203
204 void MimeWriter::AddBoundary() {
205 AddString(mime_boundary_);
206 AddString(g_rn);
207 }
208
209 void MimeWriter::AddEnd() {
210 AddString(mime_boundary_);
211 AddString(g_dashdash_msg);
212 AddString(g_rn);
213 }
214
215 void MimeWriter::AddPairData(const char* msg_type,
216 size_t msg_type_size,
217 const char* msg_data,
218 size_t msg_data_size) {
219 AddString(g_form_data_msg);
220 AddItem(msg_type, msg_type_size);
221 AddString(g_quote_msg);
222 AddString(g_rn);
223 AddString(g_rn);
224 AddItem(msg_data, msg_data_size);
225 AddString(g_rn);
226 }
227
228 void MimeWriter::AddPairDataInChunks(const char* msg_type,
229 size_t msg_type_size,
230 const char* msg_data,
231 size_t msg_data_size,
232 size_t chunk_size,
233 bool strip_trailing_spaces) {
234 if (chunk_size > kMaxCrashChunkSize)
235 return;
236
237 unsigned i = 0;
238 size_t done = 0, msg_length = msg_data_size;
239
240 while (msg_length) {
241 char num[16];
242 const unsigned num_len = my_int_len(++i);
243 my_itos(num, i, num_len);
244
245 size_t chunk_len = std::min(chunk_size, msg_length);
246
247 AddString(g_form_data_msg);
248 AddItem(msg_type, msg_type_size);
249 AddItem(num, num_len);
250 AddString(g_quote_msg);
251 AddString(g_rn);
252 AddString(g_rn);
253 if (strip_trailing_spaces) {
254 AddItemWithoutTrailingSpaces(msg_data + done, chunk_len);
255 } else {
256 AddItem(msg_data + done, chunk_len);
257 }
258 AddString(g_rn);
259 AddBoundary();
260 Flush();
261
262 done += chunk_len;
263 msg_length -= chunk_len;
264 }
265 }
266
267 void MimeWriter::AddFileDump(uint8_t* file_data,
268 size_t file_size) {
269 AddString(g_form_data_msg);
270 AddString(g_dump_msg);
271 AddString(g_rn);
272 AddString(g_content_type_msg);
273 AddString(g_rn);
274 AddString(g_rn);
275 AddItem(file_data, file_size);
276 AddString(g_rn);
277 }
278
279 void MimeWriter::AddItem(const void* base, size_t size) {
280 // Check if the iovec is full and needs to be flushed to output file.
281 if (iov_index_ == kIovCapacity) {
282 Flush();
283 }
284 iov_[iov_index_].iov_base = const_cast<void*>(base);
285 iov_[iov_index_].iov_len = size;
286 ++iov_index_;
287 }
288
289 void MimeWriter::AddItemWithoutTrailingSpaces(const void* base, size_t size) {
290 while (size > 0) {
291 const char* c = static_cast<const char*>(base) + size - 1;
292 if (*c != ' ')
293 break;
294 size--;
295 }
296 AddItem(base, size);
297 }
298
299 void DumpProcess() {
300 if (g_breakpad)
301 g_breakpad->WriteMinidump();
302 }
303
304 } // namespace
305
306 void HandleCrashDump(const BreakpadInfo& info) {
307 // WARNING: this code runs in a compromised context. It may not call into
308 // libc nor allocate memory normally.
309
310 const int dumpfd = sys_open(info.filename, O_RDONLY, 0);
311 if (dumpfd < 0) {
312 static const char msg[] = "Cannot upload crash dump: failed to open\n";
313 sys_write(2, msg, sizeof(msg));
314 return;
315 }
316 struct kernel_stat st;
317 if (sys_fstat(dumpfd, &st) != 0) {
318 static const char msg[] = "Cannot upload crash dump: stat failed\n";
319 sys_write(2, msg, sizeof(msg));
320 IGNORE_RET(sys_close(dumpfd));
321 return;
322 }
323
324 google_breakpad::PageAllocator allocator;
325
326 uint8_t* dump_data = reinterpret_cast<uint8_t*>(allocator.Alloc(st.st_size));
327 if (!dump_data) {
328 static const char msg[] = "Cannot upload crash dump: cannot alloc\n";
329 sys_write(2, msg, sizeof(msg));
330 IGNORE_RET(sys_close(dumpfd));
331 return;
332 }
333
334 sys_read(dumpfd, dump_data, st.st_size);
335 IGNORE_RET(sys_close(dumpfd));
336
337 // We need to build a MIME block for uploading to the server. Since we are
338 // going to fork and run wget, it needs to be written to a temp file.
339
340 const int ufd = sys_open("/dev/urandom", O_RDONLY, 0);
341 if (ufd < 0) {
342 static const char msg[] = "Cannot upload crash dump because /dev/urandom"
343 " is missing\n";
344 sys_write(2, msg, sizeof(msg) - 1);
345 return;
346 }
347
348 static const char temp_file_template[] =
349 "/tmp/chromium-upload-XXXXXXXXXXXXXXXX";
350 char temp_file[sizeof(temp_file_template)];
351 int temp_file_fd = -1;
352 if (info.upload) {
353 memcpy(temp_file, temp_file_template, sizeof(temp_file_template));
354
355 for (unsigned i = 0; i < 10; ++i) {
356 uint64_t t;
357 sys_read(ufd, &t, sizeof(t));
358 write_uint64_hex(temp_file + sizeof(temp_file) - (16 + 1), t);
359
360 temp_file_fd = sys_open(temp_file, O_WRONLY | O_CREAT | O_EXCL, 0600);
361 if (temp_file_fd >= 0)
362 break;
363 }
364
365 if (temp_file_fd < 0) {
366 static const char msg[] = "Failed to create temporary file in /tmp: "
367 "cannot upload crash dump\n";
368 sys_write(2, msg, sizeof(msg) - 1);
369 IGNORE_RET(sys_close(ufd));
370 return;
371 }
372 } else {
373 temp_file_fd = sys_open(info.filename, O_WRONLY, 0600);
374 if (temp_file_fd < 0) {
375 static const char msg[] = "Failed to save crash dump: failed to open\n";
376 sys_write(2, msg, sizeof(msg) - 1);
377 IGNORE_RET(sys_close(ufd));
378 return;
379 }
380 }
381
382 // The MIME boundary is 28 hyphens, followed by a 64-bit nonce and a NUL.
383 char mime_boundary[28 + 16 + 1];
384 my_memset(mime_boundary, '-', 28);
385 uint64_t boundary_rand;
386 sys_read(ufd, &boundary_rand, sizeof(boundary_rand));
387 write_uint64_hex(mime_boundary + 28, boundary_rand);
388 mime_boundary[28 + 16] = 0;
389 IGNORE_RET(sys_close(ufd));
390
391 // The MIME block looks like this:
392 // BOUNDARY \r\n
393 // Content-Disposition: form-data; name="prod" \r\n \r\n
394 // Chrome_Linux \r\n
395 // BOUNDARY \r\n
396 // Content-Disposition: form-data; name="ver" \r\n \r\n
397 // 1.2.3.4 \r\n
398 // BOUNDARY \r\n
399 // Content-Disposition: form-data; name="guid" \r\n \r\n
400 // 1.2.3.4 \r\n
401 // BOUNDARY \r\n
402 //
403 // zero or one:
404 // Content-Disposition: form-data; name="ptime" \r\n \r\n
405 // abcdef \r\n
406 // BOUNDARY \r\n
407 //
408 // zero or one:
409 // Content-Disposition: form-data; name="ptype" \r\n \r\n
410 // abcdef \r\n
411 // BOUNDARY \r\n
412 //
413 // zero or more gpu entries:
414 // Content-Disposition: form-data; name="gpu-xxxxx" \r\n \r\n
415 // <gpu-xxxxx> \r\n
416 // BOUNDARY \r\n
417 //
418 // zero or one:
419 // Content-Disposition: form-data; name="lsb-release" \r\n \r\n
420 // abcdef \r\n
421 // BOUNDARY \r\n
422 //
423 // zero or more:
424 // Content-Disposition: form-data; name="url-chunk-1" \r\n \r\n
425 // abcdef \r\n
426 // BOUNDARY \r\n
427 //
428 // zero or one:
429 // Content-Disposition: form-data; name="channel" \r\n \r\n
430 // beta \r\n
431 // BOUNDARY \r\n
432 //
433 // zero or one:
434 // Content-Disposition: form-data; name="num-views" \r\n \r\n
435 // 3 \r\n
436 // BOUNDARY \r\n
437 //
438 // zero or one:
439 // Content-Disposition: form-data; name="num-extensions" \r\n \r\n
440 // 5 \r\n
441 // BOUNDARY \r\n
442 //
443 // zero to 10:
444 // Content-Disposition: form-data; name="extension-1" \r\n \r\n
445 // abcdefghijklmnopqrstuvwxyzabcdef \r\n
446 // BOUNDARY \r\n
447 //
448 // zero or one:
449 // Content-Disposition: form-data; name="num-switches" \r\n \r\n
450 // 5 \r\n
451 // BOUNDARY \r\n
452 //
453 // zero to 15:
454 // Content-Disposition: form-data; name="switch-1" \r\n \r\n
455 // --foo \r\n
456 // BOUNDARY \r\n
457 //
458 // Content-Disposition: form-data; name="dump"; filename="dump" \r\n
459 // Content-Type: application/octet-stream \r\n \r\n
460 // <dump contents>
461 // \r\n BOUNDARY -- \r\n
462
463 MimeWriter writer(temp_file_fd, mime_boundary);
464 {
465 #if defined(OS_CHROMEOS)
466 static const char chrome_product_msg[] = "Chrome_ChromeOS";
467 #else // OS_LINUX
468 static const char chrome_product_msg[] = "Chrome_Linux";
469 #endif
470 static const char version_msg[] = PRODUCT_VERSION;
471
472 writer.AddBoundary();
473 writer.AddPairString("prod", chrome_product_msg);
474 writer.AddBoundary();
475 writer.AddPairString("ver", version_msg);
476 writer.AddBoundary();
477 writer.AddPairString("guid", info.guid);
478 writer.AddBoundary();
479 writer.Flush();
480 }
481
482 if (info.process_start_time > 0) {
483 struct kernel_timeval tv;
484 if (!sys_gettimeofday(&tv, NULL)) {
485 uint64_t time = kernel_timeval_to_ms(&tv);
486 if (time > info.process_start_time) {
487 time -= info.process_start_time;
488 char time_str[21];
489 const unsigned time_len = my_uint64_len(time);
490 my_uint64tos(time_str, time, time_len);
491
492 static const char process_time_msg[] = "ptime";
493 writer.AddPairData(process_time_msg, sizeof(process_time_msg) - 1,
494 time_str, time_len);
495 writer.AddBoundary();
496 writer.Flush();
497 }
498 }
499 }
500
501 if (info.process_type_length) {
502 writer.AddPairString("ptype", info.process_type);
503 writer.AddBoundary();
504 writer.Flush();
505 }
506
507 // If GPU info is known, send it.
508 unsigned gpu_vendor_len = my_strlen(child_process_logging::g_gpu_vendor_id);
509 if (gpu_vendor_len) {
510 static const char vendor_msg[] = "gpu-venid";
511 static const char device_msg[] = "gpu-devid";
512 static const char driver_msg[] = "gpu-driver";
513 static const char psver_msg[] = "gpu-psver";
514 static const char vsver_msg[] = "gpu-vsver";
515
516 writer.AddPairString(vendor_msg, child_process_logging::g_gpu_vendor_id);
517 writer.AddBoundary();
518 writer.AddPairString(device_msg, child_process_logging::g_gpu_device_id);
519 writer.AddBoundary();
520 writer.AddPairString(driver_msg, child_process_logging::g_gpu_driver_ver);
521 writer.AddBoundary();
522 writer.AddPairString(psver_msg, child_process_logging::g_gpu_ps_ver);
523 writer.AddBoundary();
524 writer.AddPairString(vsver_msg, child_process_logging::g_gpu_vs_ver);
525 writer.AddBoundary();
526 writer.Flush();
527 }
528
529 if (info.distro_length) {
530 static const char distro_msg[] = "lsb-release";
531 writer.AddPairString(distro_msg, info.distro);
532 writer.AddBoundary();
533 writer.Flush();
534 }
535
536 // For renderers and plugins.
537 if (info.crash_url_length) {
538 static const char url_chunk_msg[] = "url-chunk-";
539 static const unsigned kMaxUrlLength = 8 * MimeWriter::kMaxCrashChunkSize;
540 writer.AddPairDataInChunks(url_chunk_msg, sizeof(url_chunk_msg) - 1,
541 info.crash_url, std::min(info.crash_url_length, kMaxUrlLength),
542 MimeWriter::kMaxCrashChunkSize, false /* Don't strip whitespaces. */);
543 }
544
545 if (my_strlen(child_process_logging::g_channel)) {
546 writer.AddPairString("channel", child_process_logging::g_channel);
547 writer.AddBoundary();
548 writer.Flush();
549 }
550
551 if (my_strlen(child_process_logging::g_num_views)) {
552 writer.AddPairString("num-views", child_process_logging::g_num_views);
553 writer.AddBoundary();
554 writer.Flush();
555 }
556
557 if (my_strlen(child_process_logging::g_num_extensions)) {
558 writer.AddPairString("num-extensions",
559 child_process_logging::g_num_extensions);
560 writer.AddBoundary();
561 writer.Flush();
562 }
563
564 unsigned extension_ids_len =
565 my_strlen(child_process_logging::g_extension_ids);
566 if (extension_ids_len) {
567 static const char extension_msg[] = "extension-";
568 static const unsigned kMaxExtensionsLen =
569 kMaxReportedActiveExtensions * child_process_logging::kExtensionLen;
570 writer.AddPairDataInChunks(extension_msg, sizeof(extension_msg) - 1,
571 child_process_logging::g_extension_ids,
572 std::min(extension_ids_len, kMaxExtensionsLen),
573 child_process_logging::kExtensionLen,
574 false /* Don't strip whitespace. */);
575 }
576
577 unsigned printer_info_len =
578 my_strlen(child_process_logging::g_printer_info);
579 if (printer_info_len) {
580 static const char printer_info_msg[] = "prn-info-";
581 static const unsigned kMaxPrnInfoLen =
582 kMaxReportedPrinterRecords * child_process_logging::kPrinterInfoStrLen;
583 writer.AddPairDataInChunks(printer_info_msg, sizeof(printer_info_msg) - 1,
584 child_process_logging::g_printer_info,
585 std::min(printer_info_len, kMaxPrnInfoLen),
586 child_process_logging::kPrinterInfoStrLen,
587 true);
588 }
589
590 if (my_strlen(child_process_logging::g_num_switches)) {
591 writer.AddPairString("num-switches",
592 child_process_logging::g_num_switches);
593 writer.AddBoundary();
594 writer.Flush();
595 }
596
597 unsigned switches_len =
598 my_strlen(child_process_logging::g_switches);
599 if (switches_len) {
600 static const char switch_msg[] = "switch-";
601 static const unsigned kMaxSwitchLen =
602 kMaxSwitches * child_process_logging::kSwitchLen;
603 writer.AddPairDataInChunks(switch_msg, sizeof(switch_msg) - 1,
604 child_process_logging::g_switches,
605 std::min(switches_len, kMaxSwitchLen),
606 child_process_logging::kSwitchLen,
607 true /* Strip whitespace since switches are padded to kSwitchLen. */);
608 }
609
610 writer.AddFileDump(dump_data, st.st_size);
611 writer.AddEnd();
612 writer.Flush();
613
614 IGNORE_RET(sys_close(temp_file_fd));
615
616 if (!info.upload)
617 return;
618
619 // The --header argument to wget looks like:
620 // --header=Content-Type: multipart/form-data; boundary=XYZ
621 // where the boundary has two fewer leading '-' chars
622 static const char header_msg[] =
623 "--header=Content-Type: multipart/form-data; boundary=";
624 char* const header = reinterpret_cast<char*>(allocator.Alloc(
625 sizeof(header_msg) - 1 + sizeof(mime_boundary) - 2));
626 memcpy(header, header_msg, sizeof(header_msg) - 1);
627 memcpy(header + sizeof(header_msg) - 1, mime_boundary + 2,
628 sizeof(mime_boundary) - 2);
629 // We grab the NUL byte from the end of |mime_boundary|.
630
631 // The --post-file argument to wget looks like:
632 // --post-file=/tmp/...
633 static const char post_file_msg[] = "--post-file=";
634 char* const post_file = reinterpret_cast<char*>(allocator.Alloc(
635 sizeof(post_file_msg) - 1 + sizeof(temp_file)));
636 memcpy(post_file, post_file_msg, sizeof(post_file_msg) - 1);
637 memcpy(post_file + sizeof(post_file_msg) - 1, temp_file, sizeof(temp_file));
638
639 const pid_t child = sys_fork();
640 if (!child) {
641 // Spawned helper process.
642 //
643 // This code is called both when a browser is crashing (in which case,
644 // nothing really matters any more) and when a renderer/plugin crashes, in
645 // which case we need to continue.
646 //
647 // Since we are a multithreaded app, if we were just to fork(), we might
648 // grab file descriptors which have just been created in another thread and
649 // hold them open for too long.
650 //
651 // Thus, we have to loop and try and close everything.
652 const int fd = sys_open("/proc/self/fd", O_DIRECTORY | O_RDONLY, 0);
653 if (fd < 0) {
654 for (unsigned i = 3; i < 8192; ++i)
655 IGNORE_RET(sys_close(i));
656 } else {
657 google_breakpad::DirectoryReader reader(fd);
658 const char* name;
659 while (reader.GetNextEntry(&name)) {
660 int i;
661 if (my_strtoui(&i, name) && i > 2 && i != fd)
662 IGNORE_RET(sys_close(i));
663 reader.PopEntry();
664 }
665
666 IGNORE_RET(sys_close(fd));
667 }
668
669 IGNORE_RET(sys_setsid());
670
671 // Leave one end of a pipe in the wget process and watch for it getting
672 // closed by the wget process exiting.
673 int fds[2];
674 if (sys_pipe(fds) >= 0) {
675 const pid_t wget_child = sys_fork();
676 if (!wget_child) {
677 // Wget process.
678 IGNORE_RET(sys_close(fds[0]));
679 IGNORE_RET(sys_dup2(fds[1], 3));
680 static const char* const kWgetBinary = "/usr/bin/wget";
681 const char* args[] = {
682 kWgetBinary,
683 header,
684 post_file,
685 kUploadURL,
686 "--timeout=10", // Set a timeout so we don't hang forever.
687 "--tries=1", // Don't retry if the upload fails.
688 "-O", // output reply to fd 3
689 "/dev/fd/3",
690 NULL,
691 };
692
693 execve(kWgetBinary, const_cast<char**>(args), environ);
694 static const char msg[] = "Cannot upload crash dump: cannot exec "
695 "/usr/bin/wget\n";
696 sys_write(2, msg, sizeof(msg) - 1);
697 sys__exit(1);
698 }
699
700 // Helper process.
701 if (wget_child > 0) {
702 IGNORE_RET(sys_close(fds[1]));
703 char id_buf[17]; // Crash report IDs are expected to be 16 chars.
704 ssize_t len = -1;
705 // Wget should finish in about 10 seconds. Add a few more 500 ms
706 // internals to account for process startup time.
707 for (size_t wait_count = 0; wait_count < 24; ++wait_count) {
708 struct kernel_pollfd poll_fd;
709 poll_fd.fd = fds[0];
710 poll_fd.events = POLLIN | POLLPRI | POLLERR;
711 int ret = sys_poll(&poll_fd, 1, 500);
712 if (ret < 0) {
713 // Error
714 break;
715 } else if (ret > 0) {
716 // There is data to read.
717 len = HANDLE_EINTR(sys_read(fds[0], id_buf, sizeof(id_buf) - 1));
718 break;
719 }
720 // ret == 0 -> timed out, continue waiting.
721 }
722 if (len > 0) {
723 // Write crash dump id to stderr.
724 id_buf[len] = 0;
725 static const char msg[] = "\nCrash dump id: ";
726 sys_write(2, msg, sizeof(msg) - 1);
727 sys_write(2, id_buf, my_strlen(id_buf));
728 sys_write(2, "\n", 1);
729
730 // Write crash dump id to crash log as: seconds_since_epoch,crash_id
731 struct kernel_timeval tv;
732 if (g_crash_log_path && !sys_gettimeofday(&tv, NULL)) {
733 uint64_t time = kernel_timeval_to_ms(&tv) / 1000;
734 char time_str[21];
735 const unsigned time_len = my_uint64_len(time);
736 my_uint64tos(time_str, time, time_len);
737
738 int log_fd = sys_open(g_crash_log_path,
739 O_CREAT | O_WRONLY | O_APPEND,
740 0600);
741 if (log_fd > 0) {
742 sys_write(log_fd, time_str, time_len);
743 sys_write(log_fd, ",", 1);
744 sys_write(log_fd, id_buf, my_strlen(id_buf));
745 sys_write(log_fd, "\n", 1);
746 IGNORE_RET(sys_close(log_fd));
747 }
748 }
749 }
750 if (sys_waitpid(wget_child, NULL, WNOHANG) == 0) {
751 // Wget process is still around, kill it.
752 sys_kill(wget_child, SIGKILL);
753 }
754 }
755 }
756
757 // Helper process.
758 IGNORE_RET(sys_unlink(info.filename));
759 IGNORE_RET(sys_unlink(temp_file));
760 sys__exit(0);
761 }
762
763 // Main browser process.
764 if (child <= 0)
765 return;
766 HANDLE_EINTR(sys_waitpid(child, NULL, 0));
767 }
768
769 static bool CrashDone(const char* dump_path,
770 const char* minidump_id,
771 const bool upload,
772 const bool succeeded) {
773 // WARNING: this code runs in a compromised context. It may not call into
774 // libc nor allocate memory normally.
775 if (!succeeded)
776 return false;
777
778 google_breakpad::PageAllocator allocator;
779 const unsigned dump_path_len = my_strlen(dump_path);
780 const unsigned minidump_id_len = my_strlen(minidump_id);
781 char* const path = reinterpret_cast<char*>(allocator.Alloc(
782 dump_path_len + 1 /* '/' */ + minidump_id_len +
783 4 /* ".dmp" */ + 1 /* NUL */));
784 memcpy(path, dump_path, dump_path_len);
785 path[dump_path_len] = '/';
786 memcpy(path + dump_path_len + 1, minidump_id, minidump_id_len);
787 memcpy(path + dump_path_len + 1 + minidump_id_len, ".dmp", 4);
788 path[dump_path_len + 1 + minidump_id_len + 4] = 0;
789
790 BreakpadInfo info;
791 info.filename = path;
792 info.process_type = "browser";
793 info.process_type_length = 7;
794 info.crash_url = NULL;
795 info.crash_url_length = 0;
796 info.guid = child_process_logging::g_client_id;
797 info.guid_length = my_strlen(child_process_logging::g_client_id);
798 info.distro = base::g_linux_distro;
799 info.distro_length = my_strlen(base::g_linux_distro);
800 info.upload = upload;
801 info.process_start_time = g_process_start_time;
802 HandleCrashDump(info);
803 return true;
804 }
805
806 // Wrapper function, do not add more code here.
807 static bool CrashDoneNoUpload(const char* dump_path,
808 const char* minidump_id,
809 void* context,
810 bool succeeded) {
811 return CrashDone(dump_path, minidump_id, false, succeeded);
812 }
813
814 // Wrapper function, do not add more code here.
815 static bool CrashDoneUpload(const char* dump_path,
816 const char* minidump_id,
817 void* context,
818 bool succeeded) {
819 return CrashDone(dump_path, minidump_id, true, succeeded);
820 }
821
822 void EnableCrashDumping(const bool unattended) {
823 g_is_crash_reporter_enabled = true;
824
825 FilePath tmp_path("/tmp");
826 PathService::Get(base::DIR_TEMP, &tmp_path);
827
828 FilePath dumps_path(tmp_path);
829 if (PathService::Get(chrome::DIR_CRASH_DUMPS, &dumps_path)) {
830 FilePath logfile =
831 dumps_path.AppendASCII(CrashUploadList::kReporterLogFilename);
832 std::string logfile_str = logfile.value();
833 const size_t crash_log_path_len = logfile_str.size() + 1;
834 g_crash_log_path = new char[crash_log_path_len];
835 strncpy(g_crash_log_path, logfile_str.c_str(), crash_log_path_len);
836 }
837
838 DCHECK(!g_breakpad);
839 if (unattended) {
840 g_breakpad = new google_breakpad::ExceptionHandler(
841 dumps_path.value().c_str(),
842 NULL,
843 CrashDoneNoUpload,
844 NULL,
845 true /* install handlers */);
846 } else {
847 g_breakpad = new google_breakpad::ExceptionHandler(
848 tmp_path.value().c_str(),
849 NULL,
850 CrashDoneUpload,
851 NULL,
852 true /* install handlers */);
853 }
854 }
855
856 // Non-Browser = Extension, Gpu, Plugins, Ppapi and Renderer
857 static bool NonBrowserCrashHandler(const void* crash_context,
858 size_t crash_context_size,
859 void* context) {
860 const int fd = reinterpret_cast<intptr_t>(context);
861 int fds[2] = { -1, -1 };
862 if (sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
863 static const char msg[] = "Failed to create socket for crash dumping.\n";
864 sys_write(2, msg, sizeof(msg)-1);
865 return false;
866 }
867
868 // On kernels with ptrace protection, e.g. Ubuntu 10.10+, the browser cannot
869 // ptrace this crashing process and crash dumping will fail. When using the
870 // SUID sandbox, this crashing process is likely to be in its own PID
871 // namespace, and thus there is no way to permit only the browser process to
872 // ptrace it.
873 // The workaround is to allow all processes to ptrace this process if we
874 // reach this point, by passing -1 as the allowed PID. However, support for
875 // passing -1 as the PID won't reach kernels until around the Ubuntu 12.04
876 // timeframe.
877 sys_prctl(PR_SET_PTRACER, -1);
878
879 // Start constructing the message to send to the browser.
880 char guid[kGuidSize + 1] = {0};
881 char crash_url[kMaxActiveURLSize + 1] = {0};
882 char distro[kDistroSize + 1] = {0};
883 const size_t guid_len =
884 std::min(my_strlen(child_process_logging::g_client_id), kGuidSize);
885 const size_t crash_url_len =
886 std::min(my_strlen(child_process_logging::g_active_url),
887 kMaxActiveURLSize);
888 const size_t distro_len =
889 std::min(my_strlen(base::g_linux_distro), kDistroSize);
890 memcpy(guid, child_process_logging::g_client_id, guid_len);
891 memcpy(crash_url, child_process_logging::g_active_url, crash_url_len);
892 memcpy(distro, base::g_linux_distro, distro_len);
893
894 char b; // Dummy variable for sys_read below.
895 const char* b_addr = &b; // Get the address of |b| so we can create the
896 // expected /proc/[pid]/syscall content in the
897 // browser to convert namespace tids.
898
899 // The length of the control message:
900 static const unsigned kControlMsgSize = sizeof(fds);
901 static const unsigned kControlMsgSpaceSize = CMSG_SPACE(kControlMsgSize);
902 static const unsigned kControlMsgLenSize = CMSG_LEN(kControlMsgSize);
903
904 const size_t kIovSize = 7;
905 struct kernel_msghdr msg;
906 my_memset(&msg, 0, sizeof(struct kernel_msghdr));
907 struct kernel_iovec iov[kIovSize];
908 iov[0].iov_base = const_cast<void*>(crash_context);
909 iov[0].iov_len = crash_context_size;
910 iov[1].iov_base = guid;
911 iov[1].iov_len = kGuidSize + 1;
912 iov[2].iov_base = crash_url;
913 iov[2].iov_len = kMaxActiveURLSize + 1;
914 iov[3].iov_base = distro;
915 iov[3].iov_len = kDistroSize + 1;
916 iov[4].iov_base = &b_addr;
917 iov[4].iov_len = sizeof(b_addr);
918 iov[5].iov_base = &fds[0];
919 iov[5].iov_len = sizeof(fds[0]);
920 iov[6].iov_base = &g_process_start_time;
921 iov[6].iov_len = sizeof(g_process_start_time);
922
923 msg.msg_iov = iov;
924 msg.msg_iovlen = kIovSize;
925 char cmsg[kControlMsgSpaceSize];
926 my_memset(cmsg, 0, kControlMsgSpaceSize);
927 msg.msg_control = cmsg;
928 msg.msg_controllen = sizeof(cmsg);
929
930 struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
931 hdr->cmsg_level = SOL_SOCKET;
932 hdr->cmsg_type = SCM_RIGHTS;
933 hdr->cmsg_len = kControlMsgLenSize;
934 ((int*) CMSG_DATA(hdr))[0] = fds[0];
935 ((int*) CMSG_DATA(hdr))[1] = fds[1];
936
937 if (HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)) < 0) {
938 static const char errmsg[] = "Failed to tell parent about crash.\n";
939 sys_write(2, errmsg, sizeof(errmsg)-1);
940 IGNORE_RET(sys_close(fds[1]));
941 return false;
942 }
943 IGNORE_RET(sys_close(fds[1]));
944
945 if (HANDLE_EINTR(sys_read(fds[0], &b, 1)) != 1) {
946 static const char errmsg[] = "Parent failed to complete crash dump.\n";
947 sys_write(2, errmsg, sizeof(errmsg)-1);
948 }
949
950 return true;
951 }
952
953 void EnableNonBrowserCrashDumping() {
954 const int fd = base::GlobalDescriptors::GetInstance()->Get(kCrashDumpSignal);
955 g_is_crash_reporter_enabled = true;
956 // We deliberately leak this object.
957 DCHECK(!g_breakpad);
958 g_breakpad = new google_breakpad::ExceptionHandler(
959 "" /* unused */, NULL, NULL, reinterpret_cast<void*>(fd), true);
960 g_breakpad->set_crash_handler(NonBrowserCrashHandler);
961 }
962
963 void InitCrashReporter() {
964 // Determine the process type and take appropriate action.
965 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
966 if (parsed_command_line.HasSwitch(switches::kDisableBreakpad))
967 return;
968
969 const std::string process_type =
970 parsed_command_line.GetSwitchValueASCII(switches::kProcessType);
971 if (process_type.empty()) {
972 EnableCrashDumping(getenv(env_vars::kHeadless) != NULL);
973 } else if (process_type == switches::kRendererProcess ||
974 process_type == switches::kPluginProcess ||
975 process_type == switches::kPpapiPluginProcess ||
976 process_type == switches::kZygoteProcess ||
977 process_type == switches::kGpuProcess) {
978 // We might be chrooted in a zygote or renderer process so we cannot call
979 // GetCollectStatsConsent because that needs access the the user's home
980 // dir. Instead, we set a command line flag for these processes.
981 // Even though plugins are not chrooted, we share the same code path for
982 // simplicity.
983 if (!parsed_command_line.HasSwitch(switches::kEnableCrashReporter))
984 return;
985 // Get the guid and linux distro from the command line switch.
986 std::string switch_value =
987 parsed_command_line.GetSwitchValueASCII(switches::kEnableCrashReporter);
988 size_t separator = switch_value.find(",");
989 if (separator != std::string::npos) {
990 child_process_logging::SetClientId(switch_value.substr(0, separator));
991 base::SetLinuxDistro(switch_value.substr(separator + 1));
992 } else {
993 child_process_logging::SetClientId(switch_value);
994 }
995 EnableNonBrowserCrashDumping();
996 }
997
998 // Set the base process start time value.
999 struct timeval tv;
1000 if (!gettimeofday(&tv, NULL))
1001 g_process_start_time = timeval_to_ms(&tv);
1002 else
1003 g_process_start_time = 0;
1004
1005 logging::SetDumpWithoutCrashingFunction(&DumpProcess);
1006 }
1007
1008 bool IsCrashReporterEnabled() {
1009 return g_is_crash_reporter_enabled;
1010 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698