blob: dd53d2e8c85d17eb012ebe2b74327f946d3fc647 [file] [log] [blame]
// Copyright 2018 The Chromium 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 "chrome/browser/conflicts/module_blacklist_cache_util_win.h"
#include <algorithm>
#include <memory>
#include <random>
#include <set>
#include <utility>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/md5.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "chrome/browser/conflicts/module_list_filter_win.h"
#include "chrome_elf/sha1/sha1.h"
#include "chrome_elf/third_party_dlls/packed_list_format.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Creates a list of unique random modules.
std::vector<third_party_dlls::PackedListModule> CreateUniqueModuleEntries(
uint32_t entry_count,
uint32_t min_time_date_stamp,
uint32_t max_time_date_stamp) {
// A pseudo-random number generator that explicitely uses the default seed to
// make the tests deterministic.
std::mt19937 random_engine(std::mt19937::default_seed);
// A distribution to generate bytes and another one to generate time date
// stamp values.
std::uniform_int_distribution<unsigned int> byte_distribution(0u, 255u);
std::uniform_int_distribution<unsigned int> time_date_stamp_distribution(
min_time_date_stamp, max_time_date_stamp);
std::vector<third_party_dlls::PackedListModule> entries(entry_count);
for (auto& entry : entries) {
// Fill up each bytes for both SHA1 hashes.
for (size_t i = 0; i < elf_sha1::kSHA1Length; ++i) {
entry.basename_hash[i] = byte_distribution(random_engine);
entry.code_id_hash[i] = byte_distribution(random_engine);
}
entry.time_date_stamp = time_date_stamp_distribution(random_engine);
}
// Sort the entries and make sure each module is unique.
std::sort(entries.begin(), entries.end(), internal::ModuleLess());
CHECK(std::adjacent_find(entries.begin(), entries.end(),
internal::ModuleEqual()) == entries.end());
return entries;
}
// Calls the |p| binary predicate for a sample of the collection.
template <typename T, typename Predicate>
void SampleBlacklistedModules(size_t count,
Predicate p,
std::vector<T>* collection) {
size_t to_skip = collection->size() / count;
for (size_t i = 0; i < count; ++i)
p((*collection)[i * to_skip]);
}
} // namespace
class ModuleBlacklistCacheUtilTest : public testing::Test {
protected:
ModuleBlacklistCacheUtilTest() = default;
~ModuleBlacklistCacheUtilTest() override = default;
// The number of module entry created for each test.
enum { kTestModuleCount = 500u };
void SetUp() override {
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
module_blacklist_cache_path_ = scoped_temp_dir_.GetPath().Append(
FILE_PATH_LITERAL("ModuleBlacklistCache"));
}
const base::FilePath& module_blacklist_cache_path() const {
return module_blacklist_cache_path_;
}
private:
base::ScopedTempDir scoped_temp_dir_;
base::FilePath module_blacklist_cache_path_;
DISALLOW_COPY_AND_ASSIGN(ModuleBlacklistCacheUtilTest);
};
TEST_F(ModuleBlacklistCacheUtilTest, CalculateTimeDateStamp) {
base::Time::Exploded chrome_birthday = {};
chrome_birthday.year = 2008;
chrome_birthday.month = 9; // September.
chrome_birthday.day_of_week = 2; // Tuesday.
chrome_birthday.day_of_month = 2;
base::Time time;
ASSERT_TRUE(chrome_birthday.HasValidValues());
ASSERT_TRUE(base::Time::FromUTCExploded(chrome_birthday, &time));
// Ensure that CalculateTimeDateStamp() will always return the number of
// hours between |time| and the Windows epoch.
EXPECT_EQ(3573552u, CalculateTimeDateStamp(time));
}
TEST_F(ModuleBlacklistCacheUtilTest, WriteEmptyCache) {
third_party_dlls::PackedListMetadata metadata = {
third_party_dlls::PackedListVersion::kCurrent, 0};
std::vector<third_party_dlls::PackedListModule> blacklisted_modules;
base::MD5Digest md5_digest;
EXPECT_TRUE(WriteModuleBlacklistCache(module_blacklist_cache_path(), metadata,
blacklisted_modules, &md5_digest));
// Check the file's stat.
int64_t file_size = 0;
EXPECT_TRUE(base::GetFileSize(module_blacklist_cache_path(), &file_size));
EXPECT_EQ(file_size, internal::CalculateExpectedFileSize(metadata));
base::MD5Digest expected = {
0x33, 0xCD, 0xEC, 0xCC, 0xCE, 0xBE, 0x80, 0x32,
0x9F, 0x1F, 0xDB, 0xEE, 0x7F, 0x58, 0x74, 0xCB,
};
for (size_t i = 0; i < arraysize(base::MD5Digest::a); ++i)
EXPECT_EQ(expected.a[i], md5_digest.a[i]);
}
TEST_F(ModuleBlacklistCacheUtilTest, WrittenFileSize) {
third_party_dlls::PackedListMetadata metadata = {
third_party_dlls::PackedListVersion::kCurrent, kTestModuleCount};
std::vector<third_party_dlls::PackedListModule> blacklisted_modules =
CreateUniqueModuleEntries(kTestModuleCount, 0u, 4000u);
base::MD5Digest md5_digest;
EXPECT_TRUE(WriteModuleBlacklistCache(module_blacklist_cache_path(), metadata,
blacklisted_modules, &md5_digest));
// Check the file's stat.
int64_t file_size = 0;
EXPECT_TRUE(base::GetFileSize(module_blacklist_cache_path(), &file_size));
EXPECT_EQ(file_size, internal::CalculateExpectedFileSize(metadata));
}
TEST_F(ModuleBlacklistCacheUtilTest, WriteAndRead) {
third_party_dlls::PackedListMetadata metadata = {
third_party_dlls::PackedListVersion::kCurrent, kTestModuleCount};
std::vector<third_party_dlls::PackedListModule> blacklisted_modules =
CreateUniqueModuleEntries(kTestModuleCount, 0u, 4000u);
base::MD5Digest md5_digest;
EXPECT_TRUE(WriteModuleBlacklistCache(module_blacklist_cache_path(), metadata,
blacklisted_modules, &md5_digest));
third_party_dlls::PackedListMetadata read_metadata;
std::vector<third_party_dlls::PackedListModule> read_blacklisted_modules;
base::MD5Digest read_md5_digest;
EXPECT_EQ(
ReadResult::kSuccess,
ReadModuleBlacklistCache(module_blacklist_cache_path(), &read_metadata,
&read_blacklisted_modules, &read_md5_digest));
EXPECT_EQ(read_metadata.version, metadata.version);
EXPECT_EQ(read_metadata.module_count, metadata.module_count);
ASSERT_EQ(read_blacklisted_modules.size(), blacklisted_modules.size());
// Note: Not using PackedListModuleEquals because the time_date_stamp is also
// verified.
EXPECT_EQ(0, memcmp(&read_blacklisted_modules[0], &blacklisted_modules[0],
read_blacklisted_modules.size() *
sizeof(third_party_dlls::PackedListModule)));
for (size_t i = 0; i < arraysize(base::MD5Digest::a); ++i)
EXPECT_EQ(md5_digest.a[i], read_md5_digest.a[i]);
}
class FakeModuleListFilter : public ModuleListFilter {
public:
FakeModuleListFilter() = default;
void AddWhitelistedModule(const third_party_dlls::PackedListModule& module) {
whitelisted_modules_.emplace(
base::StringPiece(
reinterpret_cast<const char*>(&module.basename_hash[0]),
base::size(module.basename_hash)),
base::StringPiece(
reinterpret_cast<const char*>(&module.code_id_hash[0]),
base::size(module.basename_hash)));
}
// ModuleListFilter:
bool IsWhitelisted(base::StringPiece module_basename_hash,
base::StringPiece module_code_id_hash) const override {
return base::ContainsKey(
whitelisted_modules_,
std::make_pair(module_basename_hash, module_code_id_hash));
}
std::unique_ptr<chrome::conflicts::BlacklistAction> IsBlacklisted(
const ModuleInfoKey& module_key,
const ModuleInfoData& module_data) const override {
return nullptr;
}
private:
~FakeModuleListFilter() override = default;
std::set<std::pair<base::StringPiece, base::StringPiece>>
whitelisted_modules_;
DISALLOW_COPY_AND_ASSIGN(FakeModuleListFilter);
};
TEST_F(ModuleBlacklistCacheUtilTest, RemoveWhitelistedEntries) {
std::vector<third_party_dlls::PackedListModule> blacklisted_modules =
CreateUniqueModuleEntries(kTestModuleCount, 5u, 10u);
// The number of modules that will be whitelisted in this test.
const size_t kWhitelistedModulesCount = 5u;
// Mark a few modules as whitelisted.
std::vector<third_party_dlls::PackedListModule> whitelisted_modules;
SampleBlacklistedModules(kWhitelistedModulesCount,
[&whitelisted_modules](const auto& element) {
whitelisted_modules.push_back(element);
},
&blacklisted_modules);
auto module_list_filter = base::MakeRefCounted<FakeModuleListFilter>();
for (const auto& module : whitelisted_modules)
module_list_filter->AddWhitelistedModule(module);
internal::RemoveWhitelistedEntries(*module_list_filter, &blacklisted_modules);
EXPECT_EQ(kTestModuleCount - kWhitelistedModulesCount,
blacklisted_modules.size());
for (const auto& module : whitelisted_modules) {
auto iter =
std::find_if(blacklisted_modules.begin(), blacklisted_modules.end(),
[&module](const auto& element) {
return internal::ModuleEqual()(module, element);
});
EXPECT_EQ(blacklisted_modules.end(), iter);
}
}
TEST_F(ModuleBlacklistCacheUtilTest, UpdateModuleBlacklistCacheTimestamps) {
std::vector<third_party_dlls::PackedListModule> blacklisted_modules =
CreateUniqueModuleEntries(kTestModuleCount, 5u, 10u);
// The number of modules that will get their time_date_stamp updated.
const size_t kModulesToRemove = 5u;
// Pick 5 modules in the list that will have their time_date_stamp updated to
// a new value guaranteed to be outside of the range of existing values
// (which is [5u, 10u]).
const uint32_t kNewTimeDateStamp = 15u;
std::vector<third_party_dlls::PackedListModule> updated_modules;
SampleBlacklistedModules(kModulesToRemove,
[&updated_modules](const auto& element) {
updated_modules.push_back(element);
},
&blacklisted_modules);
for (auto& module : updated_modules)
module.time_date_stamp = kNewTimeDateStamp;
internal::UpdateModuleBlacklistCacheTimestamps(updated_modules,
&blacklisted_modules);
EXPECT_EQ(kTestModuleCount, blacklisted_modules.size());
// For each entires, make sure they were updated.
for (const auto& module : updated_modules) {
auto iter =
std::find_if(blacklisted_modules.begin(), blacklisted_modules.end(),
[&module](const auto& element) {
return internal::ModuleEqual()(module, element);
});
ASSERT_NE(blacklisted_modules.end(), iter);
EXPECT_EQ(kNewTimeDateStamp, iter->time_date_stamp);
}
}
TEST_F(ModuleBlacklistCacheUtilTest, RemoveExpiredEntries_OnlyExpired) {
std::vector<third_party_dlls::PackedListModule> blacklisted_modules =
CreateUniqueModuleEntries(kTestModuleCount, 5u, 10u);
// The number of modules to remove from the collection.
const size_t kModulesToRemove = 5u;
// Set the time date stamp of 5 modules to an expired value.
const uint32_t kExpiredTimeDateStamp = 1u;
std::vector<third_party_dlls::PackedListModule> expired_modules;
SampleBlacklistedModules(kModulesToRemove,
[&expired_modules](auto& element) {
expired_modules.push_back(element);
element.time_date_stamp = kExpiredTimeDateStamp;
},
&blacklisted_modules);
std::sort(blacklisted_modules.begin(), blacklisted_modules.end(),
internal::ModuleTimeDateStampGreater());
// Set a minimum time date stamp that excludes all the entries in
// |expired_modules|.
const size_t kMinTimeDateStamp = kExpiredTimeDateStamp;
// Set a maxium cache file that fits all the existing modules.
const size_t kMaxModuleBlacklistCacheSize = kTestModuleCount;
// Assume there is no newly blacklisted module.
const size_t kNewlyBlacklistedModuleCount = 0u;
internal::RemoveExpiredEntries(
kMinTimeDateStamp, kMaxModuleBlacklistCacheSize,
kNewlyBlacklistedModuleCount, &blacklisted_modules);
// The 5 elements were removed.
EXPECT_EQ(kTestModuleCount - kModulesToRemove, blacklisted_modules.size());
for (const auto& module : expired_modules) {
auto iter =
std::find_if(blacklisted_modules.begin(), blacklisted_modules.end(),
[&module](const auto& element) {
return internal::ModuleEqual()(module, element);
});
EXPECT_EQ(blacklisted_modules.end(), iter);
}
}
TEST_F(ModuleBlacklistCacheUtilTest, RemoveExpiredEntries_NewlyBlacklisted) {
std::vector<third_party_dlls::PackedListModule> blacklisted_modules =
CreateUniqueModuleEntries(kTestModuleCount, 10u, 10000u);
std::sort(blacklisted_modules.begin(), blacklisted_modules.end(),
internal::ModuleTimeDateStampGreater());
// The number of modules to remove from the collection.
const size_t kModulesToRemove = 10u;
// Set a minimum time date stamp that doesn't exclude any entry.
const size_t kMinTimeDateStamp = 0u;
// Set a maxium cache file that fits all the existing modules.
const size_t kMaxModuleBlacklistCacheSize = kTestModuleCount;
// Remove the kModulesToRemove oldest entries by pretending there is
// kModulesToRemove newly blacklisted modules.
const size_t kNewlyBlacklistedModuleCount = kModulesToRemove;
// Get a copy of the 10 oldest modules to make sure they were removed. They
// should at the end of the vector.
std::vector<third_party_dlls::PackedListModule> excess_modules(
blacklisted_modules.end() - kNewlyBlacklistedModuleCount,
blacklisted_modules.end());
internal::RemoveExpiredEntries(
kMinTimeDateStamp, kMaxModuleBlacklistCacheSize,
kNewlyBlacklistedModuleCount, &blacklisted_modules);
// Enough elements were removed.
EXPECT_EQ(kTestModuleCount - kNewlyBlacklistedModuleCount,
blacklisted_modules.size());
for (const auto& module : excess_modules) {
auto iter =
std::find_if(blacklisted_modules.begin(), blacklisted_modules.end(),
[&module](const auto& element) {
return internal::ModuleEqual()(module, element);
});
EXPECT_EQ(blacklisted_modules.end(), iter);
}
}
TEST_F(ModuleBlacklistCacheUtilTest, RemoveExpiredEntries_MaxSize) {
std::vector<third_party_dlls::PackedListModule> blacklisted_modules =
CreateUniqueModuleEntries(kTestModuleCount, 10u, 10000u);
std::sort(blacklisted_modules.begin(), blacklisted_modules.end(),
internal::ModuleTimeDateStampGreater());
// The number of modules to remove from the collection.
const size_t kModulesToRemove = 15u;
// Set a minimum time date stamp that doesn't exclude any entry.
const size_t kMinTimeDateStamp = 0u;
// Pick a max size that ensures that kModulesToRemove entries are removed.
const size_t kMaxModuleBlacklistCacheSize =
kTestModuleCount - kModulesToRemove;
// Assume there is no newly blacklisted module.
const size_t kNewlyBlacklistedModuleCount = 0u;
// Get a copy of the last kModulesToRemove entries to make sure they are the
// one that are removed.
std::vector<third_party_dlls::PackedListModule> excess_modules(
blacklisted_modules.end() - kModulesToRemove, blacklisted_modules.end());
// The collection contains too many entries at first.
EXPECT_NE(kMaxModuleBlacklistCacheSize, blacklisted_modules.size());
internal::RemoveExpiredEntries(
kMinTimeDateStamp, kMaxModuleBlacklistCacheSize,
kNewlyBlacklistedModuleCount, &blacklisted_modules);
// Enough elements were removed.
EXPECT_EQ(kMaxModuleBlacklistCacheSize, blacklisted_modules.size());
for (const auto& module : excess_modules) {
auto iter =
std::find_if(blacklisted_modules.begin(), blacklisted_modules.end(),
[&module](const auto& element) {
return internal::ModuleEqual()(module, element);
});
EXPECT_EQ(blacklisted_modules.end(), iter);
}
}
TEST_F(ModuleBlacklistCacheUtilTest, RemoveDuplicateEntries) {
std::vector<third_party_dlls::PackedListModule> blacklisted_modules =
CreateUniqueModuleEntries(kTestModuleCount, 10u, 10000u);
// The number of modules to duplicate in this test.
const size_t kDuplicateCount = 15u;
// Create kDuplicateCount duplicate modules and set their time_date_stamp to a
// greater value so that the duplicate is kept instead of the original.
std::vector<third_party_dlls::PackedListModule> duplicated_modules;
SampleBlacklistedModules(kDuplicateCount,
[&duplicated_modules](auto& element) {
duplicated_modules.push_back(element);
},
&blacklisted_modules);
for (auto& module : duplicated_modules)
module.time_date_stamp += 10u;
// Insert at the beginning because RemoveDuplicateEntries() keeps the first
// duplicate in the list.
blacklisted_modules.insert(blacklisted_modules.begin(),
duplicated_modules.begin(),
duplicated_modules.end());
// Stable sort to keep the relative order between duplicates.
std::stable_sort(blacklisted_modules.begin(), blacklisted_modules.end(),
internal::ModuleLess());
EXPECT_EQ(kTestModuleCount + kDuplicateCount, blacklisted_modules.size());
internal::RemoveDuplicateEntries(&blacklisted_modules);
EXPECT_EQ(kTestModuleCount, blacklisted_modules.size());
for (const auto& module : duplicated_modules) {
auto iter =
std::find_if(blacklisted_modules.begin(), blacklisted_modules.end(),
[&module](const auto& element) {
return internal::ModuleEqual()(module, element) &&
module.time_date_stamp == element.time_date_stamp;
});
EXPECT_NE(blacklisted_modules.end(), iter);
}
}