blob: 3fa6249c236c6021279bdc42089e5f17f3c2630f [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "crash-reporter/missed_crash_collector.h"
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
constexpr int64_t MissedCrashCollector::kDefaultChunkSize;
MissedCrashCollector::MissedCrashCollector()
: CrashCollector("missed_crash"), input_file_(stdin) {}
MissedCrashCollector::~MissedCrashCollector() = default;
// static
bool MissedCrashCollector::ReadFILEToString(FILE* file, std::string* contents) {
// This is very much a rewording of base::ReadFileToString(), except that:
// a) We pass in a FILE* instead of opening one. We don't use
// base::ReadFileToString() because we often in fd-exhaustion when a missed
// crash occurs and we don't want to risk opening more file descriptors.
// b) We don't try to find a file size since stdin isn't going to give us a
// file size.
size_t bytes_read_this_pass;
size_t bytes_read_so_far = 0;
clearerr(file);
contents->clear();
contents->resize(kDefaultChunkSize);
while ((bytes_read_this_pass = fread(&(*contents)[bytes_read_so_far], 1,
kDefaultChunkSize, file)) > 0) {
bytes_read_so_far += bytes_read_this_pass;
// Last fread syscall (after EOF) can be avoided via feof, which is just a
// flag check.
if (feof(file))
break;
contents->resize(bytes_read_so_far + kDefaultChunkSize);
}
bool read_status = !ferror(file);
contents->resize(bytes_read_so_far);
return read_status;
}
bool MissedCrashCollector::Collect(int pid,
int recent_miss_count,
int recent_match_count,
int pending_miss_count) {
LOG(INFO) << "Processing missed crash for process " << pid;
std::string logs;
if (!ReadFILEToString(input_file_, &logs)) {
LOG(ERROR) << "Could not read input logs";
logs += "<failed read>";
// Keep going in hopes of getting some information.
}
base::FilePath crash_directory;
// We always use kRootUid here (and thus write to /var/spool/crash), even
// though the missed crash was probably under user ID 1000. Since we only
// read system logs and system information, there should be no user-specific
// information in the logs (that is, the logs don't contain anything from
// the user's cryptohome). Furthermore, since we are launched by
// anomaly_detector, we are inside anomaly_detector's minijail. Using the
// "correct" userid here would mean allowing writes to many more locations in
// that minijail config. I'd rather keep the write restrictions as tight as
// possible unless we actually have sensitive information here.
if (!GetCreatedCrashDirectoryByEuid(kRootUid, &crash_directory, nullptr)) {
LOG(WARNING) << "Could not get crash directory (full?)";
return true;
}
StripSensitiveData(&logs);
constexpr char kExecName[] = "missed_crash";
std::string dump_basename = FormatDumpBasename(kExecName, time(nullptr), pid);
const base::FilePath log_path =
GetCrashPath(crash_directory, dump_basename, "log.gz");
const base::FilePath meta_path =
GetCrashPath(crash_directory, dump_basename, "meta");
if (!WriteNewCompressedFile(log_path, logs.data(), logs.size())) {
PLOG(WARNING) << "Error writing sanitized log to " << log_path.value();
}
AddCrashMetaData("sig", "missed-crash");
AddCrashMetaUploadData("pid", base::NumberToString(pid));
AddCrashMetaUploadData("recent_miss_count",
base::NumberToString(recent_miss_count));
AddCrashMetaUploadData("recent_match_count",
base::NumberToString(recent_match_count));
AddCrashMetaUploadData("pending_miss_count",
base::NumberToString(pending_miss_count));
FinishCrash(meta_path, kExecName, log_path.BaseName().value());
return true;
}