Home | History | Annotate | Download | only in crash_reporter
      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 
     41 namespace {
     42 
     43 const char kCollectChromeFile[] =
     44     "/mnt/stateful_partition/etc/collect_chrome_crashes";
     45 const char kCrashTestInProgressPath[] =
     46     "/data/misc/crash_reporter/tmp/crash-test-in-progress";
     47 const char kDefaultLogConfig[] = "/etc/crash_reporter_logs.conf";
     48 const char kDefaultUserName[] = "chronos";
     49 const char kLeaveCoreFile[] = "/data/misc/crash_reporter/.leave_core";
     50 const char kShellPath[] = "/system/bin/sh";
     51 const char kSystemCrashPath[] = "/data/misc/crash_reporter/crash";
     52 const char kUploadVarPrefix[] = "upload_var_";
     53 const 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).
     65 const char kFallbackUserCrashPath[] = "/home/chronos/crash";
     66 
     67 // Directory mode of the user crash spool directory.
     68 const mode_t kUserCrashPathMode = 0755;
     69 
     70 // Directory mode of the system crash spool directory.
     71 const mode_t kSystemCrashPathMode = 01755;
     72 
     73 const uid_t kRootOwner = 0;
     74 const 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.
     86 const int CrashCollector::kMaxCrashDirectorySize = 32;
     87 
     88 using base::FilePath;
     89 using base::StringPrintf;
     90 
     91 CrashCollector::CrashCollector()
     92     : log_config_path_(kDefaultLogConfig) {
     93 }
     94 
     95 CrashCollector::~CrashCollector() {
     96 }
     97 
     98 void 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 
    108 int 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 
    122 std::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 
    133 std::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 
    150 FilePath 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 
    158 FilePath 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 
    168 bool 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 
    186 bool 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 
    235 FilePath CrashCollector::GetProcessPath(pid_t pid) {
    236   return FilePath(StringPrintf("/proc/%d", pid));
    237 }
    238 
    239 bool 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 
    269 bool 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.
    297 bool 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 
    338 bool 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 
    365 void 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 
    370 void CrashCollector::AddCrashMetaUploadFile(const std::string &key,
    371                                             const std::string &path) {
    372   if (!path.empty())
    373     AddCrashMetaData(kUploadFilePrefix + key, path);
    374 }
    375 
    376 void CrashCollector::AddCrashMetaUploadData(const std::string &key,
    377                                             const std::string &value) {
    378   if (!value.empty())
    379     AddCrashMetaData(kUploadVarPrefix + key, value);
    380 }
    381 
    382 void 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 
    403 bool CrashCollector::IsCrashTestInProgress() {
    404   return base::PathExists(FilePath(kCrashTestInProgressPath));
    405 }
    406 
    407 bool 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