1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/safe_browsing/safe_browsing_store_file.h" 6 7#include "base/bind.h" 8#include "base/files/scoped_temp_dir.h" 9#include "base/md5.h" 10#include "chrome/browser/safe_browsing/safe_browsing_store_unittest_helper.h" 11#include "testing/gtest/include/gtest/gtest.h" 12#include "testing/platform_test.h" 13 14namespace { 15 16const base::FilePath::CharType kFolderPrefix[] = 17 FILE_PATH_LITERAL("SafeBrowsingTestStoreFile"); 18 19class SafeBrowsingStoreFileTest : public PlatformTest { 20 public: 21 virtual void SetUp() { 22 PlatformTest::SetUp(); 23 24 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 25 26 filename_ = temp_dir_.path(); 27 filename_ = filename_.AppendASCII("SafeBrowsingTestStore"); 28 29 store_.reset(new SafeBrowsingStoreFile()); 30 store_->Init(filename_, 31 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected, 32 base::Unretained(this))); 33 corruption_detected_ = false; 34 } 35 virtual void TearDown() { 36 if (store_.get()) 37 store_->Delete(); 38 store_.reset(); 39 40 PlatformTest::TearDown(); 41 } 42 43 void OnCorruptionDetected() { 44 corruption_detected_ = true; 45 } 46 47 base::ScopedTempDir temp_dir_; 48 base::FilePath filename_; 49 scoped_ptr<SafeBrowsingStoreFile> store_; 50 bool corruption_detected_; 51}; 52 53TEST_STORE(SafeBrowsingStoreFileTest, store_.get(), filename_); 54 55// Test that Delete() deletes the temporary store, if present. 56TEST_F(SafeBrowsingStoreFileTest, DeleteTemp) { 57 const base::FilePath temp_file = 58 SafeBrowsingStoreFile::TemporaryFileForFilename(filename_); 59 60 EXPECT_FALSE(base::PathExists(filename_)); 61 EXPECT_FALSE(base::PathExists(temp_file)); 62 63 // Starting a transaction creates a temporary file. 64 EXPECT_TRUE(store_->BeginUpdate()); 65 EXPECT_TRUE(base::PathExists(temp_file)); 66 67 // Pull the rug out from under the existing store, simulating a 68 // crash. 69 store_.reset(new SafeBrowsingStoreFile()); 70 store_->Init(filename_, base::Closure()); 71 EXPECT_FALSE(base::PathExists(filename_)); 72 EXPECT_TRUE(base::PathExists(temp_file)); 73 74 // Make sure the temporary file is deleted. 75 EXPECT_TRUE(store_->Delete()); 76 EXPECT_FALSE(base::PathExists(filename_)); 77 EXPECT_FALSE(base::PathExists(temp_file)); 78} 79 80// Test basic corruption-handling. 81TEST_F(SafeBrowsingStoreFileTest, DetectsCorruption) { 82 // Load a store with some data. 83 SafeBrowsingStoreTestStorePrefix(store_.get()); 84 85 // Can successfully open and read the store. 86 std::vector<SBAddFullHash> pending_adds; 87 std::set<SBPrefix> prefix_misses; 88 SBAddPrefixes orig_prefixes; 89 std::vector<SBAddFullHash> orig_hashes; 90 EXPECT_TRUE(store_->BeginUpdate()); 91 EXPECT_TRUE(store_->FinishUpdate(pending_adds, prefix_misses, 92 &orig_prefixes, &orig_hashes)); 93 EXPECT_GT(orig_prefixes.size(), 0U); 94 EXPECT_GT(orig_hashes.size(), 0U); 95 EXPECT_FALSE(corruption_detected_); 96 97 // Corrupt the store. 98 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb+")); 99 const long kOffset = 60; 100 EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0); 101 const int32 kZero = 0; 102 int32 previous = kZero; 103 EXPECT_EQ(fread(&previous, sizeof(previous), 1, file.get()), 1U); 104 EXPECT_NE(previous, kZero); 105 EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0); 106 EXPECT_EQ(fwrite(&kZero, sizeof(kZero), 1, file.get()), 1U); 107 file.reset(); 108 109 // Update fails and corruption callback is called. 110 SBAddPrefixes add_prefixes; 111 std::vector<SBAddFullHash> add_hashes; 112 corruption_detected_ = false; 113 EXPECT_TRUE(store_->BeginUpdate()); 114 EXPECT_FALSE(store_->FinishUpdate(pending_adds, prefix_misses, 115 &add_prefixes, &add_hashes)); 116 EXPECT_TRUE(corruption_detected_); 117 EXPECT_EQ(add_prefixes.size(), 0U); 118 EXPECT_EQ(add_hashes.size(), 0U); 119 120 // Make it look like there is a lot of add-chunks-seen data. 121 const long kAddChunkCountOffset = 2 * sizeof(int32); 122 const int32 kLargeCount = 1000 * 1000 * 1000; 123 file.reset(file_util::OpenFile(filename_, "rb+")); 124 EXPECT_EQ(fseek(file.get(), kAddChunkCountOffset, SEEK_SET), 0); 125 EXPECT_EQ(fwrite(&kLargeCount, sizeof(kLargeCount), 1, file.get()), 1U); 126 file.reset(); 127 128 // Detects corruption and fails to even begin the update. 129 corruption_detected_ = false; 130 EXPECT_FALSE(store_->BeginUpdate()); 131 EXPECT_TRUE(corruption_detected_); 132} 133 134TEST_F(SafeBrowsingStoreFileTest, CheckValidity) { 135 // Empty store is valid. 136 EXPECT_FALSE(base::PathExists(filename_)); 137 ASSERT_TRUE(store_->BeginUpdate()); 138 EXPECT_FALSE(corruption_detected_); 139 EXPECT_TRUE(store_->CheckValidity()); 140 EXPECT_FALSE(corruption_detected_); 141 EXPECT_TRUE(store_->CancelUpdate()); 142 143 // A store with some data is valid. 144 EXPECT_FALSE(base::PathExists(filename_)); 145 SafeBrowsingStoreTestStorePrefix(store_.get()); 146 EXPECT_TRUE(base::PathExists(filename_)); 147 ASSERT_TRUE(store_->BeginUpdate()); 148 EXPECT_FALSE(corruption_detected_); 149 EXPECT_TRUE(store_->CheckValidity()); 150 EXPECT_FALSE(corruption_detected_); 151 EXPECT_TRUE(store_->CancelUpdate()); 152} 153 154// Corrupt the payload. 155TEST_F(SafeBrowsingStoreFileTest, CheckValidityPayload) { 156 SafeBrowsingStoreTestStorePrefix(store_.get()); 157 EXPECT_TRUE(base::PathExists(filename_)); 158 159 // 37 is the most random prime number. It's also past the header, 160 // as corrupting the header would fail BeginUpdate() in which case 161 // CheckValidity() cannot be called. 162 const size_t kOffset = 37; 163 164 { 165 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb+")); 166 EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_SET)); 167 EXPECT_GE(fputs("hello", file.get()), 0); 168 } 169 ASSERT_TRUE(store_->BeginUpdate()); 170 EXPECT_FALSE(corruption_detected_); 171 EXPECT_FALSE(store_->CheckValidity()); 172 EXPECT_TRUE(corruption_detected_); 173 EXPECT_TRUE(store_->CancelUpdate()); 174} 175 176// Corrupt the checksum. 177TEST_F(SafeBrowsingStoreFileTest, CheckValidityChecksum) { 178 SafeBrowsingStoreTestStorePrefix(store_.get()); 179 EXPECT_TRUE(base::PathExists(filename_)); 180 181 // An offset from the end of the file which is in the checksum. 182 const int kOffset = -static_cast<int>(sizeof(base::MD5Digest)); 183 184 { 185 file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb+")); 186 EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_END)); 187 EXPECT_GE(fputs("hello", file.get()), 0); 188 } 189 ASSERT_TRUE(store_->BeginUpdate()); 190 EXPECT_FALSE(corruption_detected_); 191 EXPECT_FALSE(store_->CheckValidity()); 192 EXPECT_TRUE(corruption_detected_); 193 EXPECT_TRUE(store_->CancelUpdate()); 194} 195 196} // namespace 197