blob: 28c5ce6c91c28fea88fab879321d8243fdce9499 [file] [log] [blame]
Chris Sosae4a86032010-06-16 17:08:34 -07001// Copyright (c) 2010 The Chromium OS 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
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"
Chris Masone8a68c7c2011-05-14 11:44:04 -070020#include "base/string_split.h"
Chris Sosae4a86032010-06-16 17:08:34 -070021#include "base/string_util.h"
Ken Mixtera3249322011-03-03 08:47:38 -080022#include "chromeos/process.h"
23#include "chromeos/syslog_logging.h"
Ken Mixter207694d2010-10-28 15:42:37 -070024#include "gflags/gflags.h"
Chris Sosae4a86032010-06-16 17:08:34 -070025
Ken Mixterc6a58e02010-11-01 18:05:30 -070026#pragma GCC diagnostic ignored "-Wstrict-aliasing"
Ken Mixter1b8fe012011-01-25 13:33:05 -080027DEFINE_bool(core2md_failure, false, "Core2md failure test");
28DEFINE_bool(directory_failure, false, "Spool directory failure test");
Ken Mixterc6a58e02010-11-01 18:05:30 -070029DEFINE_string(filter_in, "",
30 "Ignore all crashes but this for testing");
31#pragma GCC diagnostic error "-Wstrict-aliasing"
Ken Mixter207694d2010-10-28 15:42:37 -070032
33static const char kCollectionErrorSignature[] =
34 "crash_reporter-user-collection";
Chris Sosae4a86032010-06-16 17:08:34 -070035// This procfs file is used to cause kernel core file writing to
36// instead pipe the core file into a user space process. See
37// core(5) man page.
38static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixterc49dbd42010-12-14 17:44:11 -080039static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
40// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
41// crashes, but finite to avoid infinitely recursing on crash handling.
42static const char kCorePipeLimit[] = "4";
Ken Mixter777484c2010-07-23 16:22:44 -070043static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter777484c2010-07-23 16:22:44 -070044
Ken Mixterc49dbd42010-12-14 17:44:11 -080045static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
Ben Chanf13bb582012-01-06 08:22:07 -080046static const char kStatePrefix[] = "State:\t";
Ken Mixterc49dbd42010-12-14 17:44:11 -080047
Ken Mixter777484c2010-07-23 16:22:44 -070048const char *UserCollector::kUserId = "Uid:\t";
49const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070050
51UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070052 : generate_diagnostics_(false),
53 core_pattern_file_(kCorePatternFile),
Ken Mixterc49dbd42010-12-14 17:44:11 -080054 core_pipe_limit_file_(kCorePipeLimitFile),
Ken Mixter03403162010-08-18 15:23:16 -070055 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070056}
57
58void UserCollector::Initialize(
59 UserCollector::CountCrashFunction count_crash_function,
60 const std::string &our_path,
61 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070062 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070063 CrashCollector::Initialize(count_crash_function,
Ken Mixtera3249322011-03-03 08:47:38 -080064 is_feedback_allowed_function);
Chris Sosae4a86032010-06-16 17:08:34 -070065 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070066 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070067 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070068}
69
70UserCollector::~UserCollector() {
71}
72
Ben Chan6e709a12012-02-29 12:10:44 -080073std::string UserCollector::GetErrorTypeSignature(ErrorType error_type) const {
74 switch (error_type) {
75 case kErrorSystemIssue:
76 return "system-issue";
77 case kErrorReadCoreData:
78 return "read-core-data";
79 case kErrorUnusableProcFiles:
80 return "unusable-proc-files";
81 case kErrorInvalidCoreFile:
82 return "invalid-core-file";
83 case kErrorUnsupported32BitCoreFile:
84 return "unsupported-32bit-core-file";
85 case kErrorCore2MinidumpConversion:
86 return "core2md-conversion";
87 default:
88 return "";
89 }
90}
91
Chris Sosae4a86032010-06-16 17:08:34 -070092std::string UserCollector::GetPattern(bool enabled) const {
93 if (enabled) {
Ken Mixter1b8fe012011-01-25 13:33:05 -080094 // Combine the three crash attributes into one parameter to try to reduce
95 // the size of the invocation line for crash_reporter since the kernel
96 // has a fixed-sized (128B) buffer that it will truncate into. Note that
97 // the kernel does not support quoted arguments in core_pattern.
98 return StringPrintf("|%s --user=%%p:%%s:%%e", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -070099 } else {
100 return "core";
101 }
102}
103
104bool UserCollector::SetUpInternal(bool enabled) {
105 CHECK(initialized_);
Ken Mixtera3249322011-03-03 08:47:38 -0800106 LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
107
Ken Mixterc49dbd42010-12-14 17:44:11 -0800108 if (file_util::WriteFile(FilePath(core_pipe_limit_file_),
109 kCorePipeLimit,
110 strlen(kCorePipeLimit)) !=
111 static_cast<int>(strlen(kCorePipeLimit))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800112 LOG(ERROR) << "Unable to write " << core_pipe_limit_file_;
Ken Mixterc49dbd42010-12-14 17:44:11 -0800113 return false;
114 }
Chris Sosae4a86032010-06-16 17:08:34 -0700115 std::string pattern = GetPattern(enabled);
116 if (file_util::WriteFile(FilePath(core_pattern_file_),
117 pattern.c_str(),
118 pattern.length()) !=
119 static_cast<int>(pattern.length())) {
Ken Mixtera3249322011-03-03 08:47:38 -0800120 LOG(ERROR) << "Unable to write " << core_pattern_file_;
Chris Sosae4a86032010-06-16 17:08:34 -0700121 return false;
122 }
123 return true;
124}
125
Ken Mixter777484c2010-07-23 16:22:44 -0700126FilePath UserCollector::GetProcessPath(pid_t pid) {
127 return FilePath(StringPrintf("/proc/%d", pid));
128}
129
130bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
131 FilePath *target) {
132 int max_size = 32;
133 scoped_array<char> buffer;
134 while (true) {
135 buffer.reset(new char[max_size + 1]);
136 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
137 if (size < 0) {
Ken Mixterd49d3622011-02-09 18:23:00 -0800138 int saved_errno = errno;
Ken Mixtera3249322011-03-03 08:47:38 -0800139 LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
140 << saved_errno;
Ken Mixter777484c2010-07-23 16:22:44 -0700141 return false;
142 }
143 buffer[size] = 0;
144 if (size == max_size) {
145 // Avoid overflow when doubling.
146 if (max_size * 2 > max_size) {
147 max_size *= 2;
148 continue;
149 } else {
150 return false;
151 }
152 }
153 break;
154 }
155
156 *target = FilePath(buffer.get());
157 return true;
158}
159
160bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
161 std::string *base_name) {
162 FilePath target;
Ken Mixterd49d3622011-02-09 18:23:00 -0800163 FilePath process_path = GetProcessPath(pid);
164 FilePath exe_path = process_path.Append("exe");
165 if (!GetSymlinkTarget(exe_path, &target)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800166 LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
167 << " DirectoryExists: "
168 << file_util::DirectoryExists(process_path);
Ken Mixterd49d3622011-02-09 18:23:00 -0800169 // Try to further diagnose exe readlink failure cause.
170 struct stat buf;
171 int stat_result = stat(exe_path.value().c_str(), &buf);
172 int saved_errno = errno;
173 if (stat_result < 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800174 LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
175 << " " << saved_errno;
Ken Mixterd49d3622011-02-09 18:23:00 -0800176 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800177 LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
178 << buf.st_mode;
Ken Mixterd49d3622011-02-09 18:23:00 -0800179 }
Ken Mixter777484c2010-07-23 16:22:44 -0700180 return false;
Ken Mixterd49d3622011-02-09 18:23:00 -0800181 }
Ken Mixter777484c2010-07-23 16:22:44 -0700182 *base_name = target.BaseName().value();
183 return true;
184}
185
Ben Chanf13bb582012-01-06 08:22:07 -0800186bool UserCollector::GetFirstLineWithPrefix(
187 const std::vector<std::string> &lines,
188 const char *prefix, std::string *line) {
189 std::vector<std::string>::const_iterator line_iterator;
190 for (line_iterator = lines.begin(); line_iterator != lines.end();
191 ++line_iterator) {
192 if (line_iterator->find(prefix) == 0) {
193 *line = *line_iterator;
194 return true;
195 }
196 }
197 return false;
198}
199
200bool UserCollector::GetIdFromStatus(
201 const char *prefix, IdKind kind,
202 const std::vector<std::string> &status_lines, int *id) {
Ken Mixter777484c2010-07-23 16:22:44 -0700203 // From fs/proc/array.c:task_state(), this file contains:
204 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
Ben Chanf13bb582012-01-06 08:22:07 -0800205 std::string id_line;
206 if (!GetFirstLineWithPrefix(status_lines, prefix, &id_line)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700207 return false;
208 }
Ben Chanf13bb582012-01-06 08:22:07 -0800209 std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
Ken Mixter777484c2010-07-23 16:22:44 -0700210 std::vector<std::string> ids;
Chris Masone3ba6c5b2011-05-13 16:57:09 -0700211 base::SplitString(id_substring, '\t', &ids);
Ken Mixter777484c2010-07-23 16:22:44 -0700212 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
213 return false;
214 }
215 const char *number = ids[kind].c_str();
216 char *end_number = NULL;
217 *id = strtol(number, &end_number, 10);
Ben Chanf13bb582012-01-06 08:22:07 -0800218 if (*end_number != '\0') {
Ken Mixter777484c2010-07-23 16:22:44 -0700219 return false;
Ben Chanf13bb582012-01-06 08:22:07 -0800220 }
221 return true;
222}
223
224bool UserCollector::GetStateFromStatus(
225 const std::vector<std::string> &status_lines, std::string *state) {
226 std::string state_line;
227 if (!GetFirstLineWithPrefix(status_lines, kStatePrefix, &state_line)) {
228 return false;
229 }
230 *state = state_line.substr(strlen(kStatePrefix), std::string::npos);
Ken Mixter777484c2010-07-23 16:22:44 -0700231 return true;
232}
233
Ken Mixter207694d2010-10-28 15:42:37 -0700234void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
Ben Chan6e709a12012-02-29 12:10:44 -0800235 ErrorType error_type,
Ken Mixter207694d2010-10-28 15:42:37 -0700236 const std::string &exec) {
237 FilePath crash_path;
Ken Mixtera3249322011-03-03 08:47:38 -0800238 LOG(INFO) << "Writing conversion problems as separate crash report.";
Ken Mixter207694d2010-10-28 15:42:37 -0700239 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800240 LOG(ERROR) << "Could not even get log directory; out of space?";
Ken Mixter207694d2010-10-28 15:42:37 -0700241 return;
242 }
243 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
Ken Mixtera3249322011-03-03 08:47:38 -0800244 std::string error_log = chromeos::GetLog();
Ken Mixter1b8fe012011-01-25 13:33:05 -0800245 FilePath diag_log_path = GetCrashPath(crash_path, dump_basename, "diaglog");
246 if (GetLogContents(FilePath(kDefaultLogConfig), kCollectionErrorSignature,
247 diag_log_path)) {
248 // We load the contents of diag_log into memory and append it to
249 // the error log. We cannot just append to files because we need
250 // to always create new files to prevent attack.
251 std::string diag_log_contents;
252 file_util::ReadFileToString(diag_log_path, &diag_log_contents);
253 error_log.append(diag_log_contents);
254 file_util::Delete(diag_log_path, false);
255 }
Ken Mixter207694d2010-10-28 15:42:37 -0700256 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
257 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
Ken Mixter9b346472010-11-07 13:45:45 -0800258 // We must use WriteNewFile instead of file_util::WriteFile as we do
259 // not want to write with root access to a symlink that an attacker
260 // might have created.
Ken Mixter1b8fe012011-01-25 13:33:05 -0800261 WriteNewFile(log_path, error_log.data(), error_log.length());
Ken Mixter207694d2010-10-28 15:42:37 -0700262 AddCrashMetaData("sig", kCollectionErrorSignature);
Ben Chan6e709a12012-02-29 12:10:44 -0800263 AddCrashMetaData("error_type", GetErrorTypeSignature(error_type));
Ken Mixter207694d2010-10-28 15:42:37 -0700264 WriteCrashMetaData(meta_path, exec, log_path.value());
265}
266
Ken Mixter777484c2010-07-23 16:22:44 -0700267bool UserCollector::CopyOffProcFiles(pid_t pid,
268 const FilePath &container_dir) {
269 if (!file_util::CreateDirectory(container_dir)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800270 LOG(ERROR) << "Could not create " << container_dir.value().c_str();
Ken Mixter777484c2010-07-23 16:22:44 -0700271 return false;
272 }
273 FilePath process_path = GetProcessPath(pid);
274 if (!file_util::PathExists(process_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800275 LOG(ERROR) << "Path " << process_path.value() << " does not exist";
Ken Mixter777484c2010-07-23 16:22:44 -0700276 return false;
277 }
278 static const char *proc_files[] = {
279 "auxv",
280 "cmdline",
281 "environ",
282 "maps",
283 "status"
284 };
285 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
286 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
287 container_dir.Append(proc_files[i]))) {
Ken Mixtera3249322011-03-03 08:47:38 -0800288 LOG(ERROR) << "Could not copy " << proc_files[i] << " file";
Ken Mixter777484c2010-07-23 16:22:44 -0700289 return false;
290 }
291 }
Ben Chanec7d7832012-01-09 10:29:58 -0800292 return true;
Ben Chanf13bb582012-01-06 08:22:07 -0800293}
294
Ben Chan6e709a12012-02-29 12:10:44 -0800295bool UserCollector::ValidateProcFiles(const FilePath &container_dir) const {
Ben Chanf13bb582012-01-06 08:22:07 -0800296 // Check if the maps file is empty, which could be due to the crashed
297 // process being reaped by the kernel before finishing a core dump.
298 int64 file_size = 0;
299 if (!file_util::GetFileSize(container_dir.Append("maps"), &file_size)) {
300 LOG(ERROR) << "Could not get the size of maps file";
301 return false;
302 }
303 if (file_size == 0) {
304 LOG(ERROR) << "maps file is empty";
305 return false;
306 }
Ken Mixter777484c2010-07-23 16:22:44 -0700307 return true;
308}
309
Ben Chan6e709a12012-02-29 12:10:44 -0800310UserCollector::ErrorType UserCollector::ValidateCoreFile(
311 const FilePath &core_path) const {
312 int fd = HANDLE_EINTR(open(core_path.value().c_str(), O_RDONLY));
313 if (fd < 0) {
314 LOG(ERROR) << "Could not open core file " << core_path.value();
315 return kErrorInvalidCoreFile;
316 }
317
318 char e_ident[EI_NIDENT];
319 bool read_ok = file_util::ReadFromFD(fd, e_ident, sizeof(e_ident));
320 HANDLE_EINTR(close(fd));
321 if (!read_ok) {
322 LOG(ERROR) << "Could not read header of core file";
323 return kErrorInvalidCoreFile;
324 }
325
326 if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
327 e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
328 LOG(ERROR) << "Invalid core file";
329 return kErrorInvalidCoreFile;
330 }
331
332#if __WORDSIZE == 64
333 // TODO(benchan, mkrebs): Remove this check once core2md can
334 // handles both 32-bit and 64-bit ELF on a 64-bit platform.
335 if (e_ident[EI_CLASS] == ELFCLASS32) {
336 LOG(ERROR) << "Conversion of 32-bit core file on 64-bit platform is "
337 << "currently not supported";
338 return kErrorUnsupported32BitCoreFile;
339 }
340#endif
341
342 return kErrorNone;
343}
344
Ken Mixter777484c2010-07-23 16:22:44 -0700345bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
Ken Mixter207694d2010-10-28 15:42:37 -0700346 FilePath *crash_file_path,
347 bool *out_of_capacity) {
Ken Mixter777484c2010-07-23 16:22:44 -0700348 FilePath process_path = GetProcessPath(pid);
349 std::string status;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800350 if (FLAGS_directory_failure) {
Ken Mixtera3249322011-03-03 08:47:38 -0800351 LOG(ERROR) << "Purposefully failing to create spool directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700352 return false;
353 }
Ken Mixter777484c2010-07-23 16:22:44 -0700354 if (!file_util::ReadFileToString(process_path.Append("status"),
355 &status)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800356 LOG(ERROR) << "Could not read status file";
357 LOG(INFO) << "Path " << process_path.value() << " DirectoryExists: "
358 << file_util::DirectoryExists(process_path);
Ken Mixter777484c2010-07-23 16:22:44 -0700359 return false;
360 }
Ben Chanf13bb582012-01-06 08:22:07 -0800361
362 std::vector<std::string> status_lines;
363 base::SplitString(status, '\n', &status_lines);
364
365 std::string process_state;
366 if (!GetStateFromStatus(status_lines, &process_state)) {
367 LOG(ERROR) << "Could not find process state in status file";
368 return false;
369 }
370 LOG(INFO) << "State of crashed process [" << pid << "]: " << process_state;
371
Ken Mixter777484c2010-07-23 16:22:44 -0700372 int process_euid;
Ben Chanf13bb582012-01-06 08:22:07 -0800373 if (!GetIdFromStatus(kUserId, kIdEffective, status_lines, &process_euid)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800374 LOG(ERROR) << "Could not find euid in status file";
Ken Mixter777484c2010-07-23 16:22:44 -0700375 return false;
376 }
Ken Mixter207694d2010-10-28 15:42:37 -0700377 if (!GetCreatedCrashDirectoryByEuid(process_euid,
378 crash_file_path,
379 out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800380 LOG(ERROR) << "Could not create crash directory";
Ken Mixter207694d2010-10-28 15:42:37 -0700381 return false;
382 }
383 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700384}
385
386bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
387 // Copy off all stdin to a core file.
388 FilePath stdin_path("/dev/fd/0");
389 if (file_util::CopyFile(stdin_path, core_path)) {
390 return true;
391 }
392
Ken Mixtera3249322011-03-03 08:47:38 -0800393 LOG(ERROR) << "Could not write core file";
Ken Mixter777484c2010-07-23 16:22:44 -0700394 // If the file system was full, make sure we remove any remnants.
395 file_util::Delete(core_path, false);
396 return false;
397}
398
Ken Mixter207694d2010-10-28 15:42:37 -0700399bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
400 const FilePath &procfs_directory,
401 const FilePath &minidump_path,
402 const FilePath &temp_directory) {
Ken Mixter777484c2010-07-23 16:22:44 -0700403 FilePath output_path = temp_directory.Append("output");
Ken Mixtera3249322011-03-03 08:47:38 -0800404 chromeos::ProcessImpl core2md;
405 core2md.RedirectOutput(output_path.value());
406 core2md.AddArg(kCoreToMinidumpConverterPath);
407 core2md.AddArg(core_path.value());
408 core2md.AddArg(procfs_directory.value());
Ken Mixter2953c3a2010-10-18 14:42:20 -0700409
Ken Mixtera3249322011-03-03 08:47:38 -0800410 if (!FLAGS_core2md_failure) {
411 core2md.AddArg(minidump_path.value());
412 } else {
Ken Mixter207694d2010-10-28 15:42:37 -0700413 // To test how core2md errors are propagaged, cause an error
414 // by forgetting a required argument.
Ken Mixter207694d2010-10-28 15:42:37 -0700415 }
416
Ken Mixtera3249322011-03-03 08:47:38 -0800417 int errorlevel = core2md.Run();
Ken Mixter777484c2010-07-23 16:22:44 -0700418
419 std::string output;
420 file_util::ReadFileToString(output_path, &output);
421 if (errorlevel != 0) {
Ken Mixtera3249322011-03-03 08:47:38 -0800422 LOG(ERROR) << "Problem during " << kCoreToMinidumpConverterPath
423 << " [result=" << errorlevel << "]: " << output;
Ken Mixter777484c2010-07-23 16:22:44 -0700424 return false;
425 }
426
427 if (!file_util::PathExists(minidump_path)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800428 LOG(ERROR) << "Minidump file " << minidump_path.value()
429 << " was not created";
Ken Mixter777484c2010-07-23 16:22:44 -0700430 return false;
431 }
432 return true;
433}
434
Ben Chan6e709a12012-02-29 12:10:44 -0800435UserCollector::ErrorType UserCollector::ConvertCoreToMinidump(
436 pid_t pid,
437 const FilePath &container_dir,
438 const FilePath &core_path,
439 const FilePath &minidump_path) {
Ben Chanec7d7832012-01-09 10:29:58 -0800440 // If proc files are unuable, we continue to read the core file from stdin,
441 // but only skip the core-to-minidump conversion, so that we may still use
442 // the core file for debugging.
443 bool proc_files_usable =
444 CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
445
446 if (!CopyStdinToCoreFile(core_path)) {
Ben Chan6e709a12012-02-29 12:10:44 -0800447 return kErrorReadCoreData;
Ken Mixter777484c2010-07-23 16:22:44 -0700448 }
449
Ben Chanec7d7832012-01-09 10:29:58 -0800450 if (!proc_files_usable) {
451 LOG(INFO) << "Skipped converting core file to minidump due to "
452 << "unusable proc files";
Ben Chan6e709a12012-02-29 12:10:44 -0800453 return kErrorUnusableProcFiles;
Ken Mixter777484c2010-07-23 16:22:44 -0700454 }
455
Ben Chan6e709a12012-02-29 12:10:44 -0800456 ErrorType error = ValidateCoreFile(core_path);
457 if (error != kErrorNone) {
458 return error;
Ken Mixter777484c2010-07-23 16:22:44 -0700459 }
460
Ben Chan6e709a12012-02-29 12:10:44 -0800461 if (!RunCoreToMinidump(core_path,
462 container_dir, // procfs directory
463 minidump_path,
464 container_dir)) { // temporary directory
465 return kErrorCore2MinidumpConversion;
466 }
467
468 LOG(INFO) << "Stored minidump to " << minidump_path.value();
469 return kErrorNone;
Ken Mixter207694d2010-10-28 15:42:37 -0700470}
471
Ben Chan6e709a12012-02-29 12:10:44 -0800472UserCollector::ErrorType UserCollector::ConvertAndEnqueueCrash(
473 int pid, const std::string &exec, bool *out_of_capacity) {
Ken Mixter207694d2010-10-28 15:42:37 -0700474 FilePath crash_path;
475 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800476 LOG(ERROR) << "Unable to find/create process-specific crash path";
Ben Chan6e709a12012-02-29 12:10:44 -0800477 return kErrorSystemIssue;
Ken Mixter207694d2010-10-28 15:42:37 -0700478 }
479
Ben Chan294d5d12012-01-04 20:40:15 -0800480 // Directory like /tmp/crash_reporter/1234 which contains the
Ken Mixter207694d2010-10-28 15:42:37 -0700481 // procfs entries and other temporary files used during conversion.
Ben Chan294d5d12012-01-04 20:40:15 -0800482 FilePath container_dir(StringPrintf("/tmp/crash_reporter/%d", pid));
Ken Mixter1b8fe012011-01-25 13:33:05 -0800483 // Delete a pre-existing directory from crash reporter that may have
484 // been left around for diagnostics from a failed conversion attempt.
485 // If we don't, existing files can cause forking to fail.
486 file_util::Delete(container_dir, true);
Ken Mixter207694d2010-10-28 15:42:37 -0700487 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
488 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
489 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
490 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
Ken Mixterc49dbd42010-12-14 17:44:11 -0800491 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
492
493 if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path))
494 AddCrashMetaData("log", log_path.value());
Ken Mixter207694d2010-10-28 15:42:37 -0700495
Ben Chan6e709a12012-02-29 12:10:44 -0800496 ErrorType error_type =
497 ConvertCoreToMinidump(pid, container_dir, core_path, minidump_path);
498 if (error_type != kErrorNone) {
Ken Mixtera3249322011-03-03 08:47:38 -0800499 LOG(INFO) << "Leaving core file at " << core_path.value()
500 << " due to conversion error";
Ben Chan6e709a12012-02-29 12:10:44 -0800501 return error_type;
Ken Mixter207694d2010-10-28 15:42:37 -0700502 }
503
504 // Here we commit to sending this file. We must not return false
505 // after this point or we will generate a log report as well as a
506 // crash report.
507 WriteCrashMetaData(meta_path,
508 exec,
509 minidump_path.value());
510
Michael Krebs538ecbf2011-07-27 14:13:22 -0700511 if (!IsDeveloperImage()) {
Ken Mixter777484c2010-07-23 16:22:44 -0700512 file_util::Delete(core_path, false);
513 } else {
Ken Mixtera3249322011-03-03 08:47:38 -0800514 LOG(INFO) << "Leaving core file at " << core_path.value()
515 << " due to developer image";
Ken Mixter777484c2010-07-23 16:22:44 -0700516 }
517
Ken Mixter207694d2010-10-28 15:42:37 -0700518 file_util::Delete(container_dir, true);
Ben Chan6e709a12012-02-29 12:10:44 -0800519 return kErrorNone;
Ken Mixter777484c2010-07-23 16:22:44 -0700520}
521
Ken Mixter1b8fe012011-01-25 13:33:05 -0800522bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
523 pid_t *pid, int *signal,
524 std::string *kernel_supplied_name) {
525 pcrecpp::RE re("(\\d+):(\\d+):(.*)");
526 return re.FullMatch(crash_attributes, pid, signal, kernel_supplied_name);
527}
528
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700529bool UserCollector::ShouldDump(bool has_owner_consent,
530 bool is_developer,
Michael Krebs4fe30db2011-08-05 13:54:52 -0700531 bool handle_chrome_crashes,
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700532 const std::string &exec,
533 std::string *reason) {
534 reason->clear();
535
536 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
537 // crashes towards user crashes, so user crashes really mean non-Chrome
538 // user-space crashes.
Michael Krebs538ecbf2011-07-27 14:13:22 -0700539 if ((exec == "chrome" || exec == "supplied_chrome") &&
Michael Krebs4fe30db2011-08-05 13:54:52 -0700540 !handle_chrome_crashes) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700541 *reason = "ignoring - chrome crash";
542 return false;
543 }
544
545 // For developer builds, we always want to keep the crash reports unless
546 // we're testing the crash facilities themselves. This overrides
547 // feedback. Crash sending still obeys consent.
Michael Krebs538ecbf2011-07-27 14:13:22 -0700548 if (is_developer) {
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700549 *reason = "developer build - not testing - always dumping";
550 return true;
551 }
552
553 if (!has_owner_consent) {
554 *reason = "ignoring - no consent";
555 return false;
556 }
557
558 *reason = "handling";
559 return true;
560}
561
Ken Mixter1b8fe012011-01-25 13:33:05 -0800562bool UserCollector::HandleCrash(const std::string &crash_attributes,
563 const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700564 CHECK(initialized_);
Ken Mixter1b8fe012011-01-25 13:33:05 -0800565 int pid = 0;
566 int signal = 0;
567 std::string kernel_supplied_name;
568
569 if (!ParseCrashAttributes(crash_attributes, &pid, &signal,
570 &kernel_supplied_name)) {
Ken Mixtera3249322011-03-03 08:47:38 -0800571 LOG(ERROR) << "Invalid parameter: --user=" << crash_attributes;
Ken Mixter1b8fe012011-01-25 13:33:05 -0800572 return false;
573 }
574
Ken Mixter777484c2010-07-23 16:22:44 -0700575 std::string exec;
576 if (force_exec) {
577 exec.assign(force_exec);
578 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
Ken Mixter1b8fe012011-01-25 13:33:05 -0800579 // If we cannot find the exec name, use the kernel supplied name.
580 // We don't always use the kernel's since it truncates the name to
581 // 16 characters.
582 exec = StringPrintf("supplied_%s", kernel_supplied_name.c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700583 }
Ken Mixterc6a58e02010-11-01 18:05:30 -0700584
585 // Allow us to test the crash reporting mechanism successfully even if
586 // other parts of the system crash.
587 if (!FLAGS_filter_in.empty() &&
588 (FLAGS_filter_in == "none" ||
589 FLAGS_filter_in != exec)) {
590 // We use a different format message to make it more obvious in tests
591 // which crashes are test generated and which are real.
Ken Mixtera3249322011-03-03 08:47:38 -0800592 LOG(WARNING) << "Ignoring crash from " << exec << "[" << pid << "] while "
593 << "filter_in=" << FLAGS_filter_in << ".";
Ken Mixterc6a58e02010-11-01 18:05:30 -0700594 return true;
595 }
596
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700597 std::string reason;
598 bool dump = ShouldDump(is_feedback_allowed_function_(),
Michael Krebs538ecbf2011-07-27 14:13:22 -0700599 IsDeveloperImage(),
Michael Krebs4fe30db2011-08-05 13:54:52 -0700600 ShouldHandleChromeCrashes(),
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700601 exec,
602 &reason);
Ken Mixter2105b492010-11-09 16:14:38 -0800603
Ken Mixtera3249322011-03-03 08:47:38 -0800604 LOG(WARNING) << "Received crash notification for " << exec << "[" << pid
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700605 << "] sig " << signal << " (" << reason << ")";
Chris Sosae4a86032010-06-16 17:08:34 -0700606
Ken Mixter5d3a1a22011-03-16 12:47:20 -0700607 if (dump) {
Chris Sosae4a86032010-06-16 17:08:34 -0700608 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700609
Ken Mixter03403162010-08-18 15:23:16 -0700610 if (generate_diagnostics_) {
Ken Mixter207694d2010-10-28 15:42:37 -0700611 bool out_of_capacity = false;
Ben Chan6e709a12012-02-29 12:10:44 -0800612 ErrorType error_type =
Ken Mixter1b8fe012011-01-25 13:33:05 -0800613 ConvertAndEnqueueCrash(pid, exec, &out_of_capacity);
Ben Chan6e709a12012-02-29 12:10:44 -0800614 if (error_type != kErrorNone) {
Ken Mixter207694d2010-10-28 15:42:37 -0700615 if (!out_of_capacity)
Ben Chan6e709a12012-02-29 12:10:44 -0800616 EnqueueCollectionErrorLog(pid, error_type, exec);
Ken Mixter207694d2010-10-28 15:42:37 -0700617 return false;
618 }
Ken Mixter03403162010-08-18 15:23:16 -0700619 }
Ken Mixter777484c2010-07-23 16:22:44 -0700620 }
Ken Mixter207694d2010-10-28 15:42:37 -0700621
Ken Mixter777484c2010-07-23 16:22:44 -0700622 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700623}