blob: 63d55c540bcecbfad2d022563f678c8a11145527 [file] [log] [blame]
// Copyright 2017 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 "components/subresource_filter/content/browser/verified_ruleset_dealer.h"
#include <memory>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/test_simple_task_runner.h"
#include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
#include "components/subresource_filter/core/common/test_ruleset_creator.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace subresource_filter {
namespace {
// TODO(pkalinnikov): Consider putting this to a test_support for this test file
// and SubresourceFilterRulesetDealerTest.
class TestRulesets {
public:
TestRulesets() = default;
void CreateRulesets(bool many_rules = false) {
if (many_rules) {
ASSERT_NO_FATAL_FAILURE(
test_ruleset_creator_.CreateRulesetToDisallowURLsWithManySuffixes(
kTestRulesetSuffix1, kNumberOfRulesInBigRuleset,
&test_ruleset_pair_1_));
ASSERT_NO_FATAL_FAILURE(
test_ruleset_creator_.CreateRulesetToDisallowURLsWithManySuffixes(
kTestRulesetSuffix2, kNumberOfRulesInBigRuleset,
&test_ruleset_pair_2_));
} else {
ASSERT_NO_FATAL_FAILURE(
test_ruleset_creator_.CreateRulesetToDisallowURLsWithPathSuffix(
kTestRulesetSuffix1, &test_ruleset_pair_1_));
ASSERT_NO_FATAL_FAILURE(
test_ruleset_creator_.CreateRulesetToDisallowURLsWithPathSuffix(
kTestRulesetSuffix2, &test_ruleset_pair_2_));
}
}
const testing::TestRuleset& indexed_1() const {
return test_ruleset_pair_1_.indexed;
}
const testing::TestRuleset& indexed_2() const {
return test_ruleset_pair_2_.indexed;
}
private:
static constexpr const char kTestRulesetSuffix1[] = "foo";
static constexpr const char kTestRulesetSuffix2[] = "bar";
static constexpr int kNumberOfRulesInBigRuleset = 500;
testing::TestRulesetCreator test_ruleset_creator_;
testing::TestRulesetPair test_ruleset_pair_1_;
testing::TestRulesetPair test_ruleset_pair_2_;
DISALLOW_COPY_AND_ASSIGN(TestRulesets);
};
constexpr const char TestRulesets::kTestRulesetSuffix1[];
constexpr const char TestRulesets::kTestRulesetSuffix2[];
constexpr int TestRulesets::kNumberOfRulesInBigRuleset;
std::vector<uint8_t> ReadRulesetContents(const MemoryMappedRuleset* ruleset) {
return std::vector<uint8_t>(ruleset->data(),
ruleset->data() + ruleset->length());
}
std::vector<uint8_t> ReadFileContent(base::File* file) {
DCHECK(file);
DCHECK(file->IsValid());
const int64_t file_length = file->GetLength();
DCHECK_LE(0, file_length);
std::vector<uint8_t> file_content(static_cast<size_t>(file_length), 0);
const int read_res =
file->Read(0, reinterpret_cast<char*>(&(file_content[0])),
static_cast<int>(file_length));
DCHECK_EQ(read_res, file_length);
return file_content;
}
} // namespace
// Tests for VerifiedRulesetDealer. --------------------------------------------
//
// Note that VerifiedRulesetDealer uses RulesetDealer very directly to provide
// MemoryMappedRulesets. Many aspects of its work, e.g., lifetime of a
// MemoryMappedRuleset, its lazy creation, etc., are covered with tests to
// RulesetDealer, therefore these aspects are not tested here.
constexpr char kVerificationHistogram[] =
"SubresourceFilter.RulesetVerificationStatus";
class SubresourceFilterVerifiedRulesetDealerTest : public ::testing::Test {
public:
SubresourceFilterVerifiedRulesetDealerTest() = default;
protected:
void SetUp() override {
rulesets_.CreateRulesets(true /* many_rules */);
ruleset_dealer_.reset(new VerifiedRulesetDealer);
}
const TestRulesets& rulesets() const { return rulesets_; }
VerifiedRulesetDealer* ruleset_dealer() { return ruleset_dealer_.get(); }
bool has_cached_ruleset() const {
return ruleset_dealer_->has_cached_ruleset();
}
const base::HistogramTester& histogram_tester() const {
return histogram_tester_;
}
private:
TestRulesets rulesets_;
std::unique_ptr<VerifiedRulesetDealer> ruleset_dealer_;
base::HistogramTester histogram_tester_;
DISALLOW_COPY_AND_ASSIGN(SubresourceFilterVerifiedRulesetDealerTest);
};
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
RulesetIsMemoryMappedAndVerifiedLazily) {
ruleset_dealer()->SetRulesetFile(
testing::TestRuleset::Open(rulesets().indexed_1()));
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
ruleset_dealer()->status());
scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
ruleset_dealer()->GetRuleset();
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_TRUE(ref_to_ruleset);
EXPECT_TRUE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kIntact, 1);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
CorruptedRulesetIsNeitherProvidedNorCached) {
testing::TestRuleset::CorruptByTruncating(rulesets().indexed_1(), 123);
ruleset_dealer()->SetRulesetFile(
testing::TestRuleset::Open(rulesets().indexed_1()));
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
ruleset_dealer()->status());
scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
ruleset_dealer()->GetRuleset();
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(ref_to_ruleset);
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kCorrupt, 1);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerTest, MmapFailureInitial) {
ruleset_dealer()->SetRulesetFile(
testing::TestRuleset::Open(rulesets().indexed_1()));
MemoryMappedRuleset::SetMemoryMapFailuresForTesting(true);
EXPECT_FALSE(!!ruleset_dealer()->GetRuleset());
MemoryMappedRuleset::SetMemoryMapFailuresForTesting(false);
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kInvalidFile,
ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kInvalidFile,
1);
}
// This is a duplicated test from RulesetDealer, to ensure that verification
// doesn't introduce any bad assumptions about mmap failures.
TEST_F(SubresourceFilterVerifiedRulesetDealerTest, MmapFailureSubsequent) {
ruleset_dealer()->SetRulesetFile(
testing::TestRuleset::Open(rulesets().indexed_1()));
{
scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
ruleset_dealer()->GetRuleset();
EXPECT_TRUE(!!ref_to_ruleset);
// Simulate subsequent mmap failures
MemoryMappedRuleset::SetMemoryMapFailuresForTesting(true);
// Calls to GetRuleset should succeed as long as the strong ref
// is still around.
EXPECT_TRUE(ruleset_dealer()->has_cached_ruleset());
EXPECT_TRUE(!!ruleset_dealer()->GetRuleset());
EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kIntact,
1);
}
EXPECT_FALSE(ruleset_dealer()->has_cached_ruleset());
EXPECT_FALSE(!!ruleset_dealer()->GetRuleset());
EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kIntact,
1);
MemoryMappedRuleset::SetMemoryMapFailuresForTesting(false);
EXPECT_TRUE(!!ruleset_dealer()->GetRuleset());
EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kIntact,
1);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
TruncatingFileMakesRulesetInvalid) {
testing::TestRuleset::CorruptByTruncating(rulesets().indexed_1(), 4096);
ruleset_dealer()->SetRulesetFile(
testing::TestRuleset::Open(rulesets().indexed_1()));
scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
ruleset_dealer()->GetRuleset();
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(ref_to_ruleset);
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kCorrupt, 1);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
FillingRangeMakesRulesetInvalid) {
testing::TestRuleset::CorruptByFilling(rulesets().indexed_1(),
2501 /* from */, 4000 /* to */,
255 /* fill_with */);
ruleset_dealer()->SetRulesetFile(
testing::TestRuleset::Open(rulesets().indexed_1()));
scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
ruleset_dealer()->GetRuleset();
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(ref_to_ruleset);
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kCorrupt, 1);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
RulesetIsVerifiedAfterUpdate) {
testing::TestRuleset::CorruptByTruncating(rulesets().indexed_1(), 123);
ruleset_dealer()->SetRulesetFile(
testing::TestRuleset::Open(rulesets().indexed_1()));
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
ruleset_dealer()->status());
scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
ruleset_dealer()->GetRuleset();
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(ref_to_ruleset);
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kCorrupt, 1);
ruleset_dealer()->SetRulesetFile(
testing::TestRuleset::Open(rulesets().indexed_2()));
EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
ruleset_dealer()->status());
ref_to_ruleset = ruleset_dealer()->GetRuleset();
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_TRUE(ref_to_ruleset);
EXPECT_TRUE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
histogram_tester().ExpectTotalCount(kVerificationHistogram, 2);
histogram_tester().ExpectBucketCount(kVerificationHistogram,
RulesetVerificationStatus::kCorrupt, 1);
histogram_tester().ExpectBucketCount(kVerificationHistogram,
RulesetVerificationStatus::kIntact, 1);
}
// Check that without the checksum parameter to OpenAndSetRulesetFile,
// the corrupted file is seen as valid.
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
OpenAndSetRulesetFileValidNoChecksum) {
// See also SubresourceFilterBrowserTest.InvalidRuleset_Checksum, corrupting
// in this manner doesn't invalidate the Flatbuffer Verifier check.
testing::TestRuleset::CorruptByFilling(rulesets().indexed_1(), 28250, 28251,
32);
base::File file = ruleset_dealer()->OpenAndSetRulesetFile(
/*expected_checksum=*/0, rulesets().indexed_1().path);
// Check the required file is opened.
ASSERT_TRUE(file.IsValid());
// Check |OpenAndSetRulesetFile| forwards call to |SetRulesetFile| on success.
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
ruleset_dealer()->status());
histogram_tester().ExpectTotalCount(kVerificationHistogram, 0);
// Check that after getting the ruleset the expected values are present.
scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
ruleset_dealer()->GetRuleset();
EXPECT_TRUE(ref_to_ruleset);
EXPECT_EQ(RulesetVerificationStatus::kIntact, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kIntact, 1);
}
// Check that when adding the checksum parameter to OpenAndSetRulesetFile,
// the corrupted file is detected as invalid.
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
OpenAndSetRulesetFileInvalidChecksum) {
int expected_checksum =
base::PersistentHash(&(rulesets().indexed_1().contents[0]),
rulesets().indexed_1().contents.size());
// See also SubresourceFilterBrowserTest.InvalidRuleset_Checksum, corrupting
// in this manner doesn't invalidate the Flatbuffer Verifier check.
testing::TestRuleset::CorruptByFilling(rulesets().indexed_1(), 28250, 28251,
32);
base::File file = ruleset_dealer()->OpenAndSetRulesetFile(
expected_checksum, rulesets().indexed_1().path);
// Check the required file is opened.
ASSERT_TRUE(file.IsValid());
// Check |OpenAndSetRulesetFile| forwards call to |SetRulesetFile| on success.
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
ruleset_dealer()->status());
histogram_tester().ExpectTotalCount(kVerificationHistogram, 0);
// Check that after getting the ruleset the expected values are present.
scoped_refptr<const MemoryMappedRuleset> ref_to_ruleset =
ruleset_dealer()->GetRuleset();
EXPECT_FALSE(ref_to_ruleset);
EXPECT_EQ(RulesetVerificationStatus::kCorrupt, ruleset_dealer()->status());
histogram_tester().ExpectUniqueSample(kVerificationHistogram,
RulesetVerificationStatus::kCorrupt, 1);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
OpenAndSetRulesetFileReturnsCorrectFileOnSuccess) {
base::File file = ruleset_dealer()->OpenAndSetRulesetFile(
/*expected_checksum=*/0, rulesets().indexed_1().path);
// Check the required file is opened.
ASSERT_TRUE(file.IsValid());
EXPECT_EQ(rulesets().indexed_1().contents, ReadFileContent(&file));
// Check |OpenAndSetRulesetFile| forwards call to |SetRulesetFile| on success.
EXPECT_TRUE(ruleset_dealer()->IsRulesetFileAvailable());
EXPECT_FALSE(has_cached_ruleset());
EXPECT_EQ(RulesetVerificationStatus::kNotVerified,
ruleset_dealer()->status());
histogram_tester().ExpectTotalCount(kVerificationHistogram, 0);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerTest,
OpenAndSetRulesetFileReturnsNullFileOnFailure) {
base::File file = ruleset_dealer()->OpenAndSetRulesetFile(
/*expected_checksum=*/0,
base::FilePath::FromUTF8Unsafe("non_existent_file"));
EXPECT_FALSE(file.IsValid());
EXPECT_FALSE(ruleset_dealer()->IsRulesetFileAvailable());
histogram_tester().ExpectTotalCount(kVerificationHistogram, 0);
}
// Tests for VerifiedRulesetDealer::Handle. ------------------------------------
namespace {
class TestVerifiedRulesetDealerClient {
public:
TestVerifiedRulesetDealerClient() = default;
base::OnceCallback<void(VerifiedRulesetDealer*)> GetCallback() {
return base::BindOnce(&TestVerifiedRulesetDealerClient::Callback,
base::Unretained(this));
}
void ExpectRulesetState(bool expected_availability,
RulesetVerificationStatus expected_status =
RulesetVerificationStatus::kNotVerified,
bool expected_cached = false) const {
ASSERT_EQ(1, invocation_counter_);
EXPECT_EQ(expected_availability, is_ruleset_file_available_);
EXPECT_EQ(expected_cached, has_cached_ruleset_);
EXPECT_EQ(expected_status, status_);
}
void ExpectRulesetContents(const std::vector<uint8_t>& expected_contents,
bool expected_cached = false) const {
ExpectRulesetState(true, RulesetVerificationStatus::kIntact,
expected_cached);
EXPECT_TRUE(ruleset_is_created_);
EXPECT_EQ(expected_contents, contents_);
}
private:
void Callback(VerifiedRulesetDealer* dealer) {
++invocation_counter_;
ASSERT_TRUE(dealer);
is_ruleset_file_available_ = dealer->IsRulesetFileAvailable();
has_cached_ruleset_ = dealer->has_cached_ruleset();
status_ = dealer->status();
auto ruleset = dealer->GetRuleset();
ruleset_is_created_ = !!ruleset;
if (ruleset_is_created_)
contents_ = ReadRulesetContents(ruleset.get());
}
bool is_ruleset_file_available_ = false;
bool has_cached_ruleset_ = false;
RulesetVerificationStatus status_ = RulesetVerificationStatus::kNotVerified;
bool ruleset_is_created_ = false;
std::vector<uint8_t> contents_;
int invocation_counter_ = 0;
DISALLOW_COPY_AND_ASSIGN(TestVerifiedRulesetDealerClient);
};
} // namespace
class SubresourceFilterVerifiedRulesetDealerHandleTest
: public ::testing::Test {
public:
SubresourceFilterVerifiedRulesetDealerHandleTest() = default;
protected:
void SetUp() override {
rulesets_.CreateRulesets(false /* many_rules */);
task_runner_ = new base::TestSimpleTaskRunner;
}
const TestRulesets& rulesets() const { return rulesets_; }
base::TestSimpleTaskRunner* task_runner() { return task_runner_.get(); }
private:
TestRulesets rulesets_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
content::TestBrowserThreadBundle thread_bundle_;
DISALLOW_COPY_AND_ASSIGN(SubresourceFilterVerifiedRulesetDealerHandleTest);
};
TEST_F(SubresourceFilterVerifiedRulesetDealerHandleTest,
RulesetIsMappedLazily) {
TestVerifiedRulesetDealerClient before_set_ruleset;
TestVerifiedRulesetDealerClient after_set_ruleset;
TestVerifiedRulesetDealerClient after_warm_up;
std::unique_ptr<VerifiedRulesetDealer::Handle> dealer_handle(
new VerifiedRulesetDealer::Handle(task_runner()));
dealer_handle->GetDealerAsync(before_set_ruleset.GetCallback());
dealer_handle->TryOpenAndSetRulesetFile(rulesets().indexed_1().path,
/*expected_checksum=*/0,
base::DoNothing());
dealer_handle->GetDealerAsync(after_set_ruleset.GetCallback());
dealer_handle->GetDealerAsync(after_warm_up.GetCallback());
dealer_handle.reset(nullptr);
task_runner()->RunUntilIdle();
before_set_ruleset.ExpectRulesetState(false);
after_set_ruleset.ExpectRulesetState(true);
after_warm_up.ExpectRulesetContents(rulesets().indexed_1().contents);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerHandleTest, RulesetFileIsUpdated) {
TestVerifiedRulesetDealerClient after_set_ruleset_1;
TestVerifiedRulesetDealerClient read_ruleset_1;
TestVerifiedRulesetDealerClient after_set_ruleset_2;
TestVerifiedRulesetDealerClient read_ruleset_2;
std::unique_ptr<VerifiedRulesetDealer::Handle> dealer_handle(
new VerifiedRulesetDealer::Handle(task_runner()));
dealer_handle->TryOpenAndSetRulesetFile(
rulesets().indexed_1().path, /*expected_checksum=*/0, base::DoNothing());
dealer_handle->GetDealerAsync(after_set_ruleset_1.GetCallback());
dealer_handle->GetDealerAsync(read_ruleset_1.GetCallback());
dealer_handle->TryOpenAndSetRulesetFile(
rulesets().indexed_2().path, /*expected_checksum=*/0, base::DoNothing());
dealer_handle->GetDealerAsync(after_set_ruleset_2.GetCallback());
dealer_handle->GetDealerAsync(read_ruleset_2.GetCallback());
dealer_handle.reset(nullptr);
task_runner()->RunUntilIdle();
after_set_ruleset_1.ExpectRulesetState(true);
read_ruleset_1.ExpectRulesetContents(rulesets().indexed_1().contents);
after_set_ruleset_2.ExpectRulesetState(true);
read_ruleset_2.ExpectRulesetContents(rulesets().indexed_2().contents);
}
TEST_F(SubresourceFilterVerifiedRulesetDealerHandleTest,
InvalidFileDoesNotReplaceTheValidOne) {
TestVerifiedRulesetDealerClient after_set_ruleset_1;
TestVerifiedRulesetDealerClient read_ruleset_1;
TestVerifiedRulesetDealerClient after_set_ruleset_2;
TestVerifiedRulesetDealerClient read_ruleset_2;
auto dealer_handle =
std::make_unique<VerifiedRulesetDealer::Handle>(task_runner());
dealer_handle->TryOpenAndSetRulesetFile(
rulesets().indexed_1().path, /*expected_checksum=*/0, base::DoNothing());
dealer_handle->GetDealerAsync(after_set_ruleset_1.GetCallback());
dealer_handle->GetDealerAsync(read_ruleset_1.GetCallback());
dealer_handle->TryOpenAndSetRulesetFile(
base::FilePath::FromUTF8Unsafe("non_existent_file"),
/*expected_checksum=*/0,
base::BindOnce([](base::File file) { EXPECT_FALSE(file.IsValid()); }));
dealer_handle->GetDealerAsync(after_set_ruleset_2.GetCallback());
dealer_handle->GetDealerAsync(read_ruleset_2.GetCallback());
dealer_handle.reset(nullptr);
task_runner()->RunUntilIdle();
after_set_ruleset_1.ExpectRulesetState(true);
read_ruleset_1.ExpectRulesetContents(rulesets().indexed_1().contents);
after_set_ruleset_2.ExpectRulesetState(true,
RulesetVerificationStatus::kIntact);
read_ruleset_2.ExpectRulesetContents(rulesets().indexed_1().contents);
}
// Tests for VerifiedRuleset::Handle. ------------------------------------------
namespace {
class TestVerifiedRulesetClient {
public:
TestVerifiedRulesetClient() = default;
base::OnceCallback<void(VerifiedRuleset*)> GetCallback() {
return base::BindOnce(&TestVerifiedRulesetClient::Callback,
base::Unretained(this));
}
void ExpectNoRuleset() const {
ASSERT_EQ(1, invocation_counter_);
EXPECT_FALSE(has_ruleset_);
}
void ExpectRulesetContents(
const std::vector<uint8_t> expected_contents) const {
ASSERT_EQ(1, invocation_counter_);
EXPECT_EQ(expected_contents, contents_);
}
private:
void Callback(VerifiedRuleset* ruleset) {
++invocation_counter_;
ASSERT_TRUE(ruleset);
has_ruleset_ = !!ruleset->Get();
if (has_ruleset_)
contents_ = ReadRulesetContents(ruleset->Get());
}
bool has_ruleset_ = false;
std::vector<uint8_t> contents_;
int invocation_counter_ = 0;
DISALLOW_COPY_AND_ASSIGN(TestVerifiedRulesetClient);
};
} // namespace
class SubresourceFilterVerifiedRulesetHandleTest : public ::testing::Test {
public:
SubresourceFilterVerifiedRulesetHandleTest() = default;
protected:
void SetUp() override {
rulesets_.CreateRulesets(true /* many_rules */);
task_runner_ = new base::TestSimpleTaskRunner;
dealer_handle_.reset(new VerifiedRulesetDealer::Handle(task_runner_));
}
void TearDown() override {
dealer_handle_.reset(nullptr);
task_runner_->RunUntilIdle();
}
const TestRulesets& rulesets() const { return rulesets_; }
base::TestSimpleTaskRunner* task_runner() { return task_runner_.get(); }
VerifiedRulesetDealer::Handle* dealer_handle() {
return dealer_handle_.get();
}
std::unique_ptr<VerifiedRuleset::Handle> CreateRulesetHandle() {
return std::unique_ptr<VerifiedRuleset::Handle>(
new VerifiedRuleset::Handle(dealer_handle()));
}
private:
TestRulesets rulesets_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
std::unique_ptr<VerifiedRulesetDealer::Handle> dealer_handle_;
content::TestBrowserThreadBundle thread_bundle_;
DISALLOW_COPY_AND_ASSIGN(SubresourceFilterVerifiedRulesetHandleTest);
};
TEST_F(SubresourceFilterVerifiedRulesetHandleTest,
RulesetHandleKeepsRulesetMemoryMappedAndVerified) {
TestVerifiedRulesetDealerClient created_handle;
TestVerifiedRulesetClient read_ruleset;
TestVerifiedRulesetDealerClient deleted_handle;
dealer_handle()->TryOpenAndSetRulesetFile(
rulesets().indexed_1().path, /*expected_checksum=*/0,
base::BindOnce([](base::File file) { EXPECT_TRUE(file.IsValid()); }));
auto ruleset_handle = CreateRulesetHandle();
dealer_handle()->GetDealerAsync(created_handle.GetCallback());
ruleset_handle->GetRulesetAsync(read_ruleset.GetCallback());
ruleset_handle.reset(nullptr);
dealer_handle()->GetDealerAsync(deleted_handle.GetCallback());
task_runner()->RunUntilIdle();
created_handle.ExpectRulesetContents(rulesets().indexed_1().contents, true);
read_ruleset.ExpectRulesetContents(rulesets().indexed_1().contents);
deleted_handle.ExpectRulesetState(true, RulesetVerificationStatus::kIntact);
}
TEST_F(SubresourceFilterVerifiedRulesetHandleTest,
RulesetUnmappedOnlyAfterLastHandleIsDeleted) {
TestVerifiedRulesetDealerClient created_handles;
TestVerifiedRulesetClient read_ruleset_from_handle_1;
TestVerifiedRulesetClient read_ruleset_from_handle_2;
TestVerifiedRulesetDealerClient deleted_handle_1;
TestVerifiedRulesetClient read_ruleset_again_from_handle_2;
TestVerifiedRulesetDealerClient deleted_both_handles;
dealer_handle()->TryOpenAndSetRulesetFile(
rulesets().indexed_1().path, /*expected_checksum=*/0,
base::BindOnce([](base::File file) { EXPECT_TRUE(file.IsValid()); }));
auto ruleset_handle_1 = CreateRulesetHandle();
auto ruleset_handle_2 = CreateRulesetHandle();
dealer_handle()->GetDealerAsync(created_handles.GetCallback());
ruleset_handle_1->GetRulesetAsync(read_ruleset_from_handle_1.GetCallback());
ruleset_handle_2->GetRulesetAsync(read_ruleset_from_handle_2.GetCallback());
ruleset_handle_1.reset(nullptr);
dealer_handle()->GetDealerAsync(deleted_handle_1.GetCallback());
ruleset_handle_2->GetRulesetAsync(
read_ruleset_again_from_handle_2.GetCallback());
ruleset_handle_2.reset(nullptr);
dealer_handle()->GetDealerAsync(deleted_both_handles.GetCallback());
task_runner()->RunUntilIdle();
created_handles.ExpectRulesetContents(rulesets().indexed_1().contents, true);
read_ruleset_from_handle_1.ExpectRulesetContents(
rulesets().indexed_1().contents);
read_ruleset_from_handle_2.ExpectRulesetContents(
rulesets().indexed_1().contents);
deleted_handle_1.ExpectRulesetContents(rulesets().indexed_1().contents, true);
read_ruleset_again_from_handle_2.ExpectRulesetContents(
rulesets().indexed_1().contents);
deleted_both_handles.ExpectRulesetState(true,
RulesetVerificationStatus::kIntact);
}
TEST_F(SubresourceFilterVerifiedRulesetHandleTest,
OldRulesetRemainsMappedAfterUpdateUntilHandleIsDeleted) {
TestVerifiedRulesetDealerClient created_handle_1;
TestVerifiedRulesetClient read_from_handle_1;
TestVerifiedRulesetDealerClient created_handle_2_after_update;
TestVerifiedRulesetClient read_from_handle_2;
TestVerifiedRulesetClient read_again_from_handle_1;
TestVerifiedRulesetClient read_from_handle_1_after_update;
TestVerifiedRulesetClient read_from_handle_2_after_update;
TestVerifiedRulesetDealerClient deleted_all_handles;
dealer_handle()->TryOpenAndSetRulesetFile(
rulesets().indexed_1().path, /*expected_checksum=*/0,
base::BindOnce([](base::File file) { EXPECT_TRUE(file.IsValid()); }));
auto ruleset_handle_1 = CreateRulesetHandle();
dealer_handle()->GetDealerAsync(created_handle_1.GetCallback());
ruleset_handle_1->GetRulesetAsync(read_from_handle_1.GetCallback());
dealer_handle()->TryOpenAndSetRulesetFile(
rulesets().indexed_2().path, /*expected_checksum=*/0, base::DoNothing());
auto ruleset_handle_2 = CreateRulesetHandle();
dealer_handle()->GetDealerAsync(created_handle_2_after_update.GetCallback());
ruleset_handle_2->GetRulesetAsync(read_from_handle_2.GetCallback());
ruleset_handle_1->GetRulesetAsync(read_again_from_handle_1.GetCallback());
ruleset_handle_1 = CreateRulesetHandle();
ruleset_handle_1->GetRulesetAsync(
read_from_handle_1_after_update.GetCallback());
ruleset_handle_2->GetRulesetAsync(
read_from_handle_2_after_update.GetCallback());
ruleset_handle_1.reset(nullptr);
ruleset_handle_2.reset(nullptr);
dealer_handle()->GetDealerAsync(deleted_all_handles.GetCallback());
task_runner()->RunUntilIdle();
created_handle_1.ExpectRulesetContents(rulesets().indexed_1().contents, true);
read_from_handle_1.ExpectRulesetContents(rulesets().indexed_1().contents);
created_handle_2_after_update.ExpectRulesetContents(
rulesets().indexed_2().contents, true);
read_from_handle_2.ExpectRulesetContents(rulesets().indexed_2().contents);
read_again_from_handle_1.ExpectRulesetContents(
rulesets().indexed_1().contents);
read_from_handle_1_after_update.ExpectRulesetContents(
rulesets().indexed_2().contents);
read_from_handle_2_after_update.ExpectRulesetContents(
rulesets().indexed_2().contents);
deleted_all_handles.ExpectRulesetState(true,
RulesetVerificationStatus::kIntact);
}
TEST_F(SubresourceFilterVerifiedRulesetHandleTest,
CorruptRulesetIsNotHandedOut) {
TestVerifiedRulesetDealerClient created_handle;
TestVerifiedRulesetClient read_ruleset;
TestVerifiedRulesetDealerClient deleted_handle;
testing::TestRuleset::CorruptByTruncating(rulesets().indexed_1(), 4096);
dealer_handle()->TryOpenAndSetRulesetFile(
rulesets().indexed_1().path, /*expected_checksum=*/0,
base::BindOnce([](base::File file) { EXPECT_TRUE(file.IsValid()); }));
auto ruleset_handle = CreateRulesetHandle();
dealer_handle()->GetDealerAsync(created_handle.GetCallback());
ruleset_handle->GetRulesetAsync(read_ruleset.GetCallback());
ruleset_handle.reset(nullptr);
dealer_handle()->GetDealerAsync(deleted_handle.GetCallback());
task_runner()->RunUntilIdle();
created_handle.ExpectRulesetState(true, RulesetVerificationStatus::kCorrupt);
read_ruleset.ExpectNoRuleset();
deleted_handle.ExpectRulesetState(true, RulesetVerificationStatus::kCorrupt);
}
} // namespace subresource_filter