blob: ed3fcf53a76b8a97f35c33eec393f7a307bd3568 [file] [log] [blame]
Mike Frysinger57b261c2012-04-11 14:47:09 -04001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Chris Sosae4a86032010-06-16 17:08:34 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ken Mixter03403162010-08-18 15:23:16 -07005#include "crash-reporter/user_collector.h"
6
Ben Chan6e709a12012-02-29 12:10:44 -08007#include <bits/wordsize.h>
8#include <elf.h>
9#include <fcntl.h>
Ken Mixter777484c2010-07-23 16:22:44 -070010#include <grp.h> // For struct group.
Ken Mixter1b8fe012011-01-25 13:33:05 -080011#include <pcrecpp.h>
Ken Mixter777484c2010-07-23 16:22:44 -070012#include <pwd.h> // For struct passwd.
Ken Mixter2953c3a2010-10-18 14:42:20 -070013#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
Ken Mixter777484c2010-07-23 16:22:44 -070014
Chris Sosae4a86032010-06-16 17:08:34 -070015#include <string>
Ken Mixter2953c3a2010-10-18 14:42:20 -070016#include <vector>
Chris Sosae4a86032010-06-16 17:08:34 -070017
18#include "base/file_util.h"
19#include "base/logging.h"
Michael Krebs2f3ed032012-08-21 20:17:03 -070020#include "base/stl_util.h"
Chris Masone8a68c7c2011-05-14 11:44:04 -070021#include "base/string_split.h"
Chris Sosae4a86032010-06-16 17:08:34 -070022#include "base/string_util.h"
Mike Frysinger57b261c2012-04-11 14:47:09 -040023#include "base/stringprintf.h"
Ken Mixtera3249322011-03-03 08:47:38 -080024#include "chromeos/process.h"
25#include "chromeos/syslog_logging.h"
Ken Mixter207694d2010-10-28 15:42:37 -070026#include "gflags/gflags.h"
Chris Sosae4a86032010-06-16 17:08:34 -070027
Ken Mixterc6a58e02010-11-01 18:05:30 -070028#pragma GCC diagnostic ignored "-Wstrict-aliasing"
Ken Mixter1b8fe012011-01-25 13:33:05 -080029DEFINE_bool(core2md_failure, false, "Core2md failure test");
30DEFINE_bool(directory_failure, false, "Spool directory failure test");
Ken Mixterc6a58e02010-11-01 18:05:30 -070031DEFINE_string(filter_in, "",
32 "Ignore all crashes but this for testing");
33#pragma GCC diagnostic error "-Wstrict-aliasing"
Ken Mixter207694d2010-10-28 15:42:37 -070034
35static const char kCollectionErrorSignature[] =
36 "crash_reporter-user-collection";
Chris Sosae4a86032010-06-16 17:08:34 -070037// This procfs file is used to cause kernel core file writing to
38// instead pipe the core file into a user space process. See
39// core(5) man page.
40static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixterc49dbd42010-12-14 17:44:11 -080041static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
42// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
43// crashes, but finite to avoid infinitely recursing on crash handling.
44static const char kCorePipeLimit[] = "4";
Ken Mixter777484c2010-07-23 16:22:44 -070045static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter777484c2010-07-23 16:22:44 -070046
Ben Chanf13bb582012-01-06 08:22:07 -080047static const char kStatePrefix[] = "State:\t";
Ken Mixterc49dbd42010-12-14 17:44:11 -080048
Ken Mixter777484c2010-07-23 16:22:44 -070049const char *UserCollector::kUserId = "Uid:\t";
50const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070051
52UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070053 : generate_diagnostics_(false),
54 core_pattern_file_(kCorePatternFile),
Ken Mixterc49dbd42010-12-14 17:44:11 -080055 core_pipe_limit_file_(kCorePipeLimitFile),
Ken Mixter03403162010-08-18 15:23:16 -070056 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070057}
58
59void UserCollector::Initialize(
60 UserCollector::CountCrashFunction count_crash_function,
61 const std::string &our_path,
62 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070063 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070064 CrashCollector::Initialize(count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080065 is_feedback_allowed_function);
Chris Sosae4a86032010-06-16 17:08:34 -070066 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070067 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070068 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070069}
70
71UserCollector::~UserCollector() {
72}
73
Ben Chan6e709a12012-02-29 12:10:44 -080074std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
75 switch (error_type) {
76 case kErrorSystemIssue:
77 return "system-issue";
78 case kErrorReadCoreData:
79 return "read-core-data";
80 case kErrorUnusableProcFiles:
81 return "unusable-proc-files";
82 case kErrorInvalidCoreFile:
83 return "invalid-core-file";
84 case kErrorUnsupported32BitCoreFile:
85 return "unsupported-32bit-core-file";
86 case kErrorCore2MinidumpConversion:
87 return "core2md-conversion";
88 default:
89 return "";
90 }
91}
92
Chris Sosae4a86032010-06-16 17:08:34 -070093std::string UserCollector::GetPattern(bool enabled) const {
94 if (enabled) {
Ken Mixter1b8fe012011-01-25 13:33:05 -080095 // Combine the three crash attributes into one parameter to try to reduce
96 // the size of the invocation line for crash_reporter since the kernel
97 // has a fixed-sized (128B) buffer that it will truncate into. Note that
98 // the kernel does not support quoted arguments in core_pattern.
99 return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -0700100 } else {
101 return "core";
102 }
103}
104
105bool UserCollector::SetUpInternal(bool enabled) {
106 CHECK(initialized_);
Ken Mixtera3249322011-03-03 08:47:38 -0800107 LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
108
Ken Mixterc49dbd42010-12-14 17:44:11 -0800109 if (file_util::WriteFile(FilePath(core_pipe_limit_file_),
110 kCorePipeLimit,
111 strlen(kCorePipeLimit)) !=
112 static_cast<int>(strlen(kCorePipeLimit))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800113 LOG(ERROR) << "Unable to write " << core_pipe_limit_file_;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800114 return false;
115 }
Chris Sosae4a86032010-06-16 17:08:34 -0700116 std::string pattern = GetPattern(enabled);
117 if (file_util::WriteFile(FilePath(core_pattern_file_),
118 pattern.c_str(),
119 pattern.length()) !=
120 static_cast<int>(pattern.length())) {
Ken Mixtera3249322011-03-03 08:47:38 -0800121 LOG(ERROR) << "Unable to write " << core_pattern_file_;
Chris Sosae4a86032010-06-16 17:08:34 -0700122 return false;
123 }
124 return true;
125}
126
Ken Mixter777484c2010-07-23 16:22:44 -0700127FilePath UserCollector::GetProcessPath(pid_t pid) {
128 return FilePath(StringPrintf("/proc/%d", pid));
129}
130
131bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
132 FilePath *target) {
133 int max_size = 32;
134 scoped_array<char> buffer;
135 while (true) {
136 buffer.reset(new char[max_size + 1]);
137 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
138 if (size < 0) {
Ken Mixterd49d3622011-02-09 18:23:00 -0800139 int saved_errno = errno;
Ken Mixtera3249322011-03-03 08:47:38 -0800140 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
141 << saved_errno;
Ken Mixter777484c2010-07-23 16:22:44 -0700142 return false;
143 }
144 buffer[size] = 0;
145 if (size == max_size) {
146 // Avoid overflow when doubling.
147 if (max_size * 2 > max_size) {
148 max_size *= 2;
149 continue;
150 } else {
151 return false;
152 }
153 }
154 break;
155 }
156
157 *target = FilePath(buffer.get());
158 return true;
159}
160
161bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
162 std::string *base_name) {
163 FilePath target;
Ken Mixterd49d3622011-02-09 18:23:00 -0800164 FilePath process_path = GetProcessPath(pid);
165 FilePath exe_path = process_path.Append("exe");
166 if (!GetSymlinkTarget(exe_path, &target)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800167 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
168 << " DirectoryExists: "
169 << file_util::DirectoryExists(process_path);
Ken Mixterd49d3622011-02-09 18:23:00 -0800170 // Try to further diagnose exe readlink failure cause.
171 struct stat buf;
172 int stat_result = stat(exe_path.value().c_str(), &buf);
173 int saved_errno = errno;
174 if (stat_result < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800175 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
176 << " " << saved_errno;
Ken Mixterd49d3622011-02-09 18:23:00 -0800177 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800178 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
179 << buf.st_mode;
Ken Mixterd49d3622011-02-09 18:23:00 -0800180 }
Ken Mixter777484c2010-07-23 16:22:44 -0700181 return false;
Ken Mixterd49d3622011-02-09 18:23:00 -0800182 }
Ken Mixter777484c2010-07-23 16:22:44 -0700183 *base_name = target.BaseName().value();
184 return true;
185}
186
Ben Chanf13bb582012-01-06 08:22:07 -0800187bool UserCollector::GetFirstLineWithPrefix(
188 const std::vector<std::string> &lines,
189 const char *prefix, std::string *line) {
190 std::vector<std::string>::const_iterator line_iterator;
191 for (line_iterator = lines.begin(); line_iterator != lines.end();
192 ++line_iterator) {
193 if (line_iterator->find(prefix) == 0) {
194 *line = *line_iterator;
195 return true;
196 }
197 }
198 return false;
199}
200
201bool UserCollector::GetIdFromStatus(
202 const char *prefix, IdKind kind,
203 const std::vector<std::string> &status_lines, int *id) {
Ken Mixter777484c2010-07-23 16:22:44 -0700204 // From fs/proc/array.c:task_state(), this file contains:
205 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
Ben Chanf13bb582012-01-06 08:22:07 -0800206 std::string id_line;
207 if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700208 return false;
209 }
Ben Chanf13bb582012-01-06 08:22:07 -0800210 std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
Ken Mixter777484c2010-07-23 16:22:44 -0700211 std::vector<std::string> ids;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700212 base::SplitString(id_substring, '\t', &ids);
Ken Mixter777484c2010-07-23 16:22:44 -0700213 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
214 return false;
215 }
216 const char *number = ids[kind].c_str();
217 char *end_number = NULL;
218 *id = strtol(number, &end_number, 10);
Ben Chanf13bb582012-01-06 08:22:07 -0800219 if (*end_number != '\0') {
Ken Mixter777484c2010-07-23 16:22:44 -0700220 return false;
Ben Chanf13bb582012-01-06 08:22:07 -0800221 }
222 return true;
223}
224
225bool UserCollector::GetStateFromStatus(
226 const std::vector<std::string> &status_lines, std::string *state) {
227 std::string state_line;
228 if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
229 return false;
230 }
231 *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
Ken Mixter777484c2010-07-23 16:22:44 -0700232 return true;
233}
234
Ken Mixter207694d2010-10-28 15:42:37 -0700235void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
Ben Chan6e709a12012-02-29 12:10:44 -0800236 ErrorType error_type,
Ken Mixter207694d2010-10-28 15:42:37 -0700237 const std::string &exec) {
238 FilePath crash_path;
Ken Mixtera3249322011-03-03 08:47:38 -0800239 LOG(INFO) << "Writing conversion problems as separate crash report.";
Ken Mixter207694d2010-10-28 15:42:37 -0700240 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800241 LOG(ERROR) << "Could not even get log directory; out of space?";
Ken Mixter207694d2010-10-28 15:42:37 -0700242 return;
243 }
244 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
Ken Mixtera3249322011-03-03 08:47:38 -0800245 std::string error_log = chromeos::GetLog();
Ken Mixter1b8fe012011-01-25 13:33:05 -0800246 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
Simon Queacc79382012-05-04 18:10:09 -0700247 if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
Ken Mixter1b8fe012011-01-25 13:33:05 -0800248 diag_log_path)) {
249 // We load the contents of diag_log into memory and append it to
250 // the error log. We cannot just append to files because we need
251 // to always create new files to prevent attack.
252 std::string diag_log_contents;
253 file_util::ReadFileToString(diag_log_path, &diag_log_contents);
254 error_log.append(diag_log_contents);
255 file_util::Delete(diag_log_path, false);
256 }
Ken Mixter207694d2010-10-28 15:42:37 -0700257 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
258 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
Ken Mixter9b346472010-11-07 13:45:45 -0800259 // We must use WriteNewFile instead of file_util::WriteFile as we do
260 // not want to write with root access to a symlink that an attacker
261 // might have created.
Ken Mixter1b8fe012011-01-25 13:33:05 -0800262 WriteNewFile(log_path, error_log.data(), error_log.length());
Ken Mixter207694d2010-10-28 15:42:37 -0700263 AddCrashMetaData("sig", kCollectionErrorSignature);
Ben Chan6e709a12012-02-29 12:10:44 -0800264 AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
Ken Mixter207694d2010-10-28 15:42:37 -0700265 WriteCrashMetaData(meta_path, exec, log_path.value());
266}
267
Ken Mixter777484c2010-07-23 16:22:44 -0700268bool UserCollector::CopyOffProcFiles(pid_t pid,
269 const FilePath &container_dir) {
270 if (!file_util::CreateDirectory(container_dir)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800271 LOG(ERROR) << "Could not create " << container_dir.value().c_str();
Ken Mixter777484c2010-07-23 16:22:44 -0700272 return false;
273 }
274 FilePath process_path = GetProcessPath(pid);
275 if (!file_util::PathExists(process_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800276 LOG(ERROR) << "Path " << process_path.value() << " does not exist";
Ken Mixter777484c2010-07-23 16:22:44 -0700277 return false;
278 }
279 static const char *proc_files[] = {
280 "auxv",
281 "cmdline",
282 "environ",
283 "maps",
284 "status"
285 };
286 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
287 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
288 container_dir.Append(proc_files[i]))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800289 LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
Ken Mixter777484c2010-07-23 16:22:44 -0700290 return false;
291 }
292 }
Ben Chanec7d7832012-01-09 10:29:58 -0800293 return true;
Ben Chanf13bb582012-01-06 08:22:07 -0800294}
295
Ben Chan6e709a12012-02-29 12:10:44 -0800296bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
Ben Chanf13bb582012-01-06 08:22:07 -0800297 // Check if the maps file is empty, which could be due to the crashed
298 // process being reaped by the kernel before finishing a core dump.
299 int64 file_size = 0;
300 if (!file_util::GetFileSize(container_dir.Append("maps"), &file_size)) {
301 LOG(ERROR) << "Could not get the size of maps file";
302 return false;
303 }
304 if (file_size == 0) {
305 LOG(ERROR) << "maps file is empty";
306 return false;
307 }
Ken Mixter777484c2010-07-23 16:22:44 -0700308 return true;
309}
310
Ben Chan6e709a12012-02-29 12:10:44 -0800311UserCollector::ErrorType UserCollector::ValidateCoreFile(
312 const FilePath &core_path) const {
313 int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
314 if (fd < 0) {
315 LOG(ERROR) << "Could not open core file " << core_path.value();
316 return kErrorInvalidCoreFile;
317 }
318
319 char e_ident[EI_NIDENT];
320 bool read_ok = file_util::ReadFromFD(fd, e_ident, sizeof(e_ident));
321 HANDLE_EINTR(close(fd));
322 if (!read_ok) {
323 LOG(ERROR) << "Could not read header of core file";
324 return kErrorInvalidCoreFile;
325 }
326
327 if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
328 e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
329 LOG(ERROR) << "Invalid core file";
330 return kErrorInvalidCoreFile;
331 }
332
333#if __WORDSIZE == 64
334 // TODO(benchan, mkrebs): Remove this check once core2md can
335 // handles both 32-bit and 64-bit ELF on a 64-bit platform.
336 if (e_ident[EI_CLASS] == ELFCLASS32) {
337 LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
338 << "currently not supported";
339 return kErrorUnsupported32BitCoreFile;
340 }
341#endif
342
343 return kErrorNone;
344}
345
Ken Mixter777484c2010-07-23 16:22:44 -0700346bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
Ken Mixter207694d2010-10-28 15:42:37 -0700347 FilePath *crash_file_path,
348 bool *out_of_capacity) {
Ken Mixter777484c2010-07-23 16:22:44 -0700349 FilePath process_path = GetProcessPath(pid);
350 std::string status;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800351 if (FLAGS_directory_failure) {
Ken Mixtera3249322011-03-03 08:47:38 -0800352 LOG(ERROR) << "Purposefully failing to create spool directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700353 return false;
354 }
Ken Mixter777484c2010-07-23 16:22:44 -0700355 if (!file_util::ReadFileToString(process_path.Append("status"),
356 &status)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800357 LOG(ERROR) << "Could not read status file";
358 LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
359 << file_util::DirectoryExists(process_path);
Ken Mixter777484c2010-07-23 16:22:44 -0700360 return false;
361 }
Ben Chanf13bb582012-01-06 08:22:07 -0800362
363 std::vector<std::string> status_lines;
364 base::SplitString(status, '\n', &status_lines);
365
366 std::string process_state;
367 if (!GetStateFromStatus(status_lines, &process_state)) {
368 LOG(ERROR) << "Could not find process state in status file";
369 return false;
370 }
371 LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
372
Ken Mixter777484c2010-07-23 16:22:44 -0700373 int process_euid;
Ben Chanf13bb582012-01-06 08:22:07 -0800374 if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &process_euid)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800375 LOG(ERROR) << "Could not find euid in status file";
Ken Mixter777484c2010-07-23 16:22:44 -0700376 return false;
377 }
Ken Mixter207694d2010-10-28 15:42:37 -0700378 if (!GetCreatedCrashDirectoryByEuid(process_euid,
379 crash_file_path,
380 out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800381 LOG(ERROR) << "Could not create crash directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700382 return false;
383 }
384 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700385}
386
387bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
388 // Copy off all stdin to a core file.
389 FilePath stdin_path("/dev/fd/0");
390 if (file_util::CopyFile(stdin_path, core_path)) {
391 return true;
392 }
393
Ken Mixtera3249322011-03-03 08:47:38 -0800394 LOG(ERROR) << "Could not write core file";
Ken Mixter777484c2010-07-23 16:22:44 -0700395 // If the file system was full, make sure we remove any remnants.
396 file_util::Delete(core_path, false);
397 return false;
398}
399
Ken Mixter207694d2010-10-28 15:42:37 -0700400bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
401 const FilePath &procfs_directory,
402 const FilePath &minidump_path,
403 const FilePath &temp_directory) {
Ken Mixter777484c2010-07-23 16:22:44 -0700404 FilePath output_path = temp_directory.Append("output");
Ken Mixtera3249322011-03-03 08:47:38 -0800405 chromeos::ProcessImpl core2md;
406 core2md.RedirectOutput(output_path.value());
407 core2md.AddArg(kCoreToMinidumpConverterPath);
408 core2md.AddArg(core_path.value());
409 core2md.AddArg(procfs_directory.value());
Ken Mixter2953c3a2010-10-18 14:42:20 -0700410
Ken Mixtera3249322011-03-03 08:47:38 -0800411 if (!FLAGS_core2md_failure) {
412 core2md.AddArg(minidump_path.value());
413 } else {
Ken Mixter207694d2010-10-28 15:42:37 -0700414 // To test how core2md errors are propagaged, cause an error
415 // by forgetting a required argument.
Ken Mixter207694d2010-10-28 15:42:37 -0700416 }
417
Ken Mixtera3249322011-03-03 08:47:38 -0800418 int errorlevel = core2md.Run();
Ken Mixter777484c2010-07-23 16:22:44 -0700419
420 std::string output;
421 file_util::ReadFileToString(output_path, &output);
422 if (errorlevel != 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800423 LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
424 << " [result=" << errorlevel << "]: " << output;
Ken Mixter777484c2010-07-23 16:22:44 -0700425 return false;
426 }
427
428 if (!file_util::PathExists(minidump_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800429 LOG(ERROR) << "Minidump file " << minidump_path.value()
430 << " was not created";
Ken Mixter777484c2010-07-23 16:22:44 -0700431 return false;
432 }
433 return true;
434}
435
Ben Chan6e709a12012-02-29 12:10:44 -0800436UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
437 pid_t pid,
438 const FilePath &container_dir,
439 const FilePath &core_path,
440 const FilePath &minidump_path) {
Ben Chanec7d7832012-01-09 10:29:58 -0800441 // If proc files are unuable, we continue to read the core file from stdin,
442 // but only skip the core-to-minidump conversion, so that we may still use
443 // the core file for debugging.
444 bool proc_files_usable =
445 CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
446
447 if (!CopyStdinToCoreFile(core_path)) {
Ben Chan6e709a12012-02-29 12:10:44 -0800448 return kErrorReadCoreData;
Ken Mixter777484c2010-07-23 16:22:44 -0700449 }
450
Ben Chanec7d7832012-01-09 10:29:58 -0800451 if (!proc_files_usable) {
452 LOG(INFO) << "Skipped converting core file to minidump due to "
453 << "unusable proc files";
Ben Chan6e709a12012-02-29 12:10:44 -0800454 return kErrorUnusableProcFiles;
Ken Mixter777484c2010-07-23 16:22:44 -0700455 }
456
Ben Chan6e709a12012-02-29 12:10:44 -0800457 ErrorType error = ValidateCoreFile(core_path);
458 if (error != kErrorNone) {
459 return error;
Ken Mixter777484c2010-07-23 16:22:44 -0700460 }
461
Ben Chan6e709a12012-02-29 12:10:44 -0800462 if (!RunCoreToMinidump(core_path,
463 container_dir, // procfs directory
464 minidump_path,
465 container_dir)) { // temporary directory
466 return kErrorCore2MinidumpConversion;
467 }
468
469 LOG(INFO) << "Stored minidump to " << minidump_path.value();
470 return kErrorNone;
Ken Mixter207694d2010-10-28 15:42:37 -0700471}
472
Ben Chan6e709a12012-02-29 12:10:44 -0800473UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
474 int pid, const std::string &exec, bool *out_of_capacity) {
Ken Mixter207694d2010-10-28 15:42:37 -0700475 FilePath crash_path;
476 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800477 LOG(ERROR) << "Unable to find/create process-specific crash path";
Ben Chan6e709a12012-02-29 12:10:44 -0800478 return kErrorSystemIssue;
Ken Mixter207694d2010-10-28 15:42:37 -0700479 }
480
Ben Chan294d5d12012-01-04 20:40:15 -0800481 // Directory like /tmp/crash_reporter/1234 which contains the
Ken Mixter207694d2010-10-28 15:42:37 -0700482 // procfs entries and other temporary files used during conversion.
Ben Chan294d5d12012-01-04 20:40:15 -0800483 FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", pid));
Ken Mixter1b8fe012011-01-25 13:33:05 -0800484 // Delete a pre-existing directory from crash reporter that may have
485 // been left around for diagnostics from a failed conversion attempt.
486 // If we don't, existing files can cause forking to fail.
487 file_util::Delete(container_dir, true);
Ken Mixter207694d2010-10-28 15:42:37 -0700488 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
489 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
490 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
491 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
Ken Mixterc49dbd42010-12-14 17:44:11 -0800492 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
493
Simon Queacc79382012-05-04 18:10:09 -0700494 if (GetLogContents(FilePath(log_config_path_), exec, log_path))
Ken Mixterc49dbd42010-12-14 17:44:11 -0800495 AddCrashMetaData("log", log_path.value());
Ken Mixter207694d2010-10-28 15:42:37 -0700496
Ben Chan6e709a12012-02-29 12:10:44 -0800497 ErrorType error_type =
498 ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
499 if (error_type != kErrorNone) {
Ken Mixtera3249322011-03-03 08:47:38 -0800500 LOG(INFO) << "Leaving core file at " << core_path.value()
501 << " due to conversion error";
Ben Chan6e709a12012-02-29 12:10:44 -0800502 return error_type;
Ken Mixter207694d2010-10-28 15:42:37 -0700503 }
504
505 // Here we commit to sending this file. We must not return false
506 // after this point or we will generate a log report as well as a
507 // crash report.
508 WriteCrashMetaData(meta_path,
509 exec,
510 minidump_path.value());
511
Michael Krebs538ecbf2011-07-27 14:13:22 -0700512 if (!IsDeveloperImage()) {
Ken Mixter777484c2010-07-23 16:22:44 -0700513 file_util::Delete(core_path, false);
514 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800515 LOG(INFO) << "Leaving core file at " << core_path.value()
516 << " due to developer image";
Ken Mixter777484c2010-07-23 16:22:44 -0700517 }
518
Ken Mixter207694d2010-10-28 15:42:37 -0700519 file_util::Delete(container_dir, true);
Ben Chan6e709a12012-02-29 12:10:44 -0800520 return kErrorNone;
Ken Mixter777484c2010-07-23 16:22:44 -0700521}
522
Ken Mixter1b8fe012011-01-25 13:33:05 -0800523bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
524 pid_t *pid, int *signal,
525 std::string *kernel_supplied_name) {
526 pcrecpp::RE re("(\\d+):(\\d+):(.*)");
527 return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name);
528}
529
Michael Krebs2f3ed032012-08-21 20:17:03 -0700530/* Returns true if the given executable name matches that of Chrome. This
531 * includes checks for threads that Chrome has renamed. */
532static bool IsChromeExecName(const std::string &exec) {
533 static const char *kChromeNames[] = {
534 "chrome",
Michael Krebsb1b91a52012-11-26 14:26:17 -0800535 /* These are additional thread names seen in http://crash/ */
536 "MediaPipeline",
Michael Krebs2f3ed032012-08-21 20:17:03 -0700537 /* These come from the use of base::PlatformThread::SetName() directly */
538 "CrBrowserMain", "CrRendererMain", "CrUtilityMain", "CrPPAPIMain",
539 "CrPPAPIBrokerMain", "CrPluginMain", "CrWorkerMain", "CrGpuMain",
540 "BrokerEvent", "CrVideoRenderer", "CrShutdownDetector",
541 "UsbEventHandler", "CrNaClMain", "CrServiceMain",
542 /* These thread names come from the use of base::Thread */
543 "Gamepad polling thread", "Chrome_InProcGpuThread",
544 "Chrome_DragDropThread", "Renderer::FILE", "VC manager",
545 "VideoCaptureModuleImpl", "JavaBridge", "VideoCaptureManagerThread",
546 "Geolocation", "Geolocation_wifi_provider",
547 "Device orientation polling thread", "Chrome_InProcRendererThread",
548 "NetworkChangeNotifier", "Watchdog", "inotify_reader",
549 "cf_iexplore_background_thread", "BrowserWatchdog",
550 "Chrome_HistoryThread", "Chrome_SyncThread", "Chrome_ShellDialogThread",
551 "Printing_Worker", "Chrome_SafeBrowsingThread", "SimpleDBThread",
552 "D-Bus thread", "AudioThread", "NullAudioThread", "V4L2Thread",
553 "ChromotingClientDecodeThread", "Profiling_Flush",
554 "worker_thread_ticker", "AudioMixerAlsa", "AudioMixerCras",
555 "FakeAudioRecordingThread", "CaptureThread",
556 "Chrome_WebSocketproxyThread", "ProcessWatcherThread",
557 "Chrome_CameraThread", "import_thread", "NaCl_IOThread",
558 "Chrome_CloudPrintJobPrintThread", "Chrome_CloudPrintProxyCoreThread",
559 "DaemonControllerFileIO", "ChromotingMainThread",
560 "ChromotingEncodeThread", "ChromotingDesktopThread",
561 "ChromotingIOThread", "ChromotingFileIOThread",
562 "Chrome_libJingle_WorkerThread", "Chrome_ChildIOThread",
563 "GLHelperThread", "RemotingHostPlugin",
564 // "PAC thread #%d", // not easy to check because of "%d"
565 "Chrome_DBThread", "Chrome_WebKitThread", "Chrome_FileThread",
566 "Chrome_FileUserBlockingThread", "Chrome_ProcessLauncherThread",
567 "Chrome_CacheThread", "Chrome_IOThread", "Cache Thread", "File Thread",
568 "ServiceProcess_IO", "ServiceProcess_File",
569 "extension_crash_uploader", "gpu-process_crash_uploader",
570 "plugin_crash_uploader", "renderer_crash_uploader",
571 /* These come from the use of webkit_glue::WebThreadImpl */
572 "Compositor", "Browser Compositor",
573 // "WorkerPool/%d", // not easy to check because of "%d"
574 /* These come from the use of base::Watchdog */
575 "Startup watchdog thread Watchdog", "Shutdown watchdog thread Watchdog",
576 /* These come from the use of AudioDeviceThread::Start */
Michael Krebsa1cc3832012-09-13 13:24:12 -0700577 "AudioDevice", "AudioInputDevice", "AudioOutputDevice",
Michael Krebs2f3ed032012-08-21 20:17:03 -0700578 /* These come from the use of MessageLoopFactory::GetMessageLoop */
579 "GpuVideoDecoder", "RtcVideoDecoderThread", "PipelineThread",
580 "AudioDecoderThread", "VideoDecoderThread",
581 /* These come from the use of MessageLoopFactory::GetMessageLoopProxy */
582 "CaptureVideoDecoderThread", "CaptureVideoDecoder",
583 /* These come from the use of base::SimpleThread */
584 "LocalInputMonitor/%d", // "%d" gets lopped off for kernel-supplied
585 /* These come from the use of base::DelegateSimpleThread */
586 "ipc_channel_nacl reader thread/%d", "plugin_audio_input_thread/%d",
587 "plugin_audio_thread/%d",
588 /* These come from the use of base::SequencedWorkerPool */
589 "BrowserBlockingWorker%d/%d", // "%d" gets lopped off for kernel-supplied
590 };
591 static std::set<std::string> chrome_names;
592
593 /* Initialize a set of chrome names, for efficient lookup */
594 if (chrome_names.empty()) {
595 for (size_t i = 0; i < arraysize(kChromeNames); i++) {
596 std::string check_name(kChromeNames[i]);
597 chrome_names.insert(check_name);
598 // When checking a kernel-supplied name, it should be truncated to 15
599 // chars. See PR_SET_NAME in
600 // http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html,
601 // although that page misleads by saying "16 bytes".
602 chrome_names.insert("supplied_" + std::string(check_name, 0, 15));
603 }
604 }
605
606 return ContainsKey(chrome_names, exec);
607}
608
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700609bool UserCollector::ShouldDump(bool has_owner_consent,
610 bool is_developer,
Michael Krebs4fe30db2011-08-05 13:54:52 -0700611 bool handle_chrome_crashes,
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700612 const std::string &exec,
613 std::string *reason) {
614 reason->clear();
615
616 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
617 // crashes towards user crashes, so user crashes really mean non-Chrome
618 // user-space crashes.
Michael Krebs2f3ed032012-08-21 20:17:03 -0700619 if (!handle_chrome_crashes && IsChromeExecName(exec)) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700620 *reason = "ignoring - chrome crash";
621 return false;
622 }
623
624 // For developer builds, we always want to keep the crash reports unless
625 // we're testing the crash facilities themselves. This overrides
626 // feedback. Crash sending still obeys consent.
Michael Krebs538ecbf2011-07-27 14:13:22 -0700627 if (is_developer) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700628 *reason = "developer build - not testing - always dumping";
629 return true;
630 }
631
632 if (!has_owner_consent) {
633 *reason = "ignoring - no consent";
634 return false;
635 }
636
637 *reason = "handling";
638 return true;
639}
640
Ken Mixter1b8fe012011-01-25 13:33:05 -0800641bool UserCollector::HandleCrash(const std::string &crash_attributes,
642 const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700643 CHECK(initialized_);
Ken Mixter1b8fe012011-01-25 13:33:05 -0800644 int pid = 0;
645 int signal = 0;
646 std::string kernel_supplied_name;
647
648 if (!ParseCrashAttributes(crash_attributes, &pid, &signal,
649 &kernel_supplied_name)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800650 LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800651 return false;
652 }
653
Ken Mixter777484c2010-07-23 16:22:44 -0700654 std::string exec;
655 if (force_exec) {
656 exec.assign(force_exec);
657 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
Ken Mixter1b8fe012011-01-25 13:33:05 -0800658 // If we cannot find the exec name, use the kernel supplied name.
659 // We don't always use the kernel's since it truncates the name to
660 // 16 characters.
661 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700662 }
Ken Mixterc6a58e02010-11-01 18:05:30 -0700663
664 // Allow us to test the crash reporting mechanism successfully even if
665 // other parts of the system crash.
666 if (!FLAGS_filter_in.empty() &&
667 (FLAGS_filter_in == "none" ||
668 FLAGS_filter_in != exec)) {
669 // We use a different format message to make it more obvious in tests
670 // which crashes are test generated and which are real.
Ken Mixtera3249322011-03-03 08:47:38 -0800671 LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
672 << "filter_in=" << FLAGS_filter_in << ".";
Ken Mixterc6a58e02010-11-01 18:05:30 -0700673 return true;
674 }
675
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700676 std::string reason;
677 bool dump = ShouldDump(is_feedback_allowed_function_(),
Michael Krebs538ecbf2011-07-27 14:13:22 -0700678 IsDeveloperImage(),
Michael Krebs4fe30db2011-08-05 13:54:52 -0700679 ShouldHandleChromeCrashes(),
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700680 exec,
681 &reason);
Ken Mixter2105b492010-11-09 16:14:38 -0800682
Ken Mixtera3249322011-03-03 08:47:38 -0800683 LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700684 << "] sig " << signal << " (" << reason << ")";
Chris Sosae4a86032010-06-16 17:08:34 -0700685
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700686 if (dump) {
Chris Sosae4a86032010-06-16 17:08:34 -0700687 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700688
Ken Mixter03403162010-08-18 15:23:16 -0700689 if (generate_diagnostics_) {
Ken Mixter207694d2010-10-28 15:42:37 -0700690 bool out_of_capacity = false;
Ben Chan6e709a12012-02-29 12:10:44 -0800691 ErrorType error_type =
Ken Mixter1b8fe012011-01-25 13:33:05 -0800692 ConvertAndEnqueueCrash(pid, exec, &out_of_capacity);
Ben Chan6e709a12012-02-29 12:10:44 -0800693 if (error_type != kErrorNone) {
Ken Mixter207694d2010-10-28 15:42:37 -0700694 if (!out_of_capacity)
Ben Chan6e709a12012-02-29 12:10:44 -0800695 EnqueueCollectionErrorLog(pid, error_type, exec);
Ken Mixter207694d2010-10-28 15:42:37 -0700696 return false;
697 }
Ken Mixter03403162010-08-18 15:23:16 -0700698 }
Ken Mixter777484c2010-07-23 16:22:44 -0700699 }
Ken Mixter207694d2010-10-28 15:42:37 -0700700
Ken Mixter777484c2010-07-23 16:22:44 -0700701 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700702}