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.
5 #include "chrome/browser/safe_browsing/safe_browsing_store_file.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_file.h"
10 #include "base/files/scoped_temp_dir.h"
12 #include "base/path_service.h"
13 #include "base/test/test_simple_task_runner.h"
14 #include "chrome/common/chrome_paths.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "testing/platform_test.h"
20 const int kAddChunk1
= 1;
21 const int kAddChunk2
= 3;
22 const int kAddChunk3
= 5;
23 const int kAddChunk4
= 7;
24 // Disjoint chunk numbers for subs to flush out typos.
25 const int kSubChunk1
= 2;
26 const int kSubChunk2
= 4;
28 const SBFullHash kHash1
= SBFullHashForString("one");
29 const SBFullHash kHash2
= SBFullHashForString("two");
30 const SBFullHash kHash3
= SBFullHashForString("three");
31 const SBFullHash kHash4
= SBFullHashForString("four");
32 const SBFullHash kHash5
= SBFullHashForString("five");
33 const SBFullHash kHash6
= SBFullHashForString("six");
35 const SBPrefix kMinSBPrefix
= 0u;
36 const SBPrefix kMaxSBPrefix
= ~kMinSBPrefix
;
40 namespace safe_browsing
{
42 class SafeBrowsingStoreFileTest
: public PlatformTest
{
44 SafeBrowsingStoreFileTest()
45 : task_runner_(new base::TestSimpleTaskRunner
),
46 corruption_detected_(false) {}
48 void SetUp() override
{
49 PlatformTest::SetUp();
51 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
53 filename_
= temp_dir_
.path();
54 filename_
= filename_
.AppendASCII("SafeBrowsingTestStore");
56 store_
.reset(new SafeBrowsingStoreFile(task_runner_
));
57 store_
->Init(filename_
,
58 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected
,
59 base::Unretained(this)));
61 void TearDown() override
{
66 PlatformTest::TearDown();
69 void OnCorruptionDetected() {
70 corruption_detected_
= true;
73 // Populate the store with some testing data.
74 void PopulateStore() {
75 ASSERT_TRUE(store_
->BeginUpdate());
77 EXPECT_TRUE(store_
->BeginChunk());
78 store_
->SetAddChunk(kAddChunk1
);
79 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
80 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash1
.prefix
));
81 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash2
.prefix
));
82 EXPECT_TRUE(store_
->FinishChunk());
84 EXPECT_TRUE(store_
->BeginChunk());
85 store_
->SetSubChunk(kSubChunk1
);
86 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
87 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk3
, kHash3
.prefix
));
88 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk3
, kHash3
));
89 EXPECT_TRUE(store_
->FinishChunk());
91 EXPECT_TRUE(store_
->BeginChunk());
92 store_
->SetAddChunk(kAddChunk2
);
93 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk2
));
94 EXPECT_TRUE(store_
->WriteAddHash(kAddChunk2
, kHash4
));
95 EXPECT_TRUE(store_
->FinishChunk());
97 // Chunk numbers shouldn't leak over.
98 EXPECT_FALSE(store_
->CheckAddChunk(kSubChunk1
));
99 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk3
));
100 EXPECT_FALSE(store_
->CheckSubChunk(kAddChunk1
));
101 EXPECT_FALSE(store_
->CheckSubChunk(kAddChunk2
));
103 safe_browsing::PrefixSetBuilder builder
;
104 std::vector
<SBAddFullHash
> add_full_hashes_result
;
106 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
109 // Manually read the shard stride info from the file.
110 uint32
ReadStride() {
111 base::ScopedFILE
file(base::OpenFile(filename_
, "rb"));
112 const long kOffset
= 4 * sizeof(uint32
);
113 EXPECT_EQ(fseek(file
.get(), kOffset
, SEEK_SET
), 0);
114 uint32 shard_stride
= 0;
115 EXPECT_EQ(fread(&shard_stride
, sizeof(shard_stride
), 1, file
.get()), 1U);
119 scoped_refptr
<base::TestSimpleTaskRunner
> task_runner_
;
120 base::ScopedTempDir temp_dir_
;
121 base::FilePath filename_
;
122 scoped_ptr
<SafeBrowsingStoreFile
> store_
;
123 bool corruption_detected_
;
126 // Test that the empty store looks empty.
127 TEST_F(SafeBrowsingStoreFileTest
, Empty
) {
128 ASSERT_TRUE(store_
->BeginUpdate());
130 std::vector
<int> chunks
;
131 store_
->GetAddChunks(&chunks
);
132 EXPECT_TRUE(chunks
.empty());
133 store_
->GetSubChunks(&chunks
);
134 EXPECT_TRUE(chunks
.empty());
136 // Shouldn't see anything, but anything is a big set to test.
137 EXPECT_FALSE(store_
->CheckAddChunk(0));
138 EXPECT_FALSE(store_
->CheckAddChunk(1));
139 EXPECT_FALSE(store_
->CheckAddChunk(-1));
141 EXPECT_FALSE(store_
->CheckSubChunk(0));
142 EXPECT_FALSE(store_
->CheckSubChunk(1));
143 EXPECT_FALSE(store_
->CheckSubChunk(-1));
145 safe_browsing::PrefixSetBuilder builder
;
146 std::vector
<SBAddFullHash
> add_full_hashes_result
;
148 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
149 EXPECT_TRUE(add_full_hashes_result
.empty());
151 std::vector
<SBPrefix
> prefixes_result
;
152 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
153 EXPECT_TRUE(prefixes_result
.empty());
156 // Write some prefix and hash data to the store, add more data in another
157 // transaction, then verify that the union of all the data is present.
158 TEST_F(SafeBrowsingStoreFileTest
, BasicStore
) {
161 ASSERT_TRUE(store_
->BeginUpdate());
163 std::vector
<int> chunks
;
164 store_
->GetAddChunks(&chunks
);
165 ASSERT_EQ(2U, chunks
.size());
166 EXPECT_EQ(kAddChunk1
, chunks
[0]);
167 EXPECT_EQ(kAddChunk2
, chunks
[1]);
169 store_
->GetSubChunks(&chunks
);
170 ASSERT_EQ(1U, chunks
.size());
171 EXPECT_EQ(kSubChunk1
, chunks
[0]);
173 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
174 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
176 EXPECT_TRUE(store_
->BeginChunk());
177 store_
->SetAddChunk(kAddChunk3
);
178 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kHash5
.prefix
));
179 EXPECT_TRUE(store_
->FinishChunk());
181 // Still has the chunks expected in the next update.
183 safe_browsing::PrefixSetBuilder builder
;
184 std::vector
<SBAddFullHash
> add_full_hashes_result
;
185 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
187 std::vector
<SBPrefix
> prefixes_result
;
188 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
189 ASSERT_EQ(3U, prefixes_result
.size());
190 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
191 EXPECT_EQ(kHash5
.prefix
, prefixes_result
[1]);
192 EXPECT_EQ(kHash2
.prefix
, prefixes_result
[2]);
194 ASSERT_EQ(1U, add_full_hashes_result
.size());
195 EXPECT_EQ(kAddChunk2
, add_full_hashes_result
[0].chunk_id
);
196 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_full_hashes_result
[0].full_hash
));
200 // Verify that the min and max prefixes are stored and operated on.
201 TEST_F(SafeBrowsingStoreFileTest
, PrefixMinMax
) {
204 ASSERT_TRUE(store_
->BeginUpdate());
206 EXPECT_TRUE(store_
->BeginChunk());
207 store_
->SetAddChunk(kAddChunk3
);
208 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kMinSBPrefix
));
209 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kMaxSBPrefix
));
210 EXPECT_TRUE(store_
->FinishChunk());
213 safe_browsing::PrefixSetBuilder builder
;
214 std::vector
<SBAddFullHash
> add_full_hashes_result
;
215 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
217 std::vector
<SBPrefix
> prefixes_result
;
218 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
219 ASSERT_EQ(4U, prefixes_result
.size());
220 EXPECT_EQ(kMinSBPrefix
, prefixes_result
[0]);
221 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[1]);
222 EXPECT_EQ(kHash2
.prefix
, prefixes_result
[2]);
223 EXPECT_EQ(kMaxSBPrefix
, prefixes_result
[3]);
226 ASSERT_TRUE(store_
->BeginUpdate());
228 EXPECT_TRUE(store_
->BeginChunk());
229 store_
->SetAddChunk(kSubChunk2
);
230 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk2
, kAddChunk3
, kMinSBPrefix
));
231 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk2
, kAddChunk3
, kMaxSBPrefix
));
232 EXPECT_TRUE(store_
->FinishChunk());
235 safe_browsing::PrefixSetBuilder builder
;
236 std::vector
<SBAddFullHash
> add_full_hashes_result
;
237 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
239 std::vector
<SBPrefix
> prefixes_result
;
240 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
241 ASSERT_EQ(2U, prefixes_result
.size());
242 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
243 EXPECT_EQ(kHash2
.prefix
, prefixes_result
[1]);
247 // Test that subs knockout adds.
248 TEST_F(SafeBrowsingStoreFileTest
, SubKnockout
) {
249 ASSERT_TRUE(store_
->BeginUpdate());
251 EXPECT_TRUE(store_
->BeginChunk());
252 store_
->SetAddChunk(kAddChunk1
);
253 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash1
.prefix
));
254 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash2
.prefix
));
255 EXPECT_TRUE(store_
->FinishChunk());
257 EXPECT_TRUE(store_
->BeginChunk());
258 store_
->SetAddChunk(kAddChunk2
);
259 EXPECT_TRUE(store_
->WriteAddHash(kAddChunk2
, kHash4
));
260 EXPECT_TRUE(store_
->FinishChunk());
262 EXPECT_TRUE(store_
->BeginChunk());
263 store_
->SetSubChunk(kSubChunk1
);
264 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk3
, kHash3
.prefix
));
265 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk1
, kHash2
.prefix
));
266 EXPECT_TRUE(store_
->FinishChunk());
269 safe_browsing::PrefixSetBuilder builder
;
270 std::vector
<SBAddFullHash
> add_full_hashes_result
;
271 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
273 // Knocked out the chunk expected.
274 std::vector
<SBPrefix
> prefixes_result
;
275 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
276 ASSERT_EQ(1U, prefixes_result
.size());
277 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
279 ASSERT_EQ(1U, add_full_hashes_result
.size());
280 EXPECT_EQ(kAddChunk2
, add_full_hashes_result
[0].chunk_id
);
281 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_full_hashes_result
[0].full_hash
));
284 ASSERT_TRUE(store_
->BeginUpdate());
286 // This add should be knocked out by an existing sub.
287 EXPECT_TRUE(store_
->BeginChunk());
288 store_
->SetAddChunk(kAddChunk3
);
289 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kHash3
.prefix
));
290 EXPECT_TRUE(store_
->FinishChunk());
293 safe_browsing::PrefixSetBuilder builder
;
294 std::vector
<SBAddFullHash
> add_full_hashes_result
;
295 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
297 std::vector
<SBPrefix
> prefixes_result
;
298 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
299 ASSERT_EQ(1U, prefixes_result
.size());
300 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
302 ASSERT_EQ(1U, add_full_hashes_result
.size());
303 EXPECT_EQ(kAddChunk2
, add_full_hashes_result
[0].chunk_id
);
304 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_full_hashes_result
[0].full_hash
));
307 ASSERT_TRUE(store_
->BeginUpdate());
309 // But by here the sub should be gone, so it should stick this time.
310 EXPECT_TRUE(store_
->BeginChunk());
311 store_
->SetAddChunk(kAddChunk3
);
312 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kHash3
.prefix
));
313 EXPECT_TRUE(store_
->FinishChunk());
316 safe_browsing::PrefixSetBuilder builder
;
317 std::vector
<SBAddFullHash
> add_full_hashes_result
;
318 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
320 std::vector
<SBPrefix
> prefixes_result
;
321 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
322 ASSERT_EQ(2U, prefixes_result
.size());
323 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
324 EXPECT_EQ(kHash3
.prefix
, prefixes_result
[1]);
326 ASSERT_EQ(1U, add_full_hashes_result
.size());
327 EXPECT_EQ(kAddChunk2
, add_full_hashes_result
[0].chunk_id
);
328 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_full_hashes_result
[0].full_hash
));
332 // Test that deletes delete the chunk's data.
333 TEST_F(SafeBrowsingStoreFileTest
, DeleteChunks
) {
334 ASSERT_TRUE(store_
->BeginUpdate());
336 // A prefix chunk which will be deleted.
337 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk1
));
338 store_
->SetAddChunk(kAddChunk1
);
339 EXPECT_TRUE(store_
->BeginChunk());
340 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash1
.prefix
));
341 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash2
.prefix
));
342 EXPECT_TRUE(store_
->FinishChunk());
344 // A prefix chunk which won't be deleted.
345 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk2
));
346 store_
->SetAddChunk(kAddChunk2
);
347 EXPECT_TRUE(store_
->BeginChunk());
348 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk2
, kHash3
.prefix
));
349 EXPECT_TRUE(store_
->FinishChunk());
351 // A full-hash chunk which won't be deleted.
352 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk3
));
353 store_
->SetAddChunk(kAddChunk3
);
354 EXPECT_TRUE(store_
->BeginChunk());
355 EXPECT_TRUE(store_
->WriteAddHash(kAddChunk3
, kHash6
));
356 EXPECT_TRUE(store_
->FinishChunk());
358 // A sub chunk to delete.
359 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk1
));
360 store_
->SetSubChunk(kSubChunk1
);
361 EXPECT_TRUE(store_
->BeginChunk());
362 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk3
, kHash4
));
363 EXPECT_TRUE(store_
->FinishChunk());
365 // A sub chunk to keep.
366 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk2
));
367 store_
->SetSubChunk(kSubChunk2
);
368 EXPECT_TRUE(store_
->BeginChunk());
369 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk2
, kAddChunk4
, kHash5
));
370 EXPECT_TRUE(store_
->FinishChunk());
372 store_
->DeleteAddChunk(kAddChunk1
);
373 store_
->DeleteSubChunk(kSubChunk1
);
375 // Not actually deleted until finish.
376 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
377 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk2
));
378 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk3
));
379 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
380 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk2
));
383 safe_browsing::PrefixSetBuilder builder
;
384 std::vector
<SBAddFullHash
> add_full_hashes_result
;
385 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
387 std::vector
<SBPrefix
> prefixes_result
;
388 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
389 ASSERT_EQ(1U, prefixes_result
.size());
390 EXPECT_EQ(kHash3
.prefix
, prefixes_result
[0]);
392 ASSERT_EQ(1U, add_full_hashes_result
.size());
393 EXPECT_EQ(kAddChunk3
, add_full_hashes_result
[0].chunk_id
);
394 EXPECT_TRUE(SBFullHashEqual(kHash6
, add_full_hashes_result
[0].full_hash
));
397 // Expected chunks are there in another update.
398 ASSERT_TRUE(store_
->BeginUpdate());
399 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk1
));
400 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk2
));
401 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk3
));
402 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk1
));
403 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk2
));
406 store_
->DeleteAddChunk(kAddChunk2
);
407 store_
->DeleteAddChunk(kAddChunk3
);
408 store_
->DeleteSubChunk(kSubChunk2
);
411 safe_browsing::PrefixSetBuilder builder
;
412 std::vector
<SBAddFullHash
> add_full_hashes_result
;
413 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
415 std::vector
<SBPrefix
> prefixes_result
;
416 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
417 EXPECT_TRUE(prefixes_result
.empty());
418 EXPECT_TRUE(add_full_hashes_result
.empty());
421 // Expect no more chunks.
422 ASSERT_TRUE(store_
->BeginUpdate());
423 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk1
));
424 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk2
));
425 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk3
));
426 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk1
));
427 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk2
));
430 safe_browsing::PrefixSetBuilder builder
;
431 std::vector
<SBAddFullHash
> add_full_hashes_result
;
432 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
434 std::vector
<SBPrefix
> prefixes_result
;
435 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
436 EXPECT_TRUE(prefixes_result
.empty());
437 EXPECT_TRUE(add_full_hashes_result
.empty());
441 // Test that deleting the store deletes the store.
442 TEST_F(SafeBrowsingStoreFileTest
, Delete
) {
443 // Delete should work if the file wasn't there in the first place.
444 EXPECT_FALSE(base::PathExists(filename_
));
445 EXPECT_TRUE(store_
->Delete());
447 // Create a store file.
450 EXPECT_TRUE(base::PathExists(filename_
));
451 EXPECT_TRUE(store_
->Delete());
452 EXPECT_FALSE(base::PathExists(filename_
));
455 // Test that Delete() deletes the temporary store, if present.
456 TEST_F(SafeBrowsingStoreFileTest
, DeleteTemp
) {
457 const base::FilePath temp_file
=
458 SafeBrowsingStoreFile::TemporaryFileForFilename(filename_
);
460 EXPECT_FALSE(base::PathExists(filename_
));
461 EXPECT_FALSE(base::PathExists(temp_file
));
463 // Starting a transaction creates a temporary file.
464 ASSERT_TRUE(store_
->BeginUpdate());
465 EXPECT_TRUE(base::PathExists(temp_file
));
467 // Pull the rug out from under the existing store, simulating a
469 store_
.reset(new SafeBrowsingStoreFile(task_runner_
));
470 store_
->Init(filename_
, base::Closure());
471 EXPECT_FALSE(base::PathExists(filename_
));
472 EXPECT_TRUE(base::PathExists(temp_file
));
474 // Make sure the temporary file is deleted.
475 EXPECT_TRUE(store_
->Delete());
476 EXPECT_FALSE(base::PathExists(filename_
));
477 EXPECT_FALSE(base::PathExists(temp_file
));
480 // Test basic corruption-handling.
481 TEST_F(SafeBrowsingStoreFileTest
, DetectsCorruption
) {
482 // Load a store with some data.
485 // Can successfully open and read the store.
487 std::vector
<SBPrefix
> orig_prefixes
;
488 std::vector
<SBAddFullHash
> orig_hashes
;
489 safe_browsing::PrefixSetBuilder builder
;
490 ASSERT_TRUE(store_
->BeginUpdate());
491 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &orig_hashes
));
492 builder
.GetPrefixSetNoHashes()->GetPrefixes(&orig_prefixes
);
493 EXPECT_GT(orig_prefixes
.size(), 0U);
494 EXPECT_GT(orig_hashes
.size(), 0U);
495 EXPECT_FALSE(corruption_detected_
);
498 // Corrupt the store.
499 base::ScopedFILE
file(base::OpenFile(filename_
, "rb+"));
500 const long kOffset
= 60;
501 EXPECT_EQ(fseek(file
.get(), kOffset
, SEEK_SET
), 0);
502 const uint32 kZero
= 0;
503 uint32 previous
= kZero
;
504 EXPECT_EQ(fread(&previous
, sizeof(previous
), 1, file
.get()), 1U);
505 EXPECT_NE(previous
, kZero
);
506 EXPECT_EQ(fseek(file
.get(), kOffset
, SEEK_SET
), 0);
507 EXPECT_EQ(fwrite(&kZero
, sizeof(kZero
), 1, file
.get()), 1U);
510 // Update fails and corruption callback is called.
511 std::vector
<SBAddFullHash
> add_hashes
;
512 corruption_detected_
= false;
514 safe_browsing::PrefixSetBuilder builder
;
515 ASSERT_TRUE(store_
->BeginUpdate());
516 EXPECT_FALSE(store_
->FinishUpdate(&builder
, &add_hashes
));
517 EXPECT_TRUE(corruption_detected_
);
520 // Make it look like there is a lot of add-chunks-seen data.
521 const long kAddChunkCountOffset
= 2 * sizeof(int32
);
522 const int32 kLargeCount
= 1000 * 1000 * 1000;
523 file
.reset(base::OpenFile(filename_
, "rb+"));
524 EXPECT_EQ(fseek(file
.get(), kAddChunkCountOffset
, SEEK_SET
), 0);
525 EXPECT_EQ(fwrite(&kLargeCount
, sizeof(kLargeCount
), 1, file
.get()), 1U);
528 // Detects corruption and fails to even begin the update.
529 corruption_detected_
= false;
530 EXPECT_FALSE(store_
->BeginUpdate());
531 EXPECT_TRUE(corruption_detected_
);
534 TEST_F(SafeBrowsingStoreFileTest
, CheckValidity
) {
535 // Empty store is valid.
536 EXPECT_FALSE(base::PathExists(filename_
));
537 ASSERT_TRUE(store_
->BeginUpdate());
538 EXPECT_FALSE(corruption_detected_
);
539 EXPECT_TRUE(store_
->CheckValidity());
540 EXPECT_FALSE(corruption_detected_
);
541 EXPECT_TRUE(store_
->CancelUpdate());
543 // A store with some data is valid.
544 EXPECT_FALSE(base::PathExists(filename_
));
546 EXPECT_TRUE(base::PathExists(filename_
));
547 ASSERT_TRUE(store_
->BeginUpdate());
548 EXPECT_FALSE(corruption_detected_
);
549 EXPECT_TRUE(store_
->CheckValidity());
550 EXPECT_FALSE(corruption_detected_
);
551 EXPECT_TRUE(store_
->CancelUpdate());
554 // Corrupt the header.
555 TEST_F(SafeBrowsingStoreFileTest
, CheckValidityHeader
) {
557 EXPECT_TRUE(base::PathExists(filename_
));
559 // 37 is the most random prime number. It's also past the initial header
560 // struct, somewhere in the chunk list.
561 const size_t kOffset
= 37;
564 base::ScopedFILE
file(base::OpenFile(filename_
, "rb+"));
565 EXPECT_EQ(0, fseek(file
.get(), kOffset
, SEEK_SET
));
566 EXPECT_GE(fputs("hello", file
.get()), 0);
568 ASSERT_FALSE(store_
->BeginUpdate());
569 EXPECT_TRUE(corruption_detected_
);
572 // Corrupt the prefix payload.
573 TEST_F(SafeBrowsingStoreFileTest
, CheckValidityPayload
) {
575 EXPECT_TRUE(base::PathExists(filename_
));
577 // 137 is the second most random prime number. It's also past the header and
578 // chunk-id area. Corrupting the header would fail BeginUpdate() in which
579 // case CheckValidity() cannot be called.
580 const size_t kOffset
= 137;
583 base::ScopedFILE
file(base::OpenFile(filename_
, "rb+"));
584 EXPECT_EQ(0, fseek(file
.get(), kOffset
, SEEK_SET
));
585 EXPECT_GE(fputs("hello", file
.get()), 0);
587 ASSERT_TRUE(store_
->BeginUpdate());
588 EXPECT_FALSE(corruption_detected_
);
589 EXPECT_FALSE(store_
->CheckValidity());
590 EXPECT_TRUE(corruption_detected_
);
591 EXPECT_TRUE(store_
->CancelUpdate());
594 // Corrupt the checksum.
595 TEST_F(SafeBrowsingStoreFileTest
, CheckValidityChecksum
) {
597 EXPECT_TRUE(base::PathExists(filename_
));
599 // An offset from the end of the file which is in the checksum.
600 const int kOffset
= -static_cast<int>(sizeof(base::MD5Digest
));
603 base::ScopedFILE
file(base::OpenFile(filename_
, "rb+"));
604 EXPECT_EQ(0, fseek(file
.get(), kOffset
, SEEK_END
));
605 EXPECT_GE(fputs("hello", file
.get()), 0);
607 ASSERT_TRUE(store_
->BeginUpdate());
608 EXPECT_FALSE(corruption_detected_
);
609 EXPECT_FALSE(store_
->CheckValidity());
610 EXPECT_TRUE(corruption_detected_
);
611 EXPECT_TRUE(store_
->CancelUpdate());
614 TEST_F(SafeBrowsingStoreFileTest
, GetAddPrefixesAndHashes
) {
615 ASSERT_TRUE(store_
->BeginUpdate());
617 EXPECT_TRUE(store_
->BeginChunk());
618 store_
->SetAddChunk(kAddChunk1
);
619 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
620 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash1
.prefix
));
621 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash2
.prefix
));
622 EXPECT_TRUE(store_
->FinishChunk());
624 EXPECT_TRUE(store_
->BeginChunk());
625 store_
->SetAddChunk(kAddChunk2
);
626 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk2
));
627 EXPECT_TRUE(store_
->WriteAddHash(kAddChunk2
, kHash4
));
628 EXPECT_TRUE(store_
->FinishChunk());
630 store_
->SetSubChunk(kSubChunk1
);
631 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
632 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk3
, kHash3
.prefix
));
633 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk3
, kHash3
));
634 EXPECT_TRUE(store_
->FinishChunk());
636 // Chunk numbers shouldn't leak over.
637 EXPECT_FALSE(store_
->CheckAddChunk(kSubChunk1
));
638 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk3
));
639 EXPECT_FALSE(store_
->CheckSubChunk(kAddChunk1
));
641 std::vector
<int> chunks
;
642 store_
->GetAddChunks(&chunks
);
643 ASSERT_EQ(2U, chunks
.size());
644 EXPECT_EQ(kAddChunk1
, chunks
[0]);
645 EXPECT_EQ(kAddChunk2
, chunks
[1]);
647 store_
->GetSubChunks(&chunks
);
648 ASSERT_EQ(1U, chunks
.size());
649 EXPECT_EQ(kSubChunk1
, chunks
[0]);
651 safe_browsing::PrefixSetBuilder builder
;
652 std::vector
<SBAddFullHash
> add_full_hashes_result
;
653 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
655 SBAddPrefixes add_prefixes
;
656 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
657 ASSERT_EQ(2U, add_prefixes
.size());
658 EXPECT_EQ(kAddChunk1
, add_prefixes
[0].chunk_id
);
659 EXPECT_EQ(kHash1
.prefix
, add_prefixes
[0].prefix
);
660 EXPECT_EQ(kAddChunk1
, add_prefixes
[1].chunk_id
);
661 EXPECT_EQ(kHash2
.prefix
, add_prefixes
[1].prefix
);
663 std::vector
<SBAddFullHash
> add_hashes
;
664 EXPECT_TRUE(store_
->GetAddFullHashes(&add_hashes
));
665 ASSERT_EQ(1U, add_hashes
.size());
666 EXPECT_EQ(kAddChunk2
, add_hashes
[0].chunk_id
);
667 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_hashes
[0].full_hash
));
670 // Test that the database handles resharding correctly, both when growing and
672 TEST_F(SafeBrowsingStoreFileTest
, Resharding
) {
673 // Loop through multiple stride boundaries (1<<32, 1<<31, 1<<30, 1<<29).
674 const uint32 kTargetStride
= 1 << 29;
676 // Each chunk will require 8 bytes per prefix, plus 4 bytes for chunk
677 // information. It should be less than |kTargetFootprint| in the
678 // implementation, but high enough to keep the number of rewrites modest (to
679 // keep the test fast).
680 const size_t kPrefixesPerChunk
= 10000;
682 uint32 shard_stride
= 0;
685 // Add a series of chunks, tracking that the stride size changes in a
686 // direction appropriate to increasing file size.
688 ASSERT_TRUE(store_
->BeginUpdate());
690 EXPECT_TRUE(store_
->BeginChunk());
691 store_
->SetAddChunk(chunk_id
);
692 EXPECT_TRUE(store_
->CheckAddChunk(chunk_id
));
693 for (size_t i
= 0; i
< kPrefixesPerChunk
; ++i
) {
694 EXPECT_TRUE(store_
->WriteAddPrefix(chunk_id
, static_cast<SBPrefix
>(i
)));
696 EXPECT_TRUE(store_
->FinishChunk());
698 safe_browsing::PrefixSetBuilder builder
;
699 std::vector
<SBAddFullHash
> add_full_hashes_result
;
700 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
702 SBAddPrefixes add_prefixes
;
703 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
704 ASSERT_EQ(chunk_id
* kPrefixesPerChunk
, add_prefixes
.size());
706 // New stride should be the same, or shifted one right.
707 const uint32 new_shard_stride
= ReadStride();
708 EXPECT_TRUE((new_shard_stride
== shard_stride
) ||
709 ((new_shard_stride
<< 1) == shard_stride
));
710 shard_stride
= new_shard_stride
;
712 } while (!shard_stride
|| shard_stride
> kTargetStride
);
714 // Guard against writing too many chunks. If this gets too big, adjust
715 // |kPrefixesPerChunk|.
716 EXPECT_LT(chunk_id
, 20);
718 // Remove each chunk and check that the stride goes back to 0.
720 ASSERT_TRUE(store_
->BeginUpdate());
721 EXPECT_TRUE(store_
->CheckAddChunk(chunk_id
));
722 EXPECT_FALSE(store_
->CheckAddChunk(chunk_id
+ 1));
723 store_
->DeleteAddChunk(chunk_id
);
725 safe_browsing::PrefixSetBuilder builder
;
726 std::vector
<SBAddFullHash
> add_full_hashes_result
;
727 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
729 // New stride should be the same, or shifted one left.
730 const uint32 new_shard_stride
= ReadStride();
731 EXPECT_TRUE((new_shard_stride
== shard_stride
) ||
732 (new_shard_stride
== (shard_stride
<< 1)));
733 shard_stride
= new_shard_stride
;
735 EXPECT_EQ(0u, shard_stride
);
738 // Test that a golden v7 file can no longer be read. All platforms generating
739 // v7 files were little-endian, so there is no point to testing this transition
740 // if/when a big-endian port is added.
741 #if defined(ARCH_CPU_LITTLE_ENDIAN)
742 TEST_F(SafeBrowsingStoreFileTest
, Version7
) {
745 // Copy the golden file into temporary storage. The golden file contains:
746 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
747 // - Sub chunk kSubChunk1 containing kHash3.
748 const char kBasename
[] = "FileStoreVersion7";
749 base::FilePath golden_path
;
750 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &golden_path
));
751 golden_path
= golden_path
.AppendASCII("SafeBrowsing");
752 golden_path
= golden_path
.AppendASCII(kBasename
);
753 ASSERT_TRUE(base::CopyFile(golden_path
, filename_
));
755 // Reset the store to make sure it re-reads the file.
756 ASSERT_TRUE(!task_runner_
->HasPendingTask());
757 store_
.reset(new SafeBrowsingStoreFile(task_runner_
));
758 store_
->Init(filename_
,
759 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected
,
760 base::Unretained(this)));
761 EXPECT_FALSE(corruption_detected_
);
763 // The unknown version should be encountered on the first read.
764 EXPECT_FALSE(store_
->BeginUpdate());
765 EXPECT_TRUE(corruption_detected_
);
767 // No more to test because corrupt file stores are cleaned up by the database
772 // Test that a golden v8 file can be read by the current code. All platforms
773 // generating v8 files are little-endian, so there is no point to testing this
774 // transition if/when a big-endian port is added.
775 #if defined(ARCH_CPU_LITTLE_ENDIAN)
776 TEST_F(SafeBrowsingStoreFileTest
, Version8
) {
779 // Copy the golden file into temporary storage. The golden file contains:
780 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
781 // - Sub chunk kSubChunk1 containing kHash3.
782 const char kBasename
[] = "FileStoreVersion8";
783 base::FilePath golden_path
;
784 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &golden_path
));
785 golden_path
= golden_path
.AppendASCII("SafeBrowsing");
786 golden_path
= golden_path
.AppendASCII(kBasename
);
787 ASSERT_TRUE(base::CopyFile(golden_path
, filename_
));
789 // Reset the store to make sure it re-reads the file.
790 ASSERT_TRUE(!task_runner_
->HasPendingTask());
791 store_
.reset(new SafeBrowsingStoreFile(task_runner_
));
792 store_
->Init(filename_
,
793 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected
,
794 base::Unretained(this)));
796 // Check that the expected prefixes and hashes are in place.
797 SBAddPrefixes add_prefixes
;
798 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
799 ASSERT_EQ(2U, add_prefixes
.size());
800 EXPECT_EQ(kAddChunk1
, add_prefixes
[0].chunk_id
);
801 EXPECT_EQ(kHash1
.prefix
, add_prefixes
[0].prefix
);
802 EXPECT_EQ(kAddChunk1
, add_prefixes
[1].chunk_id
);
803 EXPECT_EQ(kHash2
.prefix
, add_prefixes
[1].prefix
);
805 std::vector
<SBAddFullHash
> add_hashes
;
806 EXPECT_TRUE(store_
->GetAddFullHashes(&add_hashes
));
807 ASSERT_EQ(1U, add_hashes
.size());
808 EXPECT_EQ(kAddChunk1
, add_hashes
[0].chunk_id
);
809 EXPECT_TRUE(SBFullHashEqual(kHash2
, add_hashes
[0].full_hash
));
811 // Attempt an update to make sure things work end-to-end.
812 EXPECT_TRUE(store_
->BeginUpdate());
814 // Still has the chunks expected in the next update.
815 std::vector
<int> chunks
;
816 store_
->GetAddChunks(&chunks
);
817 ASSERT_EQ(1U, chunks
.size());
818 EXPECT_EQ(kAddChunk1
, chunks
[0]);
820 store_
->GetSubChunks(&chunks
);
821 ASSERT_EQ(1U, chunks
.size());
822 EXPECT_EQ(kSubChunk1
, chunks
[0]);
824 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
825 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
827 // Sub chunk kAddChunk1 hash kHash2.
828 // NOTE(shess): Having full hashes and prefixes in the same chunk is no longer
829 // supported, though it was when this code was written.
830 store_
->SetSubChunk(kSubChunk2
);
831 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
832 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk1
, kHash2
.prefix
));
833 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk1
, kHash2
));
834 EXPECT_TRUE(store_
->FinishChunk());
837 safe_browsing::PrefixSetBuilder builder
;
838 std::vector
<SBAddFullHash
> add_full_hashes_result
;
839 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
841 // The sub'ed prefix and hash are gone.
842 std::vector
<SBPrefix
> prefixes_result
;
843 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
844 ASSERT_EQ(1U, prefixes_result
.size());
845 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
846 EXPECT_TRUE(add_full_hashes_result
.empty());
851 } // namespace safe_browsing