blob: 59b460bdca6ae261cfa8bfe2890170a5fc91c2f5 [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
Ken Mixter777484c2010-07-23 16:22:44 -07007#include <grp.h> // For struct group.
8#include <pwd.h> // For struct passwd.
Ken Mixter2953c3a2010-10-18 14:42:20 -07009#include <sys/types.h> // For getpwuid_r, getgrnam_r, WEXITSTATUS.
Ken Mixter777484c2010-07-23 16:22:44 -070010
Chris Sosae4a86032010-06-16 17:08:34 -070011#include <string>
Ken Mixter2953c3a2010-10-18 14:42:20 -070012#include <vector>
Chris Sosae4a86032010-06-16 17:08:34 -070013
14#include "base/file_util.h"
15#include "base/logging.h"
16#include "base/string_util.h"
Ken Mixter03403162010-08-18 15:23:16 -070017#include "crash-reporter/system_logging.h"
Ken Mixter207694d2010-10-28 15:42:37 -070018#include "gflags/gflags.h"
Chris Sosae4a86032010-06-16 17:08:34 -070019
Ken Mixterc6a58e02010-11-01 18:05:30 -070020#pragma GCC diagnostic ignored "-Wstrict-aliasing"
Ken Mixter207694d2010-10-28 15:42:37 -070021DEFINE_bool(core2md_failure_test, false, "Core2md failure test");
22DEFINE_bool(directory_failure_test, false, "Spool directory failure test");
Ken Mixterc6a58e02010-11-01 18:05:30 -070023DEFINE_string(filter_in, "",
24 "Ignore all crashes but this for testing");
25#pragma GCC diagnostic error "-Wstrict-aliasing"
Ken Mixter207694d2010-10-28 15:42:37 -070026
27static const char kCollectionErrorSignature[] =
28 "crash_reporter-user-collection";
Chris Sosae4a86032010-06-16 17:08:34 -070029// This procfs file is used to cause kernel core file writing to
30// instead pipe the core file into a user space process. See
31// core(5) man page.
32static const char kCorePatternFile[] = "/proc/sys/kernel/core_pattern";
Ken Mixterc49dbd42010-12-14 17:44:11 -080033static const char kCorePipeLimitFile[] = "/proc/sys/kernel/core_pipe_limit";
34// Set core_pipe_limit to 4 so that we can catch a few unrelated concurrent
35// crashes, but finite to avoid infinitely recursing on crash handling.
36static const char kCorePipeLimit[] = "4";
Ken Mixter777484c2010-07-23 16:22:44 -070037static const char kCoreToMinidumpConverterPath[] = "/usr/bin/core2md";
Ken Mixter7ac7a702010-08-13 15:43:34 -070038static const char kLeaveCoreFile[] = "/root/.leave_core";
Ken Mixter777484c2010-07-23 16:22:44 -070039
Ken Mixterc49dbd42010-12-14 17:44:11 -080040static const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
41
Ken Mixter777484c2010-07-23 16:22:44 -070042const char *UserCollector::kUserId = "Uid:\t";
43const char *UserCollector::kGroupId = "Gid:\t";
Chris Sosae4a86032010-06-16 17:08:34 -070044
45UserCollector::UserCollector()
Ken Mixter777484c2010-07-23 16:22:44 -070046 : generate_diagnostics_(false),
47 core_pattern_file_(kCorePatternFile),
Ken Mixterc49dbd42010-12-14 17:44:11 -080048 core_pipe_limit_file_(kCorePipeLimitFile),
Ken Mixter03403162010-08-18 15:23:16 -070049 initialized_(false) {
Chris Sosae4a86032010-06-16 17:08:34 -070050}
51
52void UserCollector::Initialize(
53 UserCollector::CountCrashFunction count_crash_function,
54 const std::string &our_path,
55 UserCollector::IsFeedbackAllowedFunction is_feedback_allowed_function,
Ken Mixter777484c2010-07-23 16:22:44 -070056 SystemLogging *logger,
57 bool generate_diagnostics) {
Ken Mixter03403162010-08-18 15:23:16 -070058 CrashCollector::Initialize(count_crash_function,
59 is_feedback_allowed_function,
60 logger);
Chris Sosae4a86032010-06-16 17:08:34 -070061 our_path_ = our_path;
Chris Sosae4a86032010-06-16 17:08:34 -070062 initialized_ = true;
Ken Mixter777484c2010-07-23 16:22:44 -070063 generate_diagnostics_ = generate_diagnostics;
Chris Sosae4a86032010-06-16 17:08:34 -070064}
65
66UserCollector::~UserCollector() {
67}
68
69std::string UserCollector::GetPattern(bool enabled) const {
70 if (enabled) {
Ken Mixter777484c2010-07-23 16:22:44 -070071 return StringPrintf("|%s --signal=%%s --pid=%%p", our_path_.c_str());
Chris Sosae4a86032010-06-16 17:08:34 -070072 } else {
73 return "core";
74 }
75}
76
77bool UserCollector::SetUpInternal(bool enabled) {
78 CHECK(initialized_);
Ken Mixter03403162010-08-18 15:23:16 -070079 logger_->LogInfo("%s user crash handling",
80 enabled ? "Enabling" : "Disabling");
Ken Mixterc49dbd42010-12-14 17:44:11 -080081 if (file_util::WriteFile(FilePath(core_pipe_limit_file_),
82 kCorePipeLimit,
83 strlen(kCorePipeLimit)) !=
84 static_cast<int>(strlen(kCorePipeLimit))) {
85 logger_->LogError("Unable to write %s", core_pipe_limit_file_.c_str());
86 return false;
87 }
Chris Sosae4a86032010-06-16 17:08:34 -070088 std::string pattern = GetPattern(enabled);
89 if (file_util::WriteFile(FilePath(core_pattern_file_),
90 pattern.c_str(),
91 pattern.length()) !=
92 static_cast<int>(pattern.length())) {
93 logger_->LogError("Unable to write %s", core_pattern_file_.c_str());
94 return false;
95 }
96 return true;
97}
98
Ken Mixter777484c2010-07-23 16:22:44 -070099FilePath UserCollector::GetProcessPath(pid_t pid) {
100 return FilePath(StringPrintf("/proc/%d", pid));
101}
102
103bool UserCollector::GetSymlinkTarget(const FilePath &symlink,
104 FilePath *target) {
105 int max_size = 32;
106 scoped_array<char> buffer;
107 while (true) {
108 buffer.reset(new char[max_size + 1]);
109 ssize_t size = readlink(symlink.value().c_str(), buffer.get(), max_size);
110 if (size < 0) {
111 return false;
112 }
113 buffer[size] = 0;
114 if (size == max_size) {
115 // Avoid overflow when doubling.
116 if (max_size * 2 > max_size) {
117 max_size *= 2;
118 continue;
119 } else {
120 return false;
121 }
122 }
123 break;
124 }
125
126 *target = FilePath(buffer.get());
127 return true;
128}
129
130bool UserCollector::GetExecutableBaseNameFromPid(uid_t pid,
131 std::string *base_name) {
132 FilePath target;
133 if (!GetSymlinkTarget(GetProcessPath(pid).Append("exe"), &target))
134 return false;
135 *base_name = target.BaseName().value();
136 return true;
137}
138
139bool UserCollector::GetIdFromStatus(const char *prefix,
140 IdKind kind,
141 const std::string &status_contents,
142 int *id) {
143 // From fs/proc/array.c:task_state(), this file contains:
144 // \nUid:\t<uid>\t<euid>\t<suid>\t<fsuid>\n
145 std::vector<std::string> status_lines;
146 SplitString(status_contents, '\n', &status_lines);
147 std::vector<std::string>::iterator line_iterator;
148 for (line_iterator = status_lines.begin();
149 line_iterator != status_lines.end();
150 ++line_iterator) {
151 if (line_iterator->find(prefix) == 0)
152 break;
153 }
154 if (line_iterator == status_lines.end()) {
155 return false;
156 }
157 std::string id_substring = line_iterator->substr(strlen(prefix),
158 std::string::npos);
159 std::vector<std::string> ids;
160 SplitString(id_substring, '\t', &ids);
161 if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
162 return false;
163 }
164 const char *number = ids[kind].c_str();
165 char *end_number = NULL;
166 *id = strtol(number, &end_number, 10);
167 if (*end_number != '\0')
168 return false;
169 return true;
170}
171
Ken Mixter207694d2010-10-28 15:42:37 -0700172void UserCollector::LogCollectionError(const std::string &error_message) {
173 error_log_.append(error_message.c_str());
174 error_log_.append("\n");
175 logger_->LogError(error_message.c_str());
176}
177
178void UserCollector::EnqueueCollectionErrorLog(pid_t pid,
179 const std::string &exec) {
180 FilePath crash_path;
181 logger_->LogInfo("Writing conversion problems as separate crash report.");
182 if (!GetCreatedCrashDirectoryByEuid(0, &crash_path, NULL)) {
183 logger_->LogError("Could not even get log directory; out of space?");
184 return;
185 }
186 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
187 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
188 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
Ken Mixter9b346472010-11-07 13:45:45 -0800189 // We must use WriteNewFile instead of file_util::WriteFile as we do
190 // not want to write with root access to a symlink that an attacker
191 // might have created.
192 WriteNewFile(log_path, error_log_.data(), error_log_.length());
Ken Mixter207694d2010-10-28 15:42:37 -0700193 AddCrashMetaData("sig", kCollectionErrorSignature);
194 WriteCrashMetaData(meta_path, exec, log_path.value());
195}
196
Ken Mixter777484c2010-07-23 16:22:44 -0700197bool UserCollector::CopyOffProcFiles(pid_t pid,
198 const FilePath &container_dir) {
199 if (!file_util::CreateDirectory(container_dir)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700200 LogCollectionError(StringPrintf("Could not create %s",
201 container_dir.value().c_str()));
Ken Mixter777484c2010-07-23 16:22:44 -0700202 return false;
203 }
204 FilePath process_path = GetProcessPath(pid);
205 if (!file_util::PathExists(process_path)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700206 LogCollectionError(StringPrintf("Path %s does not exist",
207 process_path.value().c_str()));
Ken Mixter777484c2010-07-23 16:22:44 -0700208 return false;
209 }
210 static const char *proc_files[] = {
211 "auxv",
212 "cmdline",
213 "environ",
214 "maps",
215 "status"
216 };
217 for (unsigned i = 0; i < arraysize(proc_files); ++i) {
218 if (!file_util::CopyFile(process_path.Append(proc_files[i]),
219 container_dir.Append(proc_files[i]))) {
Ken Mixter207694d2010-10-28 15:42:37 -0700220 LogCollectionError(StringPrintf("Could not copy %s file",
221 proc_files[i]));
Ken Mixter777484c2010-07-23 16:22:44 -0700222 return false;
223 }
224 }
225 return true;
226}
227
Ken Mixter777484c2010-07-23 16:22:44 -0700228bool UserCollector::GetCreatedCrashDirectory(pid_t pid,
Ken Mixter207694d2010-10-28 15:42:37 -0700229 FilePath *crash_file_path,
230 bool *out_of_capacity) {
Ken Mixter777484c2010-07-23 16:22:44 -0700231 FilePath process_path = GetProcessPath(pid);
232 std::string status;
Ken Mixter207694d2010-10-28 15:42:37 -0700233 if (FLAGS_directory_failure_test) {
234 LogCollectionError("Purposefully failing to create spool directory");
235 return false;
236 }
Ken Mixter777484c2010-07-23 16:22:44 -0700237 if (!file_util::ReadFileToString(process_path.Append("status"),
238 &status)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700239 LogCollectionError("Could not read status file");
Ken Mixter777484c2010-07-23 16:22:44 -0700240 return false;
241 }
242 int process_euid;
243 if (!GetIdFromStatus(kUserId, kIdEffective, status, &process_euid)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700244 LogCollectionError("Could not find euid in status file");
Ken Mixter777484c2010-07-23 16:22:44 -0700245 return false;
246 }
Ken Mixter207694d2010-10-28 15:42:37 -0700247 if (!GetCreatedCrashDirectoryByEuid(process_euid,
248 crash_file_path,
249 out_of_capacity)) {
250 LogCollectionError("Could not create crash directory");
251 return false;
252 }
253 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700254}
255
256bool UserCollector::CopyStdinToCoreFile(const FilePath &core_path) {
257 // Copy off all stdin to a core file.
258 FilePath stdin_path("/dev/fd/0");
259 if (file_util::CopyFile(stdin_path, core_path)) {
260 return true;
261 }
262
Ken Mixter207694d2010-10-28 15:42:37 -0700263 LogCollectionError("Could not write core file");
Ken Mixter777484c2010-07-23 16:22:44 -0700264 // If the file system was full, make sure we remove any remnants.
265 file_util::Delete(core_path, false);
266 return false;
267}
268
Ken Mixter207694d2010-10-28 15:42:37 -0700269bool UserCollector::RunCoreToMinidump(const FilePath &core_path,
270 const FilePath &procfs_directory,
271 const FilePath &minidump_path,
272 const FilePath &temp_directory) {
Ken Mixter777484c2010-07-23 16:22:44 -0700273 FilePath output_path = temp_directory.Append("output");
Ken Mixter2953c3a2010-10-18 14:42:20 -0700274 std::vector<const char *> core2md_arguments;
275 core2md_arguments.push_back(kCoreToMinidumpConverterPath);
276 core2md_arguments.push_back(core_path.value().c_str());
277 core2md_arguments.push_back(procfs_directory.value().c_str());
278 core2md_arguments.push_back(minidump_path.value().c_str());
279
Ken Mixter207694d2010-10-28 15:42:37 -0700280 if (FLAGS_core2md_failure_test) {
281 // To test how core2md errors are propagaged, cause an error
282 // by forgetting a required argument.
283 core2md_arguments.pop_back();
284 }
285
Ken Mixter2953c3a2010-10-18 14:42:20 -0700286 int errorlevel = ForkExecAndPipe(core2md_arguments,
287 output_path.value().c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700288
289 std::string output;
290 file_util::ReadFileToString(output_path, &output);
291 if (errorlevel != 0) {
Ken Mixter207694d2010-10-28 15:42:37 -0700292 LogCollectionError(StringPrintf("Problem during %s [result=%d]: %s",
293 kCoreToMinidumpConverterPath,
294 errorlevel,
295 output.c_str()));
Ken Mixter777484c2010-07-23 16:22:44 -0700296 return false;
297 }
298
299 if (!file_util::PathExists(minidump_path)) {
Ken Mixter207694d2010-10-28 15:42:37 -0700300 LogCollectionError(StringPrintf("Minidump file %s was not created",
301 minidump_path.value().c_str()));
Ken Mixter777484c2010-07-23 16:22:44 -0700302 return false;
303 }
304 return true;
305}
306
Ken Mixter207694d2010-10-28 15:42:37 -0700307bool UserCollector::ConvertCoreToMinidump(pid_t pid,
308 const FilePath &container_dir,
309 const FilePath &core_path,
310 const FilePath &minidump_path) {
Ken Mixter777484c2010-07-23 16:22:44 -0700311 if (!CopyOffProcFiles(pid, container_dir)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700312 return false;
313 }
314
Ken Mixter777484c2010-07-23 16:22:44 -0700315 if (!CopyStdinToCoreFile(core_path)) {
Ken Mixter777484c2010-07-23 16:22:44 -0700316 return false;
317 }
318
Ken Mixter207694d2010-10-28 15:42:37 -0700319 bool conversion_result = RunCoreToMinidump(
320 core_path,
321 container_dir, // procfs directory
322 minidump_path,
323 container_dir); // temporary directory
Ken Mixteree849c52010-09-30 15:30:10 -0700324
Ken Mixter777484c2010-07-23 16:22:44 -0700325 if (conversion_result) {
326 logger_->LogInfo("Stored minidump to %s", minidump_path.value().c_str());
327 }
328
Ken Mixter207694d2010-10-28 15:42:37 -0700329 return conversion_result;
330}
331
332bool UserCollector::ConvertAndEnqueueCrash(int pid,
333 const std::string &exec,
334 bool *out_of_capacity) {
335 FilePath crash_path;
336 if (!GetCreatedCrashDirectory(pid, &crash_path, out_of_capacity)) {
337 LogCollectionError("Unable to find/create process-specific crash path");
338 return false;
339 }
340
341 // Directory like /tmp/crash_reporter.1234 which contains the
342 // procfs entries and other temporary files used during conversion.
343 FilePath container_dir = FilePath("/tmp").Append(
344 StringPrintf("crash_reporter.%d", pid));
345 std::string dump_basename = FormatDumpBasename(exec, time(NULL), pid);
346 FilePath core_path = GetCrashPath(crash_path, dump_basename, "core");
347 FilePath meta_path = GetCrashPath(crash_path, dump_basename, "meta");
348 FilePath minidump_path = GetCrashPath(crash_path, dump_basename, "dmp");
Ken Mixterc49dbd42010-12-14 17:44:11 -0800349 FilePath log_path = GetCrashPath(crash_path, dump_basename, "log");
350
351 if (GetLogContents(FilePath(kDefaultLogConfig), exec, log_path))
352 AddCrashMetaData("log", log_path.value());
Ken Mixter207694d2010-10-28 15:42:37 -0700353
354 if (!ConvertCoreToMinidump(pid, container_dir, core_path,
355 minidump_path)) {
356 logger_->LogInfo("Leaving core file at %s due to conversion error",
357 core_path.value().c_str());
358 return false;
359 }
360
361 // Here we commit to sending this file. We must not return false
362 // after this point or we will generate a log report as well as a
363 // crash report.
364 WriteCrashMetaData(meta_path,
365 exec,
366 minidump_path.value());
367
Ken Mixter777484c2010-07-23 16:22:44 -0700368 if (!file_util::PathExists(FilePath(kLeaveCoreFile))) {
369 file_util::Delete(core_path, false);
370 } else {
Ken Mixter207694d2010-10-28 15:42:37 -0700371 logger_->LogInfo("Leaving core file at %s due to developer image",
372 core_path.value().c_str());
Ken Mixter777484c2010-07-23 16:22:44 -0700373 }
374
Ken Mixter207694d2010-10-28 15:42:37 -0700375 file_util::Delete(container_dir, true);
376 return true;
Ken Mixter777484c2010-07-23 16:22:44 -0700377}
378
379bool UserCollector::HandleCrash(int signal, int pid, const char *force_exec) {
Chris Sosae4a86032010-06-16 17:08:34 -0700380 CHECK(initialized_);
Ken Mixter777484c2010-07-23 16:22:44 -0700381 std::string exec;
382 if (force_exec) {
383 exec.assign(force_exec);
384 } else if (!GetExecutableBaseNameFromPid(pid, &exec)) {
385 // If for some reason we don't have the base name, avoid completely
386 // failing by indicating an unknown name.
387 exec = "unknown";
388 }
Ken Mixterc6a58e02010-11-01 18:05:30 -0700389
390 // Allow us to test the crash reporting mechanism successfully even if
391 // other parts of the system crash.
392 if (!FLAGS_filter_in.empty() &&
393 (FLAGS_filter_in == "none" ||
394 FLAGS_filter_in != exec)) {
395 // We use a different format message to make it more obvious in tests
396 // which crashes are test generated and which are real.
397 logger_->LogWarning("Ignoring crash from %s[%d] while filter_in=%s",
398 exec.c_str(), pid, FLAGS_filter_in.c_str());
399 return true;
400 }
401
Ken Mixteree849c52010-09-30 15:30:10 -0700402 bool feedback = is_feedback_allowed_function_();
Ken Mixter2105b492010-11-09 16:14:38 -0800403 const char *handling_string = "handling";
404 if (!feedback) {
405 handling_string = "ignoring - no consent";
406 }
407
408 // Treat Chrome crashes as if the user opted-out. We stop counting Chrome
409 // crashes towards user crashes, so user crashes really mean non-Chrome
410 // user-space crashes.
411 if (exec == "chrome") {
412 feedback = false;
413 handling_string = "ignoring - chrome crash";
414 }
415
Ken Mixteree849c52010-09-30 15:30:10 -0700416 logger_->LogWarning("Received crash notification for %s[%d] sig %d (%s)",
Ken Mixter2105b492010-11-09 16:14:38 -0800417 exec.c_str(), pid, signal, handling_string);
Chris Sosae4a86032010-06-16 17:08:34 -0700418
Ken Mixteree849c52010-09-30 15:30:10 -0700419 if (feedback) {
Chris Sosae4a86032010-06-16 17:08:34 -0700420 count_crash_function_();
Ken Mixter777484c2010-07-23 16:22:44 -0700421
Ken Mixter03403162010-08-18 15:23:16 -0700422 if (generate_diagnostics_) {
Ken Mixter207694d2010-10-28 15:42:37 -0700423 bool out_of_capacity = false;
424 if (!ConvertAndEnqueueCrash(pid, exec, &out_of_capacity)) {
425 if (!out_of_capacity)
426 EnqueueCollectionErrorLog(pid, exec);
427 return false;
428 }
Ken Mixter03403162010-08-18 15:23:16 -0700429 }
Ken Mixter777484c2010-07-23 16:22:44 -0700430 }
Ken Mixter207694d2010-10-28 15:42:37 -0700431
Ken Mixter777484c2010-07-23 16:22:44 -0700432 return true;
Chris Sosae4a86032010-06-16 17:08:34 -0700433}