blob: 0f652a3cce9a72e298373f53e02cf5b91c6722e4 [file] [log] [blame]
// Copyright 2016 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/ec_collector.h"
#include <string>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include "crash-reporter/util.h"
using base::FilePath;
using base::StringPiece;
using base::StringPrintf;
namespace {
const char kECDebugFSPath[] = "/sys/kernel/debug/cros_ec/";
const char kECPanicInfo[] = "panicinfo";
const char kECExecName[] = "embedded-controller";
} // namespace
ECCollector::ECCollector()
: CrashCollector("ec"), debugfs_path_(kECDebugFSPath) {}
ECCollector::~ECCollector() {}
bool ECCollector::Collect() {
char data[1024];
int len;
FilePath panicinfo_path = debugfs_path_.Append(kECPanicInfo);
FilePath root_crash_directory;
if (!base::PathExists(panicinfo_path)) {
return false;
}
len = base::ReadFile(panicinfo_path, data, sizeof(data));
if (len < 0) {
PLOG(ERROR) << "Unable to open " << panicinfo_path.value();
return false;
}
if (len <= PANIC_DATA_FLAGS_BYTE) {
LOG(ERROR) << "EC panicinfo is too short (" << len << " bytes).";
return false;
}
// Check if the EC crash has already been fetched before, in a previous AP
// boot (EC sets this flag when the AP fetches the panic information).
if (data[PANIC_DATA_FLAGS_BYTE] & PANIC_DATA_FLAG_OLD_HOSTCMD) {
LOG(INFO) << "Stale EC crash: already fetched, not reporting.";
return false;
}
std::string reason = "handling";
bool feedback = true;
if (util::IsDeveloperImage()) {
reason = "developer build - always dumping";
feedback = true;
} else if (!is_feedback_allowed_function_()) {
reason = "ignoring - no consent";
feedback = false;
}
LOG(INFO) << "Received crash notification from EC (" << reason << ")";
if (feedback) {
if (!GetCreatedCrashDirectoryByEuid(0, &root_crash_directory, nullptr)) {
return true;
}
std::string dump_basename =
FormatDumpBasename(kECExecName, time(nullptr), 0);
FilePath ec_crash_path = root_crash_directory.Append(
StringPrintf("%s.eccrash", dump_basename.c_str()));
// We must use WriteNewFile instead of base::WriteFile as we
// do not want to write with root access to a symlink that an attacker
// might have created.
if (WriteNewFile(ec_crash_path, data, len) != static_cast<int>(len)) {
PLOG(ERROR) << "Failed to write EC dump to "
<< ec_crash_path.value().c_str();
return true;
}
std::string signature = StringPrintf("%s-%08X", kECExecName,
HashString(StringPiece(data, len)));
/* TODO(drinkcat): Figure out a way to add EC version to metadata. */
AddCrashMetaData("sig", signature);
FinishCrash(root_crash_directory.Append(
StringPrintf("%s.meta", dump_basename.c_str())),
kECExecName, ec_crash_path.BaseName().value());
LOG(INFO) << "Stored EC crash to " << ec_crash_path.value();
}
return true;
}