blob: a89ea81697ebb1ae39c791e8048da3df58454526 [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"
Mike Frysinger1a8780d2013-02-14 22:39:57 -050020#include "base/posix/eintr_wrapper.h"
Michael Krebs2f3ed032012-08-21 20:17:03 -070021#include "base/stl_util.h"
Chris Masone8a68c7c2011-05-14 11:44:04 -070022#include "base/string_split.h"
Chris Sosae4a86032010-06-16 17:08:34 -070023#include "base/string_util.h"
Mike Frysinger57b261c2012-04-11 14:47:09 -040024#include "base/stringprintf.h"
Ken Mixtera3249322011-03-03 08:47:38 -080025#include "chromeos/process.h"
26#include "chromeos/syslog_logging.h"
Ken Mixter207694d2010-10-28 15:42:37 -070027#include "gflags/gflags.h"
Chris Sosae4a86032010-06-16 17:08:34 -070028
Ken Mixterc6a58e02010-11-01 18:05:30 -070029#pragma GCC diagnostic ignored "-Wstrict-aliasing"
Ken Mixter1b8fe012011-01-25 13:33:05 -080030DEFINE_bool(core2md_failure, false, "Core2md failure test");
31DEFINE_bool(directory_failure, false, "Spool directory failure test");
Ken Mixterc6a58e02010-11-01 18:05:30 -070032DEFINE_string(filter_in, "",
33 "Ignore all crashes but this for testing");
34#pragma GCC diagnostic error "-Wstrict-aliasing"
Ken Mixter207694d2010-10-28 15:42:37 -070035
36static const char kCollectionErrorSignature[] =
37 "crash_reporter-user-collection";
Chris Sosae4a86032010-06-16 17:08:34 -070038// This procfs file is used to cause kernel core file writing to
39// instead pipe the core file into a user space process. See
40// core(5) man page.
41static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixterc49dbd42010-12-14 17:44:11 -080042static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
43// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
44// crashes, but finite to avoid infinitely recursing on crash handling.
45static const char kCorePipeLimit[] = "4";
Ken Mixter777484c2010-07-23 16:22:44 -070046static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter777484c2010-07-23 16:22:44 -070047
Ben Chanf13bb582012-01-06 08:22:07 -080048static const char kStatePrefix[] = "State:\t";
Ken Mixterc49dbd42010-12-14 17:44:11 -080049
Michael Krebs1c57e9e2012-09-25 18:03:13 -070050// Define an otherwise invalid value that represents an unknown UID.
51static const uid_t kUnknownUid = -1;
52
Ken Mixter777484c2010-07-23 16:22:44 -070053const char *UserCollector::kUserId = "Uid:\t";
54const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070055
Simon Que9f90aca2013-02-19 17:19:52 -080056using base::FilePath;
57
Chris Sosae4a86032010-06-16 17:08:34 -070058UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070059 : generate_diagnostics_(false),
60 core_pattern_file_(kCorePatternFile),
Ken Mixterc49dbd42010-12-14 17:44:11 -080061 core_pipe_limit_file_(kCorePipeLimitFile),
Ken Mixter03403162010-08-18 15:23:16 -070062 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070063}
64
65void UserCollector::Initialize(
66 UserCollector::CountCrashFunction count_crash_function,
67 const std::string &our_path,
68 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070069 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070070 CrashCollector::Initialize(count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080071 is_feedback_allowed_function);
Chris Sosae4a86032010-06-16 17:08:34 -070072 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070073 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070074 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070075}
76
77UserCollector::~UserCollector() {
78}
79
Ben Chan6e709a12012-02-29 12:10:44 -080080std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
81 switch (error_type) {
82 case kErrorSystemIssue:
83 return "system-issue";
84 case kErrorReadCoreData:
85 return "read-core-data";
86 case kErrorUnusableProcFiles:
87 return "unusable-proc-files";
88 case kErrorInvalidCoreFile:
89 return "invalid-core-file";
90 case kErrorUnsupported32BitCoreFile:
91 return "unsupported-32bit-core-file";
92 case kErrorCore2MinidumpConversion:
93 return "core2md-conversion";
94 default:
95 return "";
96 }
97}
98
Michael Krebs1c57e9e2012-09-25 18:03:13 -070099// Return the string that should be used for the kernel's core_pattern file.
100// Note that if you change the format of the enabled pattern, you'll probably
101// also need to change the ParseCrashAttributes() function below, the
102// user_collector_test.cc unittest, and the logging_UserCrash.py autotest.
Chris Sosae4a86032010-06-16 17:08:34 -0700103std::string UserCollector::GetPattern(bool enabled) const {
104 if (enabled) {
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700105 // Combine the four crash attributes into one parameter to try to reduce
106 // the size of the invocation line for crash_reporter, since the kernel
107 // has a fixed-sized (128B) buffer for it (before parameter expansion).
108 // Note that the kernel does not support quoted arguments in core_pattern.
109 return StringPrintf("|%s --user=%%p:%%s:%%u:%%e", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -0700110 } else {
111 return "core";
112 }
113}
114
115bool UserCollector::SetUpInternal(bool enabled) {
116 CHECK(initialized_);
Ken Mixtera3249322011-03-03 08:47:38 -0800117 LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
118
Ken Mixterc49dbd42010-12-14 17:44:11 -0800119 if (file_util::WriteFile(FilePath(core_pipe_limit_file_),
120 kCorePipeLimit,
121 strlen(kCorePipeLimit)) !=
122 static_cast<int>(strlen(kCorePipeLimit))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800123 LOG(ERROR) << "Unable to write " << core_pipe_limit_file_;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800124 return false;
125 }
Chris Sosae4a86032010-06-16 17:08:34 -0700126 std::string pattern = GetPattern(enabled);
127 if (file_util::WriteFile(FilePath(core_pattern_file_),
128 pattern.c_str(),
129 pattern.length()) !=
130 static_cast<int>(pattern.length())) {
Ken Mixtera3249322011-03-03 08:47:38 -0800131 LOG(ERROR) << "Unable to write " << core_pattern_file_;
Chris Sosae4a86032010-06-16 17:08:34 -0700132 return false;
133 }
134 return true;
135}
136
Ken Mixter777484c2010-07-23 16:22:44 -0700137FilePath UserCollector::GetProcessPath(pid_t pid) {
138 return FilePath(StringPrintf("/proc/%d", pid));
139}
140
141bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
142 FilePath *target) {
143 int max_size = 32;
144 scoped_array<char> buffer;
145 while (true) {
146 buffer.reset(new char[max_size + 1]);
147 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
148 if (size < 0) {
Ken Mixterd49d3622011-02-09 18:23:00 -0800149 int saved_errno = errno;
Ken Mixtera3249322011-03-03 08:47:38 -0800150 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
151 << saved_errno;
Ken Mixter777484c2010-07-23 16:22:44 -0700152 return false;
153 }
154 buffer[size] = 0;
155 if (size == max_size) {
156 // Avoid overflow when doubling.
157 if (max_size * 2 > max_size) {
158 max_size *= 2;
159 continue;
160 } else {
161 return false;
162 }
163 }
164 break;
165 }
166
167 *target = FilePath(buffer.get());
168 return true;
169}
170
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700171bool UserCollector::GetExecutableBaseNameFromPid(pid_t pid,
Ken Mixter777484c2010-07-23 16:22:44 -0700172 std::string *base_name) {
173 FilePath target;
Ken Mixterd49d3622011-02-09 18:23:00 -0800174 FilePath process_path = GetProcessPath(pid);
175 FilePath exe_path = process_path.Append("exe");
176 if (!GetSymlinkTarget(exe_path, &target)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800177 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
178 << " DirectoryExists: "
179 << file_util::DirectoryExists(process_path);
Ken Mixterd49d3622011-02-09 18:23:00 -0800180 // Try to further diagnose exe readlink failure cause.
181 struct stat buf;
182 int stat_result = stat(exe_path.value().c_str(), &buf);
183 int saved_errno = errno;
184 if (stat_result < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800185 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
186 << " " << saved_errno;
Ken Mixterd49d3622011-02-09 18:23:00 -0800187 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800188 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
189 << buf.st_mode;
Ken Mixterd49d3622011-02-09 18:23:00 -0800190 }
Ken Mixter777484c2010-07-23 16:22:44 -0700191 return false;
Ken Mixterd49d3622011-02-09 18:23:00 -0800192 }
Ken Mixter777484c2010-07-23 16:22:44 -0700193 *base_name = target.BaseName().value();
194 return true;
195}
196
Ben Chanf13bb582012-01-06 08:22:07 -0800197bool UserCollector::GetFirstLineWithPrefix(
198 const std::vector<std::string> &lines,
199 const char *prefix, std::string *line) {
200 std::vector<std::string>::const_iterator line_iterator;
201 for (line_iterator = lines.begin(); line_iterator != lines.end();
202 ++line_iterator) {
203 if (line_iterator->find(prefix) == 0) {
204 *line = *line_iterator;
205 return true;
206 }
207 }
208 return false;
209}
210
211bool UserCollector::GetIdFromStatus(
212 const char *prefix, IdKind kind,
213 const std::vector<std::string> &status_lines, int *id) {
Ken Mixter777484c2010-07-23 16:22:44 -0700214 // From fs/proc/array.c:task_state(), this file contains:
215 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
Ben Chanf13bb582012-01-06 08:22:07 -0800216 std::string id_line;
217 if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700218 return false;
219 }
Ben Chanf13bb582012-01-06 08:22:07 -0800220 std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
Ken Mixter777484c2010-07-23 16:22:44 -0700221 std::vector<std::string> ids;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700222 base::SplitString(id_substring, '\t', &ids);
Ken Mixter777484c2010-07-23 16:22:44 -0700223 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
224 return false;
225 }
226 const char *number = ids[kind].c_str();
227 char *end_number = NULL;
228 *id = strtol(number, &end_number, 10);
Ben Chanf13bb582012-01-06 08:22:07 -0800229 if (*end_number != '\0') {
Ken Mixter777484c2010-07-23 16:22:44 -0700230 return false;
Ben Chanf13bb582012-01-06 08:22:07 -0800231 }
232 return true;
233}
234
235bool UserCollector::GetStateFromStatus(
236 const std::vector<std::string> &status_lines, std::string *state) {
237 std::string state_line;
238 if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
239 return false;
240 }
241 *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
Ken Mixter777484c2010-07-23 16:22:44 -0700242 return true;
243}
244
Ken Mixter207694d2010-10-28 15:42:37 -0700245void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
Ben Chan6e709a12012-02-29 12:10:44 -0800246 ErrorType error_type,
Ken Mixter207694d2010-10-28 15:42:37 -0700247 const std::string &exec) {
248 FilePath crash_path;
Ken Mixtera3249322011-03-03 08:47:38 -0800249 LOG(INFO) << "Writing conversion problems as separate crash report.";
Ken Mixter207694d2010-10-28 15:42:37 -0700250 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800251 LOG(ERROR) << "Could not even get log directory; out of space?";
Ken Mixter207694d2010-10-28 15:42:37 -0700252 return;
253 }
254 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
Ken Mixtera3249322011-03-03 08:47:38 -0800255 std::string error_log = chromeos::GetLog();
Ken Mixter1b8fe012011-01-25 13:33:05 -0800256 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
Simon Queacc79382012-05-04 18:10:09 -0700257 if (GetLogContents(FilePath(log_config_path_), kCollectionErrorSignature,
Ken Mixter1b8fe012011-01-25 13:33:05 -0800258 diag_log_path)) {
259 // We load the contents of diag_log into memory and append it to
260 // the error log. We cannot just append to files because we need
261 // to always create new files to prevent attack.
262 std::string diag_log_contents;
263 file_util::ReadFileToString(diag_log_path, &diag_log_contents);
264 error_log.append(diag_log_contents);
265 file_util::Delete(diag_log_path, false);
266 }
Ken Mixter207694d2010-10-28 15:42:37 -0700267 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
268 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
Ken Mixter9b346472010-11-07 13:45:45 -0800269 // We must use WriteNewFile instead of file_util::WriteFile as we do
270 // not want to write with root access to a symlink that an attacker
271 // might have created.
Ken Mixter1b8fe012011-01-25 13:33:05 -0800272 WriteNewFile(log_path, error_log.data(), error_log.length());
Ken Mixter207694d2010-10-28 15:42:37 -0700273 AddCrashMetaData("sig", kCollectionErrorSignature);
Ben Chan6e709a12012-02-29 12:10:44 -0800274 AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
Ken Mixter207694d2010-10-28 15:42:37 -0700275 WriteCrashMetaData(meta_path, exec, log_path.value());
276}
277
Ken Mixter777484c2010-07-23 16:22:44 -0700278bool UserCollector::CopyOffProcFiles(pid_t pid,
279 const FilePath &container_dir) {
280 if (!file_util::CreateDirectory(container_dir)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800281 LOG(ERROR) << "Could not create " << container_dir.value().c_str();
Ken Mixter777484c2010-07-23 16:22:44 -0700282 return false;
283 }
284 FilePath process_path = GetProcessPath(pid);
285 if (!file_util::PathExists(process_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800286 LOG(ERROR) << "Path " << process_path.value() << " does not exist";
Ken Mixter777484c2010-07-23 16:22:44 -0700287 return false;
288 }
289 static const char *proc_files[] = {
290 "auxv",
291 "cmdline",
292 "environ",
293 "maps",
294 "status"
295 };
296 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
297 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
298 container_dir.Append(proc_files[i]))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800299 LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
Ken Mixter777484c2010-07-23 16:22:44 -0700300 return false;
301 }
302 }
Ben Chanec7d7832012-01-09 10:29:58 -0800303 return true;
Ben Chanf13bb582012-01-06 08:22:07 -0800304}
305
Ben Chan6e709a12012-02-29 12:10:44 -0800306bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
Ben Chanf13bb582012-01-06 08:22:07 -0800307 // Check if the maps file is empty, which could be due to the crashed
308 // process being reaped by the kernel before finishing a core dump.
309 int64 file_size = 0;
310 if (!file_util::GetFileSize(container_dir.Append("maps"), &file_size)) {
311 LOG(ERROR) << "Could not get the size of maps file";
312 return false;
313 }
314 if (file_size == 0) {
315 LOG(ERROR) << "maps file is empty";
316 return false;
317 }
Ken Mixter777484c2010-07-23 16:22:44 -0700318 return true;
319}
320
Ben Chan6e709a12012-02-29 12:10:44 -0800321UserCollector::ErrorType UserCollector::ValidateCoreFile(
322 const FilePath &core_path) const {
323 int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
324 if (fd < 0) {
325 LOG(ERROR) << "Could not open core file " << core_path.value();
326 return kErrorInvalidCoreFile;
327 }
328
329 char e_ident[EI_NIDENT];
330 bool read_ok = file_util::ReadFromFD(fd, e_ident, sizeof(e_ident));
331 HANDLE_EINTR(close(fd));
332 if (!read_ok) {
333 LOG(ERROR) << "Could not read header of core file";
334 return kErrorInvalidCoreFile;
335 }
336
337 if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
338 e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
339 LOG(ERROR) << "Invalid core file";
340 return kErrorInvalidCoreFile;
341 }
342
343#if __WORDSIZE == 64
344 // TODO(benchan, mkrebs): Remove this check once core2md can
345 // handles both 32-bit and 64-bit ELF on a 64-bit platform.
346 if (e_ident[EI_CLASS] == ELFCLASS32) {
347 LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
348 << "currently not supported";
349 return kErrorUnsupported32BitCoreFile;
350 }
351#endif
352
353 return kErrorNone;
354}
355
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700356bool UserCollector::GetCreatedCrashDirectory(pid_t pid, uid_t supplied_ruid,
Ken Mixter207694d2010-10-28 15:42:37 -0700357 FilePath *crash_file_path,
358 bool *out_of_capacity) {
Ken Mixter777484c2010-07-23 16:22:44 -0700359 FilePath process_path = GetProcessPath(pid);
360 std::string status;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800361 if (FLAGS_directory_failure) {
Ken Mixtera3249322011-03-03 08:47:38 -0800362 LOG(ERROR) << "Purposefully failing to create spool directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700363 return false;
364 }
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700365
366 uid_t uid;
367 if (file_util::ReadFileToString(process_path.Append("status"), &status)) {
368 std::vector<std::string> status_lines;
369 base::SplitString(status, '\n', &status_lines);
370
371 std::string process_state;
372 if (!GetStateFromStatus(status_lines, &process_state)) {
373 LOG(ERROR) << "Could not find process state in status file";
374 return false;
375 }
376 LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
377
378 // Get effective UID of crashing process.
379 int id;
380 if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &id)) {
381 LOG(ERROR) << "Could not find euid in status file";
382 return false;
383 }
384 uid = id;
385 } else if (supplied_ruid != kUnknownUid) {
386 LOG(INFO) << "Using supplied UID " << supplied_ruid
387 << " for crashed process [" << pid
388 << "] due to error reading status file";
389 uid = supplied_ruid;
390 } else {
391 LOG(ERROR) << "Could not read status file and kernel did not supply UID";
Ken Mixtera3249322011-03-03 08:47:38 -0800392 LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
393 << file_util::DirectoryExists(process_path);
Ken Mixter777484c2010-07-23 16:22:44 -0700394 return false;
395 }
Ben Chanf13bb582012-01-06 08:22:07 -0800396
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700397 if (!GetCreatedCrashDirectoryByEuid(uid, crash_file_path, out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800398 LOG(ERROR) << "Could not create crash directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700399 return false;
400 }
401 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700402}
403
404bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
405 // Copy off all stdin to a core file.
406 FilePath stdin_path("/dev/fd/0");
407 if (file_util::CopyFile(stdin_path, core_path)) {
408 return true;
409 }
410
Ken Mixtera3249322011-03-03 08:47:38 -0800411 LOG(ERROR) << "Could not write core file";
Ken Mixter777484c2010-07-23 16:22:44 -0700412 // If the file system was full, make sure we remove any remnants.
413 file_util::Delete(core_path, false);
414 return false;
415}
416
Ken Mixter207694d2010-10-28 15:42:37 -0700417bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
418 const FilePath &procfs_directory,
419 const FilePath &minidump_path,
420 const FilePath &temp_directory) {
Ken Mixter777484c2010-07-23 16:22:44 -0700421 FilePath output_path = temp_directory.Append("output");
Ken Mixtera3249322011-03-03 08:47:38 -0800422 chromeos::ProcessImpl core2md;
423 core2md.RedirectOutput(output_path.value());
424 core2md.AddArg(kCoreToMinidumpConverterPath);
425 core2md.AddArg(core_path.value());
426 core2md.AddArg(procfs_directory.value());
Ken Mixter2953c3a2010-10-18 14:42:20 -0700427
Ken Mixtera3249322011-03-03 08:47:38 -0800428 if (!FLAGS_core2md_failure) {
429 core2md.AddArg(minidump_path.value());
430 } else {
Ken Mixter207694d2010-10-28 15:42:37 -0700431 // To test how core2md errors are propagaged, cause an error
432 // by forgetting a required argument.
Ken Mixter207694d2010-10-28 15:42:37 -0700433 }
434
Ken Mixtera3249322011-03-03 08:47:38 -0800435 int errorlevel = core2md.Run();
Ken Mixter777484c2010-07-23 16:22:44 -0700436
437 std::string output;
438 file_util::ReadFileToString(output_path, &output);
439 if (errorlevel != 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800440 LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
441 << " [result=" << errorlevel << "]: " << output;
Ken Mixter777484c2010-07-23 16:22:44 -0700442 return false;
443 }
444
445 if (!file_util::PathExists(minidump_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800446 LOG(ERROR) << "Minidump file " << minidump_path.value()
447 << " was not created";
Ken Mixter777484c2010-07-23 16:22:44 -0700448 return false;
449 }
450 return true;
451}
452
Ben Chan6e709a12012-02-29 12:10:44 -0800453UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
454 pid_t pid,
455 const FilePath &container_dir,
456 const FilePath &core_path,
457 const FilePath &minidump_path) {
Ben Chanec7d7832012-01-09 10:29:58 -0800458 // If proc files are unuable, we continue to read the core file from stdin,
459 // but only skip the core-to-minidump conversion, so that we may still use
460 // the core file for debugging.
461 bool proc_files_usable =
462 CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
463
464 if (!CopyStdinToCoreFile(core_path)) {
Ben Chan6e709a12012-02-29 12:10:44 -0800465 return kErrorReadCoreData;
Ken Mixter777484c2010-07-23 16:22:44 -0700466 }
467
Ben Chanec7d7832012-01-09 10:29:58 -0800468 if (!proc_files_usable) {
469 LOG(INFO) << "Skipped converting core file to minidump due to "
470 << "unusable proc files";
Ben Chan6e709a12012-02-29 12:10:44 -0800471 return kErrorUnusableProcFiles;
Ken Mixter777484c2010-07-23 16:22:44 -0700472 }
473
Ben Chan6e709a12012-02-29 12:10:44 -0800474 ErrorType error = ValidateCoreFile(core_path);
475 if (error != kErrorNone) {
476 return error;
Ken Mixter777484c2010-07-23 16:22:44 -0700477 }
478
Ben Chan6e709a12012-02-29 12:10:44 -0800479 if (!RunCoreToMinidump(core_path,
480 container_dir, // procfs directory
481 minidump_path,
482 container_dir)) { // temporary directory
483 return kErrorCore2MinidumpConversion;
484 }
485
486 LOG(INFO) << "Stored minidump to " << minidump_path.value();
487 return kErrorNone;
Ken Mixter207694d2010-10-28 15:42:37 -0700488}
489
Ben Chan6e709a12012-02-29 12:10:44 -0800490UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700491 pid_t pid, const std::string &exec, uid_t supplied_ruid,
492 bool *out_of_capacity) {
Ken Mixter207694d2010-10-28 15:42:37 -0700493 FilePath crash_path;
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700494 if (!GetCreatedCrashDirectory(pid, supplied_ruid, &crash_path,
495 out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800496 LOG(ERROR) << "Unable to find/create process-specific crash path";
Ben Chan6e709a12012-02-29 12:10:44 -0800497 return kErrorSystemIssue;
Ken Mixter207694d2010-10-28 15:42:37 -0700498 }
499
Ben Chan294d5d12012-01-04 20:40:15 -0800500 // Directory like /tmp/crash_reporter/1234 which contains the
Ken Mixter207694d2010-10-28 15:42:37 -0700501 // procfs entries and other temporary files used during conversion.
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700502 FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", (int)pid));
Ken Mixter1b8fe012011-01-25 13:33:05 -0800503 // Delete a pre-existing directory from crash reporter that may have
504 // been left around for diagnostics from a failed conversion attempt.
505 // If we don't, existing files can cause forking to fail.
506 file_util::Delete(container_dir, true);
Ken Mixter207694d2010-10-28 15:42:37 -0700507 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
508 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
509 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
510 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
Ken Mixterc49dbd42010-12-14 17:44:11 -0800511 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
512
Simon Queacc79382012-05-04 18:10:09 -0700513 if (GetLogContents(FilePath(log_config_path_), exec, log_path))
Ken Mixterc49dbd42010-12-14 17:44:11 -0800514 AddCrashMetaData("log", log_path.value());
Ken Mixter207694d2010-10-28 15:42:37 -0700515
Ben Chan6e709a12012-02-29 12:10:44 -0800516 ErrorType error_type =
517 ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
518 if (error_type != kErrorNone) {
Ken Mixtera3249322011-03-03 08:47:38 -0800519 LOG(INFO) << "Leaving core file at " << core_path.value()
520 << " due to conversion error";
Ben Chan6e709a12012-02-29 12:10:44 -0800521 return error_type;
Ken Mixter207694d2010-10-28 15:42:37 -0700522 }
523
524 // Here we commit to sending this file. We must not return false
525 // after this point or we will generate a log report as well as a
526 // crash report.
527 WriteCrashMetaData(meta_path,
528 exec,
529 minidump_path.value());
530
Michael Krebs538ecbf2011-07-27 14:13:22 -0700531 if (!IsDeveloperImage()) {
Ken Mixter777484c2010-07-23 16:22:44 -0700532 file_util::Delete(core_path, false);
533 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800534 LOG(INFO) << "Leaving core file at " << core_path.value()
535 << " due to developer image";
Ken Mixter777484c2010-07-23 16:22:44 -0700536 }
537
Ken Mixter207694d2010-10-28 15:42:37 -0700538 file_util::Delete(container_dir, true);
Ben Chan6e709a12012-02-29 12:10:44 -0800539 return kErrorNone;
Ken Mixter777484c2010-07-23 16:22:44 -0700540}
541
Ken Mixter1b8fe012011-01-25 13:33:05 -0800542bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700543 pid_t *pid, int *signal, uid_t *uid,
Ken Mixter1b8fe012011-01-25 13:33:05 -0800544 std::string *kernel_supplied_name) {
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700545 pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)");
546 if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name))
547 return true;
548
549 LOG(INFO) << "Falling back to parsing crash attributes '"
550 << crash_attributes << "' without UID";
551 pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
552 *uid = kUnknownUid;
553 return re_without_uid.FullMatch(crash_attributes, pid, signal,
554 kernel_supplied_name);
Ken Mixter1b8fe012011-01-25 13:33:05 -0800555}
556
Michael Krebs2f3ed032012-08-21 20:17:03 -0700557/* Returns true if the given executable name matches that of Chrome. This
558 * includes checks for threads that Chrome has renamed. */
559static bool IsChromeExecName(const std::string &exec) {
560 static const char *kChromeNames[] = {
561 "chrome",
Michael Krebsb1b91a52012-11-26 14:26:17 -0800562 /* These are additional thread names seen in http://crash/ */
563 "MediaPipeline",
Michael Krebs2f3ed032012-08-21 20:17:03 -0700564 /* These come from the use of base::PlatformThread::SetName() directly */
565 "CrBrowserMain", "CrRendererMain", "CrUtilityMain", "CrPPAPIMain",
566 "CrPPAPIBrokerMain", "CrPluginMain", "CrWorkerMain", "CrGpuMain",
567 "BrokerEvent", "CrVideoRenderer", "CrShutdownDetector",
568 "UsbEventHandler", "CrNaClMain", "CrServiceMain",
569 /* These thread names come from the use of base::Thread */
570 "Gamepad polling thread", "Chrome_InProcGpuThread",
571 "Chrome_DragDropThread", "Renderer::FILE", "VC manager",
572 "VideoCaptureModuleImpl", "JavaBridge", "VideoCaptureManagerThread",
573 "Geolocation", "Geolocation_wifi_provider",
574 "Device orientation polling thread", "Chrome_InProcRendererThread",
575 "NetworkChangeNotifier", "Watchdog", "inotify_reader",
576 "cf_iexplore_background_thread", "BrowserWatchdog",
577 "Chrome_HistoryThread", "Chrome_SyncThread", "Chrome_ShellDialogThread",
578 "Printing_Worker", "Chrome_SafeBrowsingThread", "SimpleDBThread",
579 "D-Bus thread", "AudioThread", "NullAudioThread", "V4L2Thread",
580 "ChromotingClientDecodeThread", "Profiling_Flush",
581 "worker_thread_ticker", "AudioMixerAlsa", "AudioMixerCras",
582 "FakeAudioRecordingThread", "CaptureThread",
583 "Chrome_WebSocketproxyThread", "ProcessWatcherThread",
584 "Chrome_CameraThread", "import_thread", "NaCl_IOThread",
585 "Chrome_CloudPrintJobPrintThread", "Chrome_CloudPrintProxyCoreThread",
586 "DaemonControllerFileIO", "ChromotingMainThread",
587 "ChromotingEncodeThread", "ChromotingDesktopThread",
588 "ChromotingIOThread", "ChromotingFileIOThread",
589 "Chrome_libJingle_WorkerThread", "Chrome_ChildIOThread",
590 "GLHelperThread", "RemotingHostPlugin",
591 // "PAC thread #%d", // not easy to check because of "%d"
592 "Chrome_DBThread", "Chrome_WebKitThread", "Chrome_FileThread",
593 "Chrome_FileUserBlockingThread", "Chrome_ProcessLauncherThread",
594 "Chrome_CacheThread", "Chrome_IOThread", "Cache Thread", "File Thread",
595 "ServiceProcess_IO", "ServiceProcess_File",
596 "extension_crash_uploader", "gpu-process_crash_uploader",
597 "plugin_crash_uploader", "renderer_crash_uploader",
598 /* These come from the use of webkit_glue::WebThreadImpl */
599 "Compositor", "Browser Compositor",
600 // "WorkerPool/%d", // not easy to check because of "%d"
601 /* These come from the use of base::Watchdog */
602 "Startup watchdog thread Watchdog", "Shutdown watchdog thread Watchdog",
603 /* These come from the use of AudioDeviceThread::Start */
Michael Krebsa1cc3832012-09-13 13:24:12 -0700604 "AudioDevice", "AudioInputDevice", "AudioOutputDevice",
Michael Krebs2f3ed032012-08-21 20:17:03 -0700605 /* These come from the use of MessageLoopFactory::GetMessageLoop */
606 "GpuVideoDecoder", "RtcVideoDecoderThread", "PipelineThread",
607 "AudioDecoderThread", "VideoDecoderThread",
608 /* These come from the use of MessageLoopFactory::GetMessageLoopProxy */
609 "CaptureVideoDecoderThread", "CaptureVideoDecoder",
610 /* These come from the use of base::SimpleThread */
611 "LocalInputMonitor/%d", // "%d" gets lopped off for kernel-supplied
612 /* These come from the use of base::DelegateSimpleThread */
613 "ipc_channel_nacl reader thread/%d", "plugin_audio_input_thread/%d",
614 "plugin_audio_thread/%d",
615 /* These come from the use of base::SequencedWorkerPool */
616 "BrowserBlockingWorker%d/%d", // "%d" gets lopped off for kernel-supplied
617 };
618 static std::set<std::string> chrome_names;
619
620 /* Initialize a set of chrome names, for efficient lookup */
621 if (chrome_names.empty()) {
622 for (size_t i = 0; i < arraysize(kChromeNames); i++) {
623 std::string check_name(kChromeNames[i]);
624 chrome_names.insert(check_name);
625 // When checking a kernel-supplied name, it should be truncated to 15
626 // chars. See PR_SET_NAME in
627 // http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html,
628 // although that page misleads by saying "16 bytes".
629 chrome_names.insert("supplied_" + std::string(check_name, 0, 15));
630 }
631 }
632
633 return ContainsKey(chrome_names, exec);
634}
635
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700636bool UserCollector::ShouldDump(bool has_owner_consent,
637 bool is_developer,
Michael Krebs4fe30db2011-08-05 13:54:52 -0700638 bool handle_chrome_crashes,
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700639 const std::string &exec,
640 std::string *reason) {
641 reason->clear();
642
643 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
644 // crashes towards user crashes, so user crashes really mean non-Chrome
645 // user-space crashes.
Michael Krebs2f3ed032012-08-21 20:17:03 -0700646 if (!handle_chrome_crashes && IsChromeExecName(exec)) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700647 *reason = "ignoring - chrome crash";
648 return false;
649 }
650
651 // For developer builds, we always want to keep the crash reports unless
652 // we're testing the crash facilities themselves. This overrides
653 // feedback. Crash sending still obeys consent.
Michael Krebs538ecbf2011-07-27 14:13:22 -0700654 if (is_developer) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700655 *reason = "developer build - not testing - always dumping";
656 return true;
657 }
658
659 if (!has_owner_consent) {
660 *reason = "ignoring - no consent";
661 return false;
662 }
663
664 *reason = "handling";
665 return true;
666}
667
Ken Mixter1b8fe012011-01-25 13:33:05 -0800668bool UserCollector::HandleCrash(const std::string &crash_attributes,
669 const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700670 CHECK(initialized_);
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700671 pid_t pid = 0;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800672 int signal = 0;
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700673 uid_t supplied_ruid = kUnknownUid;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800674 std::string kernel_supplied_name;
675
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700676 if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
Ken Mixter1b8fe012011-01-25 13:33:05 -0800677 &kernel_supplied_name)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800678 LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800679 return false;
680 }
681
Ken Mixter777484c2010-07-23 16:22:44 -0700682 std::string exec;
683 if (force_exec) {
684 exec.assign(force_exec);
685 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
Ken Mixter1b8fe012011-01-25 13:33:05 -0800686 // If we cannot find the exec name, use the kernel supplied name.
687 // We don't always use the kernel's since it truncates the name to
688 // 16 characters.
689 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700690 }
Ken Mixterc6a58e02010-11-01 18:05:30 -0700691
692 // Allow us to test the crash reporting mechanism successfully even if
693 // other parts of the system crash.
694 if (!FLAGS_filter_in.empty() &&
695 (FLAGS_filter_in == "none" ||
696 FLAGS_filter_in != exec)) {
697 // We use a different format message to make it more obvious in tests
698 // which crashes are test generated and which are real.
Ken Mixtera3249322011-03-03 08:47:38 -0800699 LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
700 << "filter_in=" << FLAGS_filter_in << ".";
Ken Mixterc6a58e02010-11-01 18:05:30 -0700701 return true;
702 }
703
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700704 std::string reason;
705 bool dump = ShouldDump(is_feedback_allowed_function_(),
Michael Krebs538ecbf2011-07-27 14:13:22 -0700706 IsDeveloperImage(),
Michael Krebs4fe30db2011-08-05 13:54:52 -0700707 ShouldHandleChromeCrashes(),
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700708 exec,
709 &reason);
Ken Mixter2105b492010-11-09 16:14:38 -0800710
Ken Mixtera3249322011-03-03 08:47:38 -0800711 LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700712 << "] sig " << signal << ", user " << supplied_ruid
713 << " (" << reason << ")";
Chris Sosae4a86032010-06-16 17:08:34 -0700714
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700715 if (dump) {
Chris Sosae4a86032010-06-16 17:08:34 -0700716 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700717
Ken Mixter03403162010-08-18 15:23:16 -0700718 if (generate_diagnostics_) {
Ken Mixter207694d2010-10-28 15:42:37 -0700719 bool out_of_capacity = false;
Ben Chan6e709a12012-02-29 12:10:44 -0800720 ErrorType error_type =
Michael Krebs1c57e9e2012-09-25 18:03:13 -0700721 ConvertAndEnqueueCrash(pid, exec, supplied_ruid, &out_of_capacity);
Ben Chan6e709a12012-02-29 12:10:44 -0800722 if (error_type != kErrorNone) {
Ken Mixter207694d2010-10-28 15:42:37 -0700723 if (!out_of_capacity)
Ben Chan6e709a12012-02-29 12:10:44 -0800724 EnqueueCollectionErrorLog(pid, error_type, exec);
Ken Mixter207694d2010-10-28 15:42:37 -0700725 return false;
726 }
Ken Mixter03403162010-08-18 15:23:16 -0700727 }
Ken Mixter777484c2010-07-23 16:22:44 -0700728 }
Ken Mixter207694d2010-10-28 15:42:37 -0700729
Ken Mixter777484c2010-07-23 16:22:44 -0700730 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700731}