1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "crash_collector.h"
18
19#include <dirent.h>
20#include <fcntl.h>  // For file creation modes.
21#include <inttypes.h>
22#include <linux/limits.h>  // PATH_MAX
23#include <pwd.h>  // For struct passwd.
24#include <sys/types.h>  // for mode_t.
25#include <sys/wait.h>  // For waitpid.
26#include <unistd.h>  // For execv and fork.
27
28#include <set>
29#include <utility>
30#include <vector>
31
32#include <base/files/file_util.h>
33#include <base/logging.h>
34#include <base/posix/eintr_wrapper.h>
35#include <base/strings/string_split.h>
36#include <base/strings/string_util.h>
37#include <base/strings/stringprintf.h>
38#include <brillo/key_value_store.h>
39#include <brillo/process.h>
40
41namespace {
42
43const char kCollectChromeFile[] =
44    "/mnt/stateful_partition/etc/collect_chrome_crashes";
45const char kCrashTestInProgressPath[] =
46    "/data/misc/crash_reporter/tmp/crash-test-in-progress";
47const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
48const char kDefaultUserName[] = "chronos";
49const char kLeaveCoreFile[] = "/data/misc/crash_reporter/.leave_core";
50const char kShellPath[] = "/system/bin/sh";
51const char kSystemCrashPath[] = "/data/misc/crash_reporter/crash";
52const char kUploadVarPrefix[] = "upload_var_";
53const char kUploadFilePrefix[] = "upload_file_";
54
55// Normally this path is not used.  Unfortunately, there are a few edge cases
56// where we need this.  Any process that runs as kDefaultUserName that crashes
57// is consider a "user crash".  That includes the initial Chrome browser that
58// runs the login screen.  If that blows up, there is no logged in user yet,
59// so there is no per-user dir for us to stash things in.  Instead we fallback
60// to this path as it is at least encrypted on a per-system basis.
61//
62// This also comes up when running autotests.  The GUI is sitting at the login
63// screen while tests are sshing in, changing users, and triggering crashes as
64// the user (purposefully).
65const char kFallbackUserCrashPath[] = "/home/chronos/crash";
66
67// Directory mode of the user crash spool directory.
68const mode_t kUserCrashPathMode = 0755;
69
70// Directory mode of the system crash spool directory.
71const mode_t kSystemCrashPathMode = 01755;
72
73const uid_t kRootOwner = 0;
74const uid_t kRootGroup = 0;
75
76}  // namespace
77
78// Maximum crash reports per crash spool directory.  Note that this is
79// a separate maximum from the maximum rate at which we upload these
80// diagnostics.  The higher this rate is, the more space we allow for
81// core files, minidumps, and kcrash logs, and equivalently the more
82// processor and I/O bandwidth we dedicate to handling these crashes when
83// many occur at once.  Also note that if core files are configured to
84// be left on the file system, we stop adding crashes when either the
85// number of core files or minidumps reaches this number.
86const int CrashCollector::kMaxCrashDirectorySize = 32;
87
88using base::FilePath;
89using base::StringPrintf;
90
91CrashCollector::CrashCollector()
92    : log_config_path_(kDefaultLogConfig) {
93}
94
95CrashCollector::~CrashCollector() {
96}
97
98void CrashCollector::Initialize(
99    CrashCollector::CountCrashFunction count_crash_function,
100    CrashCollector::IsFeedbackAllowedFunction is_feedback_allowed_function) {
101  CHECK(count_crash_function);
102  CHECK(is_feedback_allowed_function);
103
104  count_crash_function_ = count_crash_function;
105  is_feedback_allowed_function_ = is_feedback_allowed_function;
106}
107
108int CrashCollector::WriteNewFile(const FilePath &filename,
109                                 const char *data,
110                                 int size) {
111  int fd = HANDLE_EINTR(open(filename.value().c_str(),
112                             O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0666));
113  if (fd < 0) {
114    return -1;
115  }
116
117  int rv = base::WriteFileDescriptor(fd, data, size) ? size : -1;
118  IGNORE_EINTR(close(fd));
119  return rv;
120}
121
122std::string CrashCollector::Sanitize(const std::string &name) {
123  // Make sure the sanitized name does not include any periods.
124  // The logic in crash_sender relies on this.
125  std::string result = name;
126  for (size_t i = 0; i < name.size(); ++i) {
127    if (!isalnum(result[i]) && result[i] != '_')
128      result[i] = '_';
129  }
130  return result;
131}
132
133std::string CrashCollector::FormatDumpBasename(const std::string &exec_name,
134                                               time_t timestamp,
135                                               pid_t pid) {
136  struct tm tm;
137  localtime_r(&timestamp, &tm);
138  std::string sanitized_exec_name = Sanitize(exec_name);
139  return StringPrintf("%s.%04d%02d%02d.%02d%02d%02d.%d",
140                      sanitized_exec_name.c_str(),
141                      tm.tm_year + 1900,
142                      tm.tm_mon + 1,
143                      tm.tm_mday,
144                      tm.tm_hour,
145                      tm.tm_min,
146                      tm.tm_sec,
147                      pid);
148}
149
150FilePath CrashCollector::GetCrashPath(const FilePath &crash_directory,
151                                      const std::string &basename,
152                                      const std::string &extension) {
153  return crash_directory.Append(StringPrintf("%s.%s",
154                                             basename.c_str(),
155                                             extension.c_str()));
156}
157
158FilePath CrashCollector::GetCrashDirectoryInfo(
159    mode_t *mode,
160    uid_t *directory_owner,
161    gid_t *directory_group) {
162  *mode = kSystemCrashPathMode;
163  *directory_owner = kRootOwner;
164  *directory_group = kRootGroup;
165  return FilePath(kSystemCrashPath);
166}
167
168bool CrashCollector::GetUserInfoFromName(const std::string &name,
169                                         uid_t *uid,
170                                         gid_t *gid) {
171  char storage[256];
172  struct passwd passwd_storage;
173  struct passwd *passwd_result = nullptr;
174
175  if (getpwnam_r(name.c_str(), &passwd_storage, storage, sizeof(storage),
176                 &passwd_result) != 0 || passwd_result == nullptr) {
177    LOG(ERROR) << "Cannot find user named " << name;
178    return false;
179  }
180
181  *uid = passwd_result->pw_uid;
182  *gid = passwd_result->pw_gid;
183  return true;
184}
185
186bool CrashCollector::GetCreatedCrashDirectoryByEuid(uid_t euid,
187                                                    FilePath *crash_directory,
188                                                    bool *out_of_capacity) {
189  if (out_of_capacity) *out_of_capacity = false;
190
191  // For testing.
192  if (!forced_crash_directory_.empty()) {
193    *crash_directory = forced_crash_directory_;
194    return true;
195  }
196
197  mode_t directory_mode;
198  uid_t directory_owner;
199  gid_t directory_group;
200  *crash_directory =
201      GetCrashDirectoryInfo(&directory_mode,
202                            &directory_owner,
203                            &directory_group);
204
205  if (!base::PathExists(*crash_directory)) {
206    // Create the spool directory with the appropriate mode (regardless of
207    // umask) and ownership.
208    mode_t old_mask = umask(0);
209    if (mkdir(crash_directory->value().c_str(), directory_mode) < 0 ||
210        chown(crash_directory->value().c_str(),
211              directory_owner,
212              directory_group) < 0) {
213      LOG(ERROR) << "Unable to create appropriate crash directory";
214      return false;
215    }
216    umask(old_mask);
217  }
218
219  if (!base::PathExists(*crash_directory)) {
220    LOG(ERROR) << "Unable to create crash directory "
221               << crash_directory->value().c_str();
222    return false;
223  }
224
225  if (!CheckHasCapacity(*crash_directory)) {
226    if (out_of_capacity) *out_of_capacity = true;
227    LOG(ERROR) << "Directory " << crash_directory->value()
228               << " is out of capacity.";
229    return false;
230  }
231
232  return true;
233}
234
235FilePath CrashCollector::GetProcessPath(pid_t pid) {
236  return FilePath(StringPrintf("/proc/%d", pid));
237}
238
239bool CrashCollector::GetSymlinkTarget(const FilePath &symlink,
240                                      FilePath *target) {
241  ssize_t max_size = 64;
242  std::vector<char> buffer;
243
244  while (true) {
245    buffer.resize(max_size + 1);
246    ssize_t size = readlink(symlink.value().c_str(), buffer.data(), max_size);
247    if (size < 0) {
248      int saved_errno = errno;
249      LOG(ERROR) << "Readlink failed on " << symlink.value() << " with "
250                 << saved_errno;
251      return false;
252    }
253
254    buffer[size] = 0;
255    if (size == max_size) {
256      max_size *= 2;
257      if (max_size > PATH_MAX) {
258        return false;
259      }
260      continue;
261    }
262    break;
263  }
264
265  *target = FilePath(buffer.data());
266  return true;
267}
268
269bool CrashCollector::GetExecutableBaseNameFromPid(pid_t pid,
270                                                 std::string *base_name) {
271  FilePath target;
272  FilePath process_path = GetProcessPath(pid);
273  FilePath exe_path = process_path.Append("exe");
274  if (!GetSymlinkTarget(exe_path, &target)) {
275    LOG(INFO) << "GetSymlinkTarget failed - Path " << process_path.value()
276              << " DirectoryExists: "
277              << base::DirectoryExists(process_path);
278    // Try to further diagnose exe readlink failure cause.
279    struct stat buf;
280    int stat_result = stat(exe_path.value().c_str(), &buf);
281    int saved_errno = errno;
282    if (stat_result < 0) {
283      LOG(INFO) << "stat " << exe_path.value() << " failed: " << stat_result
284                << " " << saved_errno;
285    } else {
286      LOG(INFO) << "stat " << exe_path.value() << " succeeded: st_mode="
287                << buf.st_mode;
288    }
289    return false;
290  }
291  *base_name = target.BaseName().value();
292  return true;
293}
294
295// Return true if the given crash directory has not already reached
296// maximum capacity.
297bool CrashCollector::CheckHasCapacity(const FilePath &crash_directory) {
298  DIR* dir = opendir(crash_directory.value().c_str());
299  if (!dir) {
300    LOG(WARNING) << "Unable to open crash directory "
301                 << crash_directory.value();
302    return false;
303  }
304  struct dirent ent_buf;
305  struct dirent* ent;
306  bool full = false;
307  std::set<std::string> basenames;
308  while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) {
309    if ((strcmp(ent->d_name, ".") == 0) ||
310        (strcmp(ent->d_name, "..") == 0))
311      continue;
312
313    std::string filename(ent->d_name);
314    size_t last_dot = filename.rfind(".");
315    std::string basename;
316    // If there is a valid looking extension, use the base part of the
317    // name.  If the only dot is the first byte (aka a dot file), treat
318    // it as unique to avoid allowing a directory full of dot files
319    // from accumulating.
320    if (last_dot != std::string::npos && last_dot != 0)
321      basename = filename.substr(0, last_dot);
322    else
323      basename = filename;
324    basenames.insert(basename);
325
326    if (basenames.size() >= static_cast<size_t>(kMaxCrashDirectorySize)) {
327      LOG(WARNING) << "Crash directory " << crash_directory.value()
328                   << " already full with " << kMaxCrashDirectorySize
329                   << " pending reports";
330      full = true;
331      break;
332    }
333  }
334  closedir(dir);
335  return !full;
336}
337
338bool CrashCollector::GetLogContents(const FilePath &config_path,
339                                    const std::string &exec_name,
340                                    const FilePath &output_file) {
341  brillo::KeyValueStore store;
342  if (!store.Load(config_path)) {
343    LOG(INFO) << "Unable to read log configuration file "
344              << config_path.value();
345    return false;
346  }
347
348  std::string command;
349  if (!store.GetString(exec_name, &command))
350    return false;
351
352  brillo::ProcessImpl diag_process;
353  diag_process.AddArg(kShellPath);
354  diag_process.AddStringOption("-c", command);
355  diag_process.RedirectOutput(output_file.value());
356
357  const int result = diag_process.Run();
358  if (result != 0) {
359    LOG(INFO) << "Log command \"" << command << "\" exited with " << result;
360    return false;
361  }
362  return true;
363}
364
365void CrashCollector::AddCrashMetaData(const std::string &key,
366                                      const std::string &value) {
367  extra_metadata_.append(StringPrintf("%s=%s\n", key.c_str(), value.c_str()));
368}
369
370void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
371                                            const std::string &path) {
372  if (!path.empty())
373    AddCrashMetaData(kUploadFilePrefix + key, path);
374}
375
376void CrashCollector::AddCrashMetaUploadData(const std::string &key,
377                                            const std::string &value) {
378  if (!value.empty())
379    AddCrashMetaData(kUploadVarPrefix + key, value);
380}
381
382void CrashCollector::WriteCrashMetaData(const FilePath &meta_path,
383                                        const std::string &exec_name,
384                                        const std::string &payload_path) {
385  int64_t payload_size = -1;
386  base::GetFileSize(FilePath(payload_path), &payload_size);
387  std::string meta_data = StringPrintf("%sexec_name=%s\n"
388                                       "payload=%s\n"
389                                       "payload_size=%" PRId64 "\n"
390                                       "done=1\n",
391                                       extra_metadata_.c_str(),
392                                       exec_name.c_str(),
393                                       payload_path.c_str(),
394                                       payload_size);
395  // We must use WriteNewFile instead of base::WriteFile as we
396  // do not want to write with root access to a symlink that an attacker
397  // might have created.
398  if (WriteNewFile(meta_path, meta_data.c_str(), meta_data.size()) < 0) {
399    LOG(ERROR) << "Unable to write " << meta_path.value();
400  }
401}
402
403bool CrashCollector::IsCrashTestInProgress() {
404  return base::PathExists(FilePath(kCrashTestInProgressPath));
405}
406
407bool CrashCollector::IsDeveloperImage() {
408  // If we're testing crash reporter itself, we don't want to special-case
409  // for developer images.
410  if (IsCrashTestInProgress())
411    return false;
412  return base::PathExists(FilePath(kLeaveCoreFile));
413}
414