blob: 0d06315f34f199927abf84e8eec28b170d895da1 [file] [log] [blame]
// Copyright 2012 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "crash-reporter/udev_collector.h"
#include <memory>
#include <vector>
#include <base/files/file_enumerator.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/memory/ref_counted.h>
#include <base/memory/scoped_refptr.h>
#include <base/strings/stringprintf.h>
#include <bindings/cloud_policy.pb.h>
#include <bindings/device_management_backend.pb.h>
#include <brillo/strings/string_utils.h>
#include <brillo/syslog_logging.h>
#include <dbus/message.h>
#include <dbus/mock_bus.h>
#include <dbus/mock_exported_object.h>
#include <crash-reporter-client/crash-reporter/dbus-constants.h>
#include <fbpreprocessor/proto_bindings/fbpreprocessor.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <login_manager/proto_bindings/policy_descriptor.pb.h>
#include <metrics/metrics_library.h>
#include <metrics/metrics_library_mock.h>
#include <re2/re2.h>
#include <session_manager-client-test/session_manager/dbus-proxy-mocks.h>
#include "crash-reporter/paths.h"
#include "crash-reporter/test_util.h"
using base::FilePath;
namespace {
// Bluetooth devcoredump feature flag path
// TODO(b/203034370): Remove this once the feature is fully launched and the
// feature flag is removed.
constexpr char kBluetoothDumpFlagPath[] = "/run/bluetooth/coredump_disabled";
constexpr char kFbpreprocessordBaseDirectory[] =
"/run/daemon-store/fbpreprocessord/user_hash/raw_dumps";
// Dummy log config file name.
const char kLogConfigFileName[] = "log_config_file";
// Dummy directory for storing device coredumps.
const char kDevCoredumpDirectory[] = "devcoredump";
// A bunch of random rules to put into the dummy log config file.
const char kLogConfigFileContents[] =
"crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n"
"crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n"
"crash_reporter-udev-collection-devcoredump-iwlwifi=echo devcoredump\n"
"cros_installer=echo not for udev\n"
"bt_firmware=echo bluetooth devcoredump\n";
const char kCrashLogFilePattern[] = "*.log.gz";
const char kDevCoredumpFilePattern[] = "*.devcore.gz";
const char kBluetoothCoredumpFilePattern[] = "bt_firmware.*";
const char kWiFiCoredumpFilePattern[] = "devcoredump_iwlwifi.*.devcore.gz";
const char kWiFiCoredumpDirectoryPattern[] =
"run/daemon-store/fbpreprocessord/user_hash/raw_dumps";
// Dummy content for device coredump data file.
const char kDevCoredumpDataContents[] = "coredump";
// Driver name for a coredump that should not be collected:
const char kNoCollectDriverName[] = "disallowed_driver";
// BT tests should be using BT specific driver name.
constexpr char kConnectivityBTDriverName[] = "bt_driver";
constexpr char kConnectivityWiFiDriverName[] = "iwlwifi";
constexpr char kDeviceGoogleUser[] = "alice@google.com";
constexpr char kDeviceUserInAllowlist[] = "testuser@managedchrome.com";
constexpr char kDeviceGmailUser[] = "alice@gmail.com";
constexpr char kAffiliationID[] = "affiliation_id";
// Driver names for a coredump that should be collected:
constexpr const char* kCollectedDriverNames[] = {"adreno", "qcom-venus",
"amdgpu"};
const char kCrashReporterInterface[] = "org.chromium.CrashReporterInterface";
const char kDebugDumpCreatedSignalName[] = "DebugDumpCreated";
const char kWiFiMetaFilePattern[] = "devcoredump_iwlwifi.*.meta";
const char kWiFiCrashLogFilePattern[] = "devcoredump_iwlwifi.*.log";
// Returns the number of files found in the given path that matches the
// specified file name pattern.
int GetNumFiles(const FilePath& path, const std::string& file_pattern) {
base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES,
file_pattern);
int num_files = 0;
for (FilePath file_path = enumerator.Next(); !file_path.value().empty();
file_path = enumerator.Next()) {
num_files++;
}
return num_files;
}
} // namespace
using ::testing::_;
using ::testing::Invoke;
using ::testing::WithArg;
using ::testing::WithArgs;
class UdevCollectorMock : public UdevCollector {
public:
UdevCollectorMock()
: UdevCollector(
base::MakeRefCounted<
base::RefCountedData<std::unique_ptr<MetricsLibraryInterface>>>(
std::make_unique<MetricsLibraryMock>())) {}
MOCK_METHOD(void, SetUpDBus, (), (override));
void SetSessionManagerProxy(
org::chromium::SessionManagerInterfaceProxyMock* mock) {
// This will take ownership of the mock, and the mock will be
// deleted when the UdevCollectorMock is deleted.
session_manager_proxy_.reset(mock);
}
void SetBus(scoped_refptr<dbus::MockBus> bus) { bus_ = bus.get(); }
};
class UdevCollectorTest : public ::testing::Test {
protected:
base::ScopedTempDir temp_dir_generator_;
void HandleCrash(const std::string& udev_event) {
collector_.HandleCrash(udev_event);
}
void GenerateDevCoredump(const std::string& device_name,
const std::string& driver_name) {
// Generate coredump data file.
ASSERT_TRUE(CreateDirectory(FilePath(
base::StringPrintf("%s/%s", collector_.dev_coredump_directory_.c_str(),
device_name.c_str()))));
FilePath data_path = FilePath(base::StringPrintf(
"%s/%s/data", collector_.dev_coredump_directory_.c_str(),
device_name.c_str()));
ASSERT_TRUE(test_util::CreateFile(data_path, kDevCoredumpDataContents));
// Generate uevent file for failing device.
ASSERT_TRUE(CreateDirectory(FilePath(base::StringPrintf(
"%s/%s/failing_device", collector_.dev_coredump_directory_.c_str(),
device_name.c_str()))));
FilePath uevent_path = FilePath(base::StringPrintf(
"%s/%s/failing_device/uevent",
collector_.dev_coredump_directory_.c_str(), device_name.c_str()));
ASSERT_TRUE(
test_util::CreateFile(uevent_path, "DRIVER=" + driver_name + "\n"));
}
// This function creates fbpreprocessord daemon-store directory and sets
// it to expected user, mode and group. The test invoking this function
// need to run as root to be able to change the group and ownership.
void CreateFbpreprocessordDirectoryForTest(UdevCollectorMock* collector) {
const int kFbPreprocessordAccessGid = 429;
const int kFbPreprocessordUid = 20213;
const mode_t kExpectedMode = 03770;
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
ASSERT_TRUE(base::CreateDirectory(user_hash_path));
ASSERT_EQ(chown(user_hash_path.value().c_str(), kFbPreprocessordUid,
kFbPreprocessordAccessGid),
0)
<< strerrordesc_np(errno);
ASSERT_EQ(chmod(user_hash_path.value().c_str(), kExpectedMode), 0)
<< strerrordesc_np(errno);
}
void SetupFirmwareDumpsFinchFlag(const std::string& val) {
FilePath fwdump_allowed_path =
paths::Get(paths::kAllowFirmwareDumpsFlagPath);
ASSERT_TRUE(test_util::CreateFile(fwdump_allowed_path, val));
}
void SetUpCollector(UdevCollectorMock* collector) {
// Reset the g_test_prefix in Path.
paths::SetPrefixForTesting(temp_dir_generator_.GetPath());
EXPECT_CALL(*collector, SetUpDBus()).WillRepeatedly(testing::Return());
collector->Initialize(false);
collector->log_config_path_ = log_config_path_;
collector->set_crash_directory_for_test(temp_dir_generator_.GetPath());
FilePath dev_coredump_path =
temp_dir_generator_.GetPath().Append(kDevCoredumpDirectory);
collector->dev_coredump_directory_ = dev_coredump_path.value();
SetupFirmwareDumpsFinchFlag("1");
collector->EnableConnectivityFwdumpForTest(true);
dbus::Bus::Options bus_options;
mock_bus_ = base::MakeRefCounted<dbus::MockBus>(bus_options);
}
// This function creates input request blob required to call
// RetrievePolicyEx() function.
std::vector<uint8_t> CreateExpectedDescriptorBlob(
const login_manager::PolicyAccountType& type, const std::string& user) {
login_manager::PolicyDescriptor descriptor;
descriptor.set_domain(login_manager::POLICY_DOMAIN_CHROME);
descriptor.set_account_id(user);
if (type == login_manager::PolicyAccountType::ACCOUNT_TYPE_USER) {
descriptor.set_account_type(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER);
} else if (type == login_manager::PolicyAccountType::ACCOUNT_TYPE_DEVICE) {
descriptor.set_account_type(
login_manager::PolicyAccountType::ACCOUNT_TYPE_DEVICE);
} else {
CHECK(false);
}
std::string descriptor_string = descriptor.SerializeAsString();
return std::vector<uint8_t>(descriptor_string.begin(),
descriptor_string.end());
}
void OnDebugDumpCreated(dbus::Signal* signal) {
dbus::MessageReader signal_reader(signal);
fbpreprocessor::DebugDumps dumps;
EXPECT_EQ(signal->GetInterface(), kCrashReporterInterface);
EXPECT_EQ(signal->GetMember(), kDebugDumpCreatedSignalName);
EXPECT_TRUE(signal_reader.PopArrayOfBytesAsProto(&dumps));
for (auto dump : dumps.dump()) {
EXPECT_TRUE(dump.has_wifi_dump());
base::FilePath path(dump.wifi_dump().dmpfile());
// Expect the WiFi firmware dump to be iwlwifi, for now.
EXPECT_TRUE(
RE2::FullMatch(path.BaseName().value(), kWiFiCoredumpFilePattern));
EXPECT_EQ(path.DirName(), temp_dir_generator_.GetPath().Append(
kWiFiCoredumpDirectoryPattern));
EXPECT_EQ(dump.wifi_dump().state(), fbpreprocessor::WiFiDump::RAW);
EXPECT_EQ(dump.wifi_dump().vendor(), fbpreprocessor::WiFiDump::IWLWIFI);
EXPECT_EQ(dump.wifi_dump().compression(), fbpreprocessor::WiFiDump::GZIP);
}
}
void SetDebugDumpCreatedSignalExpectation(
UdevCollectorMock* collector,
scoped_refptr<dbus::MockExportedObject> exported_object,
dbus::ObjectPath obj_path) {
EXPECT_CALL(*exported_object, ExportMethodAndBlock(_, _, _))
.WillRepeatedly(testing::Return(true));
EXPECT_CALL(*mock_bus_, GetExportedObject(testing::Eq(obj_path)))
.WillOnce(testing::Return(exported_object.get()));
EXPECT_CALL(*mock_bus_, RequestOwnershipAndBlock(_, _))
.WillOnce(testing::Return(true));
EXPECT_CALL(*exported_object, Unregister())
.WillRepeatedly(testing::Return());
EXPECT_CALL(*exported_object, SendSignal(testing::A<dbus::Signal*>()))
.WillOnce(Invoke(this, &UdevCollectorTest::OnDebugDumpCreated));
collector->SetBus(mock_bus_);
}
// This function creates Policy Fetch Response blob to simulate expected
// response of RetrievePolicyEx() function call.
std::vector<uint8_t> CreatePolicyFetchResponseBlob(
const login_manager::PolicyAccountType& type,
const std::string& affiliation_id,
const std::string& policy_val) {
enterprise_management::PolicyData policy_data;
enterprise_management::CloudPolicySettings user_policy_val;
// Add policy required for connectivity fwdumps.
user_policy_val.mutable_subproto1()
->mutable_userfeedbackwithlowleveldebugdataallowed()
->mutable_value()
->add_entries(policy_val);
std::string serialized_user_policy = user_policy_val.SerializeAsString();
policy_data.set_policy_value(serialized_user_policy);
if (type == login_manager::PolicyAccountType::ACCOUNT_TYPE_USER) {
auto id = policy_data.add_user_affiliation_ids();
*id = affiliation_id;
} else if (type == login_manager::PolicyAccountType::ACCOUNT_TYPE_DEVICE) {
auto id = policy_data.add_device_affiliation_ids();
*id = affiliation_id;
} else {
CHECK(false);
}
enterprise_management::PolicyFetchResponse response;
CHECK(policy_data.SerializeToString(response.mutable_policy_data()));
auto serialized = response.SerializeAsString();
return std::vector<uint8_t>(serialized.begin(), serialized.end());
}
UdevCollectorMock collector_;
scoped_refptr<dbus::MockBus> mock_bus_;
private:
void SetUp() override {
ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir());
log_config_path_ = temp_dir_generator_.GetPath().Append(kLogConfigFileName);
SetUpCollector(&collector_);
// Write to a dummy log config file.
ASSERT_TRUE(
test_util::CreateFile(log_config_path_, kLogConfigFileContents));
brillo::ClearLog();
}
void TearDown() override { paths::SetPrefixForTesting(base::FilePath()); }
FilePath log_config_path_;
};
TEST_F(UdevCollectorTest, TestNoMatch) {
// No rule should match this.
HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar");
EXPECT_EQ(0,
GetNumFiles(temp_dir_generator_.GetPath(), kCrashLogFilePattern));
}
TEST_F(UdevCollectorTest, TestMatches) {
// Try multiple udev events in sequence. The number of log files generated
// should increase.
HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
EXPECT_EQ(1,
GetNumFiles(temp_dir_generator_.GetPath(), kCrashLogFilePattern));
// Each collector is only allowed to handle one crash, so create a second
// collector for the second crash.
UdevCollectorMock second_collector;
SetUpCollector(&second_collector);
second_collector.HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu");
EXPECT_EQ(2,
GetNumFiles(temp_dir_generator_.GetPath(), kCrashLogFilePattern));
}
TEST_F(UdevCollectorTest, TestDevCoredump) {
GenerateDevCoredump("devcd0", kNoCollectDriverName);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
// IsDeveloperImage() returns false while running this test so devcoredumps
// will not be added to the crash directory.
EXPECT_EQ(
0, GetNumFiles(temp_dir_generator_.GetPath(), kDevCoredumpFilePattern));
GenerateDevCoredump("devcd1", kNoCollectDriverName);
// Each collector is only allowed to handle one crash, so create a second
// collector for the second crash.
UdevCollectorMock second_collector;
SetUpCollector(&second_collector);
second_collector.HandleCrash(
"ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
EXPECT_EQ(
0, GetNumFiles(temp_dir_generator_.GetPath(), kDevCoredumpFilePattern));
}
// Ensure that subsequent fwdumps are generated on back to back udev events
// for allowed users.
TEST_F(UdevCollectorTest,
RunAsRoot_TestConnectivityWiFiDevCoredumpUserAllowed) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
CreateFbpreprocessordDirectoryForTest(&collector_);
auto obj_path = dbus::ObjectPath(crash_reporter::kCrashReporterServicePath);
auto exported_object =
base::MakeRefCounted<dbus::MockExportedObject>(mock_bus_.get(), obj_path);
SetDebugDumpCreatedSignalExpectation(&collector_, exported_object, obj_path);
EXPECT_CALL(*mock, RetrievePrimarySession)
.WillOnce(WithArgs<0, 1>(
Invoke([](std::string* username, std::string* sanitized) {
*username = kDeviceGoogleUser;
*sanitized = "user_hash";
return true;
})));
EXPECT_CALL(
*mock,
RetrievePolicyEx(CreateExpectedDescriptorBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER,
kDeviceGoogleUser),
_, _, _))
.WillRepeatedly(WithArg<1>(Invoke([this](std::vector<uint8_t>* out_blob) {
*out_blob = CreatePolicyFetchResponseBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER, kAffiliationID,
"wifi");
return true;
})));
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(1, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
// Generate another coredump and check additional fwdump file is generated.
GenerateDevCoredump("devcd1", kConnectivityWiFiDriverName);
// Each collector is only allowed to handle one crash, so create a second
// collector for the second crash.
UdevCollectorMock second_collector;
SetUpCollector(&second_collector);
auto* mock2 = new org::chromium::SessionManagerInterfaceProxyMock;
second_collector.SetSessionManagerProxy(mock2);
CreateFbpreprocessordDirectoryForTest(&second_collector);
SetDebugDumpCreatedSignalExpectation(&second_collector, exported_object,
obj_path);
EXPECT_CALL(*mock2, RetrievePrimarySession)
.WillOnce(WithArgs<0, 1>(
Invoke([](std::string* username, std::string* sanitized) {
*username = kDeviceGoogleUser;
*sanitized = "user_hash";
return true;
})));
EXPECT_CALL(
*mock2,
RetrievePolicyEx(CreateExpectedDescriptorBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER,
kDeviceGoogleUser),
_, _, _))
.WillRepeatedly(WithArg<1>(Invoke([this](std::vector<uint8_t>* out_blob) {
*out_blob = CreatePolicyFetchResponseBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER, kAffiliationID,
"wifi");
return true;
})));
second_collector.HandleCrash(
"ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
EXPECT_EQ(2, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
}
// Ensure that connectivity fwdump is generated for user in allowlist.
TEST_F(UdevCollectorTest,
RunAsRoot_TestConnectivityWiFiDevCoredumpUserInAllowList) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
CreateFbpreprocessordDirectoryForTest(&collector_);
auto obj_path = dbus::ObjectPath(crash_reporter::kCrashReporterServicePath);
auto exported_object =
base::MakeRefCounted<dbus::MockExportedObject>(mock_bus_.get(), obj_path);
SetDebugDumpCreatedSignalExpectation(&collector_, exported_object, obj_path);
EXPECT_CALL(*mock, RetrievePrimarySession)
.WillOnce(WithArgs<0, 1>(
Invoke([](std::string* username, std::string* sanitized) {
*username = kDeviceUserInAllowlist;
*sanitized = "user_hash";
return true;
})));
EXPECT_CALL(
*mock,
RetrievePolicyEx(CreateExpectedDescriptorBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER,
kDeviceUserInAllowlist),
_, _, _))
.WillRepeatedly(WithArg<1>(Invoke([this](std::vector<uint8_t>* out_blob) {
*out_blob = CreatePolicyFetchResponseBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER, kAffiliationID,
"wifi");
return true;
})));
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(1, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
}
// For connectivity firmware dump we do not want .meta and .log file. This
// test ensures that .meta and .log files are not generated.
TEST_F(UdevCollectorTest, RunAsRoot_TestEnsureOnlyDevcoredumpFileIsGenerated) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
CreateFbpreprocessordDirectoryForTest(&collector_);
auto obj_path = dbus::ObjectPath(crash_reporter::kCrashReporterServicePath);
auto exported_object =
base::MakeRefCounted<dbus::MockExportedObject>(mock_bus_.get(), obj_path);
SetDebugDumpCreatedSignalExpectation(&collector_, exported_object, obj_path);
EXPECT_CALL(*mock, RetrievePrimarySession)
.WillOnce(WithArgs<0, 1>(
Invoke([](std::string* username, std::string* sanitized) {
*username = kDeviceUserInAllowlist;
*sanitized = "user_hash";
return true;
})));
EXPECT_CALL(
*mock,
RetrievePolicyEx(CreateExpectedDescriptorBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER,
kDeviceUserInAllowlist),
_, _, _))
.WillRepeatedly(WithArg<1>(Invoke([this](std::vector<uint8_t>* out_blob) {
*out_blob = CreatePolicyFetchResponseBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER, kAffiliationID,
"wifi");
return true;
})));
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(1, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
// Ensure .meta and .log file are not generated.
EXPECT_EQ(0, GetNumFiles(user_hash_path, kWiFiMetaFilePattern));
EXPECT_EQ(0, GetNumFiles(user_hash_path, kWiFiCrashLogFilePattern));
}
// Ensure fwdump is generated if policy is set and user is allowed.
TEST_F(UdevCollectorTest, RunAsRoot_TestConnectivityWiFiDevCoredumpPolicySet) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
CreateFbpreprocessordDirectoryForTest(&collector_);
auto obj_path = dbus::ObjectPath(crash_reporter::kCrashReporterServicePath);
auto exported_object =
base::MakeRefCounted<dbus::MockExportedObject>(mock_bus_.get(), obj_path);
SetDebugDumpCreatedSignalExpectation(&collector_, exported_object, obj_path);
EXPECT_CALL(*mock, RetrievePrimarySession)
.WillOnce(WithArgs<0, 1>(
Invoke([](std::string* username, std::string* sanitized) {
*username = kDeviceGoogleUser;
*sanitized = "user_hash";
return true;
})));
EXPECT_CALL(
*mock,
RetrievePolicyEx(CreateExpectedDescriptorBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER,
kDeviceGoogleUser),
_, _, _))
.WillRepeatedly(WithArg<1>(Invoke([this](std::vector<uint8_t>* out_blob) {
*out_blob = CreatePolicyFetchResponseBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER, kAffiliationID,
"wifi");
return true;
})));
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(1, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
}
// Ensure fwdump is not generated when disallowed by finch.
TEST_F(UdevCollectorTest, TestConnectivityWiFiDevCoredumpDisabledByFinch) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
SetupFirmwareDumpsFinchFlag("0");
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(0, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
}
// Ensure fwdump is not generated when finch cache file does not exist.
TEST_F(UdevCollectorTest,
TestConnectivityWiFiDevCoredumpDisabledNoFinchFilePresent) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(0, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
}
// Ensure fwdump is not generated when finch flag has unexpected value.
TEST_F(UdevCollectorTest,
TestConnectivityWiFiDevCoredumpDisabledByCorruptFinchValue) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
// AllowedFirmwareDumpsFlagPath contains some corrupted value.
SetupFirmwareDumpsFinchFlag("10");
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(0, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
}
// Ensure fwdump is not generated when there is trailing whitespace in finch
// status.
TEST_F(UdevCollectorTest,
TestConnectivityWiFiDevCoredumpEnabledByFinchValueWithWhiteSpace) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
// White space in finch flag, gets rejected.
SetupFirmwareDumpsFinchFlag("1 ");
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(0, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
}
// Ensure fwdump is not generated even if allowed by finch because it is
// disabled by connectivity_fwdump_feature_enabled_ to not mistakenly enabled
// fwdumps in feedback report feature.
TEST_F(UdevCollectorTest,
TestConnectivityWiFiDevCoredumpAllowedByFinchButDisabled) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
// Disable fwdump feature.
collector_.EnableConnectivityFwdumpForTest(false);
FilePath user_hash_path = paths::Get(kFbpreprocessordBaseDirectory);
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(0, GetNumFiles(user_hash_path, kWiFiCoredumpFilePattern));
}
// Ensures that there is no fwdump file generated if policy is not set
// but fwdump for user is allowed.
TEST_F(UdevCollectorTest, TestConnectivityWiFiDevCoredumpPolicyNotSet) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
EXPECT_CALL(*mock, RetrievePrimarySession)
.WillOnce(WithArgs<0, 1>(
Invoke([](std::string* username, std::string* sanitized) {
*username = kDeviceGoogleUser;
*sanitized = "user_hash";
return true;
})));
EXPECT_CALL(
*mock,
RetrievePolicyEx(CreateExpectedDescriptorBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER,
kDeviceGoogleUser),
_, _, _))
.WillRepeatedly(WithArg<1>(Invoke([this](std::vector<uint8_t>* out_blob) {
*out_blob = CreatePolicyFetchResponseBlob(
login_manager::PolicyAccountType::ACCOUNT_TYPE_USER, kAffiliationID,
"");
return true;
})));
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(
0, GetNumFiles(temp_dir_generator_.GetPath(), kWiFiCoredumpFilePattern));
}
// Ensure no fwdump is generated if user is not in allowed for fwdumps.
TEST_F(UdevCollectorTest, TestConnectivityWiFiDevCoredumpUserNotAllowed) {
GenerateDevCoredump("devcd0", kConnectivityWiFiDriverName);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
collector_.SetSessionManagerProxy(mock);
EXPECT_CALL(*mock, RetrievePrimarySession)
.WillOnce(WithArgs<0, 1>(
Invoke([](std::string* username, std::string* sanitized) {
*username = kDeviceGmailUser;
*sanitized = "user_hash";
return true;
})));
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(
0, GetNumFiles(temp_dir_generator_.GetPath(), kWiFiCoredumpFilePattern));
}
TEST_F(UdevCollectorTest, TestCollectedDevCoredump) {
// One more test, this time for the case of a devcoredump that should be
// collected in all builds:
const int driver_count =
sizeof(kCollectedDriverNames) / sizeof(kCollectedDriverNames[0]);
for (int i = 0; i < driver_count; i++) {
const char* driver_name = kCollectedDriverNames[i];
const std::string kernel_number = std::to_string(i + 2);
const std::string device_name = std::string("devcd") + kernel_number;
GenerateDevCoredump(device_name, driver_name);
UdevCollectorMock third_collector;
SetUpCollector(&third_collector);
const std::string udev_event =
std::string("ACTION=add:SUBSYSTEM=devcoredump:KERNEL_NUMBER=") +
kernel_number;
third_collector.HandleCrash(udev_event);
}
EXPECT_EQ(driver_count, GetNumFiles(temp_dir_generator_.GetPath(),
kDevCoredumpFilePattern));
for (int i = 0; i < driver_count; i++) {
const char* driver_name = kCollectedDriverNames[i];
// Check for the expected crash signature:
std::string sanitized_name = driver_name;
for (size_t i = 0; i < sanitized_name.size(); ++i) {
if (!isalnum(sanitized_name[i]) && sanitized_name[i] != '_')
sanitized_name[i] = '_';
}
base::FilePath meta_path;
std::string meta_pattern = "devcoredump_";
meta_pattern += sanitized_name;
meta_pattern += ".*.meta";
EXPECT_TRUE(test_util::DirectoryHasFileWithPattern(
temp_dir_generator_.GetPath(), meta_pattern, &meta_path));
std::string meta_contents;
EXPECT_TRUE(base::ReadFileToString(meta_path, &meta_contents));
std::string expected_sig =
"sig=crash_reporter-udev-collection-devcoredump-";
expected_sig += driver_name;
EXPECT_THAT(meta_contents, testing::HasSubstr(expected_sig));
}
}
TEST_F(UdevCollectorTest, RunAsRoot_TestValidBluetoothDevCoredump) {
std::string device_name = "devcd0";
GenerateDevCoredump(device_name, kConnectivityBTDriverName);
FilePath data_path =
FilePath(base::StringPrintf("%s/%s/data",
temp_dir_generator_.GetPath()
.Append(kDevCoredumpDirectory)
.value()
.c_str(),
device_name.c_str()));
std::vector<std::string> data = {
"Bluetooth devcoredump",
"State: 2",
"Driver: TestDrv",
"Vendor: TestVen",
"Controller Name: TestCon",
"--- Start dump ---",
"TestData",
};
std::string data_str = brillo::string_utils::Join("\n", data);
ASSERT_EQ(base::WriteFile(data_path, data_str.c_str(), data_str.length()),
data_str.length());
ASSERT_TRUE(test_util::CreateFile(paths::Get(kBluetoothDumpFlagPath), "0"));
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(3, GetNumFiles(temp_dir_generator_.GetPath(),
kBluetoothCoredumpFilePattern));
}
TEST_F(UdevCollectorTest, RunAsRoot_TestInvalidBluetoothDevCoredump) {
std::string device_name = "devcd1";
GenerateDevCoredump(device_name, kConnectivityBTDriverName);
FilePath data_path =
FilePath(base::StringPrintf("%s/%s/data",
temp_dir_generator_.GetPath()
.Append(kDevCoredumpDirectory)
.value()
.c_str(),
device_name.c_str()));
// Incomplete bluetooth devcoredump header, parsing should fail and no output
// files should get generated.
std::vector<std::string> data = {
"Bluetooth devcoredump",
"State: 2",
"Driver: TestDrv",
"Vendor: TestVen",
};
std::string data_str = brillo::string_utils::Join("\n", data);
ASSERT_EQ(base::WriteFile(data_path, data_str.c_str(), data_str.length()),
data_str.length());
ASSERT_TRUE(test_util::CreateFile(paths::Get(kBluetoothDumpFlagPath), "0"));
HandleCrash("ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.GetPath(),
kBluetoothCoredumpFilePattern));
}
class UdevCollectorCrashSeverityTest
: public UdevCollectorTest,
public ::testing::WithParamInterface<
test_util::ComputeCrashSeverityTestParams> {};
TEST_P(UdevCollectorCrashSeverityTest, ComputeCrashSeverity) {
const test_util::ComputeCrashSeverityTestParams& test_case = GetParam();
CrashCollector::ComputedCrashSeverity computed_severity =
collector_.ComputeSeverity(test_case.exec_name);
EXPECT_EQ(computed_severity.crash_severity, test_case.expected_severity);
EXPECT_EQ(computed_severity.product_group,
CrashCollector::Product::kPlatform);
}
INSTANTIATE_TEST_SUITE_P(
UdevCollectorCrashSeverityTestSuite,
UdevCollectorCrashSeverityTest,
testing::ValuesIn<test_util::ComputeCrashSeverityTestParams>({
{"udev-usb", CrashCollector::CrashSeverity::kError},
{"devcoredump_adreno", CrashCollector::CrashSeverity::kWarning},
{"udev-i2c-atmel_mxt_ts", CrashCollector::CrashSeverity::kWarning},
{"udev-drm", CrashCollector::CrashSeverity::kWarning},
{"another executable", CrashCollector::CrashSeverity::kUnspecified},
}));
// TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev
// events.