| // Copyright 2017 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/bert_collector.h" |
| |
| #include <fcntl.h> |
| #include <string> |
| #include <sys/stat.h> |
| |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| |
| #include "crash-reporter/util.h" |
| |
| using base::FilePath; |
| |
| namespace { |
| |
| constexpr char kACPITablePath[] = "/sys/firmware/acpi/tables"; |
| constexpr char kBertTable[] = "BERT"; |
| constexpr char kBertData[] = "data/BERT"; |
| constexpr char kBertErrorName[] = "bert_error"; |
| |
| // Validate BERT table signature, length and region length. |
| bool BertCheckTable(const struct acpi_table_bert& bert_table) { |
| if (memcmp(bert_table.signature, ACPI_SIG_BERT, ACPI_NAME_SIZE) != 0) |
| return false; |
| if (bert_table.length != sizeof(struct acpi_table_bert)) |
| return false; |
| if (bert_table.region_length != 0 && |
| bert_table.region_length < ACPI_BERT_REGION_STRUCT_SIZE) |
| return false; |
| |
| return true; |
| } |
| |
| // Read BERT table and data files. |
| // BERT stores data in little endian, and we assume the CPU endian is |
| // also little endian. |
| bool BertRead(const FilePath& bert_table_path, |
| const FilePath& bert_data_path, |
| struct acpi_table_bert* bert_table, |
| std::string* bert_table_contents, |
| std::string* bert_data_contents) { |
| // Read BERT table file. |
| if (!base::ReadFileToStringWithMaxSize(bert_table_path, |
| bert_table_contents, |
| sizeof(struct acpi_table_bert))) { |
| PLOG(ERROR) << "BERT table file read failed"; |
| return false; |
| } |
| memcpy(bert_table, bert_table_contents->data(), sizeof(*bert_table)); |
| |
| if (!BertCheckTable(*bert_table)) { |
| LOG(ERROR) << "Bad data in BERT table"; |
| return false; |
| } |
| |
| // Read BERT data file. |
| if (!base::ReadFileToStringWithMaxSize(bert_data_path, |
| bert_data_contents, |
| bert_table->region_length)) { |
| PLOG(ERROR) << "BERT data file read failed"; |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| BERTCollector::BERTCollector() |
| : CrashCollector("bert"), acpitable_path_(kACPITablePath) {} |
| |
| BERTCollector::~BERTCollector() {} |
| |
| bool BERTCollector::Collect() { |
| FilePath root_crash_directory; |
| |
| const FilePath bert_table_path = acpitable_path_.Append(kBertTable); |
| if (!base::PathExists(bert_table_path)) { |
| return false; |
| } |
| |
| const FilePath bert_data_path = acpitable_path_.Append(kBertData); |
| if (!base::PathExists(bert_data_path)) { |
| PLOG(ERROR) << bert_data_path.value() << " sysfs data not available"; |
| 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) << "BERT error from previous boot (" << reason << ")"; |
| if (!feedback) { |
| return true; |
| } |
| |
| std::string bert_table_contents; |
| std::string bert_data_contents; |
| struct acpi_table_bert bert_table; |
| // Read BERT table and BERT data information. |
| if (!BertRead(bert_table_path, bert_data_path, &bert_table, |
| &bert_table_contents, &bert_data_contents)) { |
| return false; |
| } |
| |
| // Dump BERT table and BERT data into single bertdump file. |
| if (!GetCreatedCrashDirectoryByEuid(kRootUid, |
| &root_crash_directory, |
| nullptr)) { |
| return false; |
| } |
| std::string dump_basename = |
| FormatDumpBasename(kBertErrorName, time(nullptr), 0); |
| FilePath bert_crash_path = GetCrashPath( |
| root_crash_directory, dump_basename, "bertdump"); |
| |
| // 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(bert_crash_path, bert_table_contents.c_str(), |
| bert_table.length) != bert_table.length) { |
| PLOG(ERROR) << "Failed to write BERT table to " |
| << bert_crash_path.value(); |
| return false; |
| } |
| if (!base::AppendToFile(bert_crash_path, |
| bert_data_contents.c_str(), |
| bert_table.region_length)) { |
| PLOG(ERROR) << "Failed to write BERT data to " |
| << bert_crash_path.value(); |
| return false; |
| } |
| |
| // Create meta file with bert dump info and finish up. |
| FinishCrash(GetCrashPath(root_crash_directory, dump_basename, "meta"), |
| kBertErrorName, bert_crash_path.value()); |
| |
| VLOG(3) << "Stored BERT dump to " << bert_crash_path.value(); |
| |
| return true; |
| } |