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/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 "chrome/common/chrome_paths.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "testing/platform_test.h"
19 const int kAddChunk1
= 1;
20 const int kAddChunk2
= 3;
21 const int kAddChunk3
= 5;
22 const int kAddChunk4
= 7;
23 // Disjoint chunk numbers for subs to flush out typos.
24 const int kSubChunk1
= 2;
25 const int kSubChunk2
= 4;
27 const SBFullHash kHash1
= SBFullHashForString("one");
28 const SBFullHash kHash2
= SBFullHashForString("two");
29 const SBFullHash kHash3
= SBFullHashForString("three");
30 const SBFullHash kHash4
= SBFullHashForString("four");
31 const SBFullHash kHash5
= SBFullHashForString("five");
32 const SBFullHash kHash6
= SBFullHashForString("six");
34 const SBPrefix kMinSBPrefix
= 0u;
35 const SBPrefix kMaxSBPrefix
= ~kMinSBPrefix
;
39 namespace safe_browsing
{
41 class SafeBrowsingStoreFileTest
: public PlatformTest
{
43 virtual void SetUp() {
44 PlatformTest::SetUp();
46 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
48 filename_
= temp_dir_
.path();
49 filename_
= filename_
.AppendASCII("SafeBrowsingTestStore");
51 store_
.reset(new SafeBrowsingStoreFile());
52 store_
->Init(filename_
,
53 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected
,
54 base::Unretained(this)));
55 corruption_detected_
= false;
57 virtual void TearDown() {
62 PlatformTest::TearDown();
65 void OnCorruptionDetected() {
66 corruption_detected_
= true;
69 // Populate the store with some testing data.
70 void PopulateStore() {
71 ASSERT_TRUE(store_
->BeginUpdate());
73 EXPECT_TRUE(store_
->BeginChunk());
74 store_
->SetAddChunk(kAddChunk1
);
75 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
76 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash1
.prefix
));
77 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash2
.prefix
));
78 EXPECT_TRUE(store_
->FinishChunk());
80 EXPECT_TRUE(store_
->BeginChunk());
81 store_
->SetSubChunk(kSubChunk1
);
82 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
83 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk3
, kHash3
.prefix
));
84 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk3
, kHash3
));
85 EXPECT_TRUE(store_
->FinishChunk());
87 EXPECT_TRUE(store_
->BeginChunk());
88 store_
->SetAddChunk(kAddChunk2
);
89 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk2
));
90 EXPECT_TRUE(store_
->WriteAddHash(kAddChunk2
, kHash4
));
91 EXPECT_TRUE(store_
->FinishChunk());
93 // Chunk numbers shouldn't leak over.
94 EXPECT_FALSE(store_
->CheckAddChunk(kSubChunk1
));
95 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk3
));
96 EXPECT_FALSE(store_
->CheckSubChunk(kAddChunk1
));
97 EXPECT_FALSE(store_
->CheckSubChunk(kAddChunk2
));
99 safe_browsing::PrefixSetBuilder builder
;
100 std::vector
<SBAddFullHash
> add_full_hashes_result
;
102 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
105 // Manually read the shard stride info from the file.
106 uint32
ReadStride() {
107 base::ScopedFILE
file(base::OpenFile(filename_
, "rb"));
108 const long kOffset
= 4 * sizeof(uint32
);
109 EXPECT_EQ(fseek(file
.get(), kOffset
, SEEK_SET
), 0);
110 uint32 shard_stride
= 0;
111 EXPECT_EQ(fread(&shard_stride
, sizeof(shard_stride
), 1, file
.get()), 1U);
115 base::ScopedTempDir temp_dir_
;
116 base::FilePath filename_
;
117 scoped_ptr
<SafeBrowsingStoreFile
> store_
;
118 bool corruption_detected_
;
121 // Test that the empty store looks empty.
122 TEST_F(SafeBrowsingStoreFileTest
, Empty
) {
123 ASSERT_TRUE(store_
->BeginUpdate());
125 std::vector
<int> chunks
;
126 store_
->GetAddChunks(&chunks
);
127 EXPECT_TRUE(chunks
.empty());
128 store_
->GetSubChunks(&chunks
);
129 EXPECT_TRUE(chunks
.empty());
131 // Shouldn't see anything, but anything is a big set to test.
132 EXPECT_FALSE(store_
->CheckAddChunk(0));
133 EXPECT_FALSE(store_
->CheckAddChunk(1));
134 EXPECT_FALSE(store_
->CheckAddChunk(-1));
136 EXPECT_FALSE(store_
->CheckSubChunk(0));
137 EXPECT_FALSE(store_
->CheckSubChunk(1));
138 EXPECT_FALSE(store_
->CheckSubChunk(-1));
140 safe_browsing::PrefixSetBuilder builder
;
141 std::vector
<SBAddFullHash
> add_full_hashes_result
;
143 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
144 EXPECT_TRUE(add_full_hashes_result
.empty());
146 std::vector
<SBPrefix
> prefixes_result
;
147 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
148 EXPECT_TRUE(prefixes_result
.empty());
151 // Write some prefix and hash data to the store, add more data in another
152 // transaction, then verify that the union of all the data is present.
153 TEST_F(SafeBrowsingStoreFileTest
, BasicStore
) {
156 ASSERT_TRUE(store_
->BeginUpdate());
158 std::vector
<int> chunks
;
159 store_
->GetAddChunks(&chunks
);
160 ASSERT_EQ(2U, chunks
.size());
161 EXPECT_EQ(kAddChunk1
, chunks
[0]);
162 EXPECT_EQ(kAddChunk2
, chunks
[1]);
164 store_
->GetSubChunks(&chunks
);
165 ASSERT_EQ(1U, chunks
.size());
166 EXPECT_EQ(kSubChunk1
, chunks
[0]);
168 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
169 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
171 EXPECT_TRUE(store_
->BeginChunk());
172 store_
->SetAddChunk(kAddChunk3
);
173 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kHash5
.prefix
));
174 EXPECT_TRUE(store_
->FinishChunk());
176 // Still has the chunks expected in the next update.
178 safe_browsing::PrefixSetBuilder builder
;
179 std::vector
<SBAddFullHash
> add_full_hashes_result
;
180 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
182 std::vector
<SBPrefix
> prefixes_result
;
183 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
184 ASSERT_EQ(3U, prefixes_result
.size());
185 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
186 EXPECT_EQ(kHash5
.prefix
, prefixes_result
[1]);
187 EXPECT_EQ(kHash2
.prefix
, prefixes_result
[2]);
189 ASSERT_EQ(1U, add_full_hashes_result
.size());
190 EXPECT_EQ(kAddChunk2
, add_full_hashes_result
[0].chunk_id
);
191 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_full_hashes_result
[0].full_hash
));
195 // Verify that the min and max prefixes are stored and operated on.
196 TEST_F(SafeBrowsingStoreFileTest
, PrefixMinMax
) {
199 ASSERT_TRUE(store_
->BeginUpdate());
201 EXPECT_TRUE(store_
->BeginChunk());
202 store_
->SetAddChunk(kAddChunk3
);
203 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kMinSBPrefix
));
204 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kMaxSBPrefix
));
205 EXPECT_TRUE(store_
->FinishChunk());
208 safe_browsing::PrefixSetBuilder builder
;
209 std::vector
<SBAddFullHash
> add_full_hashes_result
;
210 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
212 std::vector
<SBPrefix
> prefixes_result
;
213 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
214 ASSERT_EQ(4U, prefixes_result
.size());
215 EXPECT_EQ(kMinSBPrefix
, prefixes_result
[0]);
216 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[1]);
217 EXPECT_EQ(kHash2
.prefix
, prefixes_result
[2]);
218 EXPECT_EQ(kMaxSBPrefix
, prefixes_result
[3]);
221 ASSERT_TRUE(store_
->BeginUpdate());
223 EXPECT_TRUE(store_
->BeginChunk());
224 store_
->SetAddChunk(kSubChunk2
);
225 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk2
, kAddChunk3
, kMinSBPrefix
));
226 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk2
, kAddChunk3
, kMaxSBPrefix
));
227 EXPECT_TRUE(store_
->FinishChunk());
230 safe_browsing::PrefixSetBuilder builder
;
231 std::vector
<SBAddFullHash
> add_full_hashes_result
;
232 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
234 std::vector
<SBPrefix
> prefixes_result
;
235 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
236 ASSERT_EQ(2U, prefixes_result
.size());
237 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
238 EXPECT_EQ(kHash2
.prefix
, prefixes_result
[1]);
242 // Test that subs knockout adds.
243 TEST_F(SafeBrowsingStoreFileTest
, SubKnockout
) {
244 ASSERT_TRUE(store_
->BeginUpdate());
246 EXPECT_TRUE(store_
->BeginChunk());
247 store_
->SetAddChunk(kAddChunk1
);
248 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash1
.prefix
));
249 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash2
.prefix
));
250 EXPECT_TRUE(store_
->FinishChunk());
252 EXPECT_TRUE(store_
->BeginChunk());
253 store_
->SetAddChunk(kAddChunk2
);
254 EXPECT_TRUE(store_
->WriteAddHash(kAddChunk2
, kHash4
));
255 EXPECT_TRUE(store_
->FinishChunk());
257 EXPECT_TRUE(store_
->BeginChunk());
258 store_
->SetSubChunk(kSubChunk1
);
259 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk3
, kHash3
.prefix
));
260 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk1
, kHash2
.prefix
));
261 EXPECT_TRUE(store_
->FinishChunk());
264 safe_browsing::PrefixSetBuilder builder
;
265 std::vector
<SBAddFullHash
> add_full_hashes_result
;
266 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
268 // Knocked out the chunk expected.
269 std::vector
<SBPrefix
> prefixes_result
;
270 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
271 ASSERT_EQ(1U, prefixes_result
.size());
272 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
274 ASSERT_EQ(1U, add_full_hashes_result
.size());
275 EXPECT_EQ(kAddChunk2
, add_full_hashes_result
[0].chunk_id
);
276 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_full_hashes_result
[0].full_hash
));
279 ASSERT_TRUE(store_
->BeginUpdate());
281 // This add should be knocked out by an existing sub.
282 EXPECT_TRUE(store_
->BeginChunk());
283 store_
->SetAddChunk(kAddChunk3
);
284 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kHash3
.prefix
));
285 EXPECT_TRUE(store_
->FinishChunk());
288 safe_browsing::PrefixSetBuilder builder
;
289 std::vector
<SBAddFullHash
> add_full_hashes_result
;
290 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
292 std::vector
<SBPrefix
> prefixes_result
;
293 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
294 ASSERT_EQ(1U, prefixes_result
.size());
295 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
297 ASSERT_EQ(1U, add_full_hashes_result
.size());
298 EXPECT_EQ(kAddChunk2
, add_full_hashes_result
[0].chunk_id
);
299 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_full_hashes_result
[0].full_hash
));
302 ASSERT_TRUE(store_
->BeginUpdate());
304 // But by here the sub should be gone, so it should stick this time.
305 EXPECT_TRUE(store_
->BeginChunk());
306 store_
->SetAddChunk(kAddChunk3
);
307 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk3
, kHash3
.prefix
));
308 EXPECT_TRUE(store_
->FinishChunk());
311 safe_browsing::PrefixSetBuilder builder
;
312 std::vector
<SBAddFullHash
> add_full_hashes_result
;
313 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
315 std::vector
<SBPrefix
> prefixes_result
;
316 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
317 ASSERT_EQ(2U, prefixes_result
.size());
318 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
319 EXPECT_EQ(kHash3
.prefix
, prefixes_result
[1]);
321 ASSERT_EQ(1U, add_full_hashes_result
.size());
322 EXPECT_EQ(kAddChunk2
, add_full_hashes_result
[0].chunk_id
);
323 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_full_hashes_result
[0].full_hash
));
327 // Test that deletes delete the chunk's data.
328 TEST_F(SafeBrowsingStoreFileTest
, DeleteChunks
) {
329 ASSERT_TRUE(store_
->BeginUpdate());
331 // A prefix chunk which will be deleted.
332 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk1
));
333 store_
->SetAddChunk(kAddChunk1
);
334 EXPECT_TRUE(store_
->BeginChunk());
335 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash1
.prefix
));
336 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash2
.prefix
));
337 EXPECT_TRUE(store_
->FinishChunk());
339 // A prefix chunk which won't be deleted.
340 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk2
));
341 store_
->SetAddChunk(kAddChunk2
);
342 EXPECT_TRUE(store_
->BeginChunk());
343 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk2
, kHash3
.prefix
));
344 EXPECT_TRUE(store_
->FinishChunk());
346 // A full-hash chunk which won't be deleted.
347 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk3
));
348 store_
->SetAddChunk(kAddChunk3
);
349 EXPECT_TRUE(store_
->BeginChunk());
350 EXPECT_TRUE(store_
->WriteAddHash(kAddChunk3
, kHash6
));
351 EXPECT_TRUE(store_
->FinishChunk());
353 // A sub chunk to delete.
354 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk1
));
355 store_
->SetSubChunk(kSubChunk1
);
356 EXPECT_TRUE(store_
->BeginChunk());
357 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk3
, kHash4
));
358 EXPECT_TRUE(store_
->FinishChunk());
360 // A sub chunk to keep.
361 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk2
));
362 store_
->SetSubChunk(kSubChunk2
);
363 EXPECT_TRUE(store_
->BeginChunk());
364 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk2
, kAddChunk4
, kHash5
));
365 EXPECT_TRUE(store_
->FinishChunk());
367 store_
->DeleteAddChunk(kAddChunk1
);
368 store_
->DeleteSubChunk(kSubChunk1
);
370 // Not actually deleted until finish.
371 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
372 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk2
));
373 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk3
));
374 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
375 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk2
));
378 safe_browsing::PrefixSetBuilder builder
;
379 std::vector
<SBAddFullHash
> add_full_hashes_result
;
380 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
382 std::vector
<SBPrefix
> prefixes_result
;
383 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
384 ASSERT_EQ(1U, prefixes_result
.size());
385 EXPECT_EQ(kHash3
.prefix
, prefixes_result
[0]);
387 ASSERT_EQ(1U, add_full_hashes_result
.size());
388 EXPECT_EQ(kAddChunk3
, add_full_hashes_result
[0].chunk_id
);
389 EXPECT_TRUE(SBFullHashEqual(kHash6
, add_full_hashes_result
[0].full_hash
));
392 // Expected chunks are there in another update.
393 ASSERT_TRUE(store_
->BeginUpdate());
394 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk1
));
395 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk2
));
396 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk3
));
397 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk1
));
398 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk2
));
401 store_
->DeleteAddChunk(kAddChunk2
);
402 store_
->DeleteAddChunk(kAddChunk3
);
403 store_
->DeleteSubChunk(kSubChunk2
);
406 safe_browsing::PrefixSetBuilder builder
;
407 std::vector
<SBAddFullHash
> add_full_hashes_result
;
408 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
410 std::vector
<SBPrefix
> prefixes_result
;
411 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
412 EXPECT_TRUE(prefixes_result
.empty());
413 EXPECT_TRUE(add_full_hashes_result
.empty());
416 // Expect no more chunks.
417 ASSERT_TRUE(store_
->BeginUpdate());
418 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk1
));
419 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk2
));
420 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk3
));
421 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk1
));
422 EXPECT_FALSE(store_
->CheckSubChunk(kSubChunk2
));
425 safe_browsing::PrefixSetBuilder builder
;
426 std::vector
<SBAddFullHash
> add_full_hashes_result
;
427 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
429 std::vector
<SBPrefix
> prefixes_result
;
430 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
431 EXPECT_TRUE(prefixes_result
.empty());
432 EXPECT_TRUE(add_full_hashes_result
.empty());
436 // Test that deleting the store deletes the store.
437 TEST_F(SafeBrowsingStoreFileTest
, Delete
) {
438 // Delete should work if the file wasn't there in the first place.
439 EXPECT_FALSE(base::PathExists(filename_
));
440 EXPECT_TRUE(store_
->Delete());
442 // Create a store file.
445 EXPECT_TRUE(base::PathExists(filename_
));
446 EXPECT_TRUE(store_
->Delete());
447 EXPECT_FALSE(base::PathExists(filename_
));
450 // Test that Delete() deletes the temporary store, if present.
451 TEST_F(SafeBrowsingStoreFileTest
, DeleteTemp
) {
452 const base::FilePath temp_file
=
453 SafeBrowsingStoreFile::TemporaryFileForFilename(filename_
);
455 EXPECT_FALSE(base::PathExists(filename_
));
456 EXPECT_FALSE(base::PathExists(temp_file
));
458 // Starting a transaction creates a temporary file.
459 ASSERT_TRUE(store_
->BeginUpdate());
460 EXPECT_TRUE(base::PathExists(temp_file
));
462 // Pull the rug out from under the existing store, simulating a
464 store_
.reset(new SafeBrowsingStoreFile());
465 store_
->Init(filename_
, base::Closure());
466 EXPECT_FALSE(base::PathExists(filename_
));
467 EXPECT_TRUE(base::PathExists(temp_file
));
469 // Make sure the temporary file is deleted.
470 EXPECT_TRUE(store_
->Delete());
471 EXPECT_FALSE(base::PathExists(filename_
));
472 EXPECT_FALSE(base::PathExists(temp_file
));
475 // Test basic corruption-handling.
476 TEST_F(SafeBrowsingStoreFileTest
, DetectsCorruption
) {
477 // Load a store with some data.
480 // Can successfully open and read the store.
482 std::vector
<SBPrefix
> orig_prefixes
;
483 std::vector
<SBAddFullHash
> orig_hashes
;
484 safe_browsing::PrefixSetBuilder builder
;
485 ASSERT_TRUE(store_
->BeginUpdate());
486 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &orig_hashes
));
487 builder
.GetPrefixSetNoHashes()->GetPrefixes(&orig_prefixes
);
488 EXPECT_GT(orig_prefixes
.size(), 0U);
489 EXPECT_GT(orig_hashes
.size(), 0U);
490 EXPECT_FALSE(corruption_detected_
);
493 // Corrupt the store.
494 base::ScopedFILE
file(base::OpenFile(filename_
, "rb+"));
495 const long kOffset
= 60;
496 EXPECT_EQ(fseek(file
.get(), kOffset
, SEEK_SET
), 0);
497 const uint32 kZero
= 0;
498 uint32 previous
= kZero
;
499 EXPECT_EQ(fread(&previous
, sizeof(previous
), 1, file
.get()), 1U);
500 EXPECT_NE(previous
, kZero
);
501 EXPECT_EQ(fseek(file
.get(), kOffset
, SEEK_SET
), 0);
502 EXPECT_EQ(fwrite(&kZero
, sizeof(kZero
), 1, file
.get()), 1U);
505 // Update fails and corruption callback is called.
506 std::vector
<SBAddFullHash
> add_hashes
;
507 corruption_detected_
= false;
509 safe_browsing::PrefixSetBuilder builder
;
510 ASSERT_TRUE(store_
->BeginUpdate());
511 EXPECT_FALSE(store_
->FinishUpdate(&builder
, &add_hashes
));
512 EXPECT_TRUE(corruption_detected_
);
515 // Make it look like there is a lot of add-chunks-seen data.
516 const long kAddChunkCountOffset
= 2 * sizeof(int32
);
517 const int32 kLargeCount
= 1000 * 1000 * 1000;
518 file
.reset(base::OpenFile(filename_
, "rb+"));
519 EXPECT_EQ(fseek(file
.get(), kAddChunkCountOffset
, SEEK_SET
), 0);
520 EXPECT_EQ(fwrite(&kLargeCount
, sizeof(kLargeCount
), 1, file
.get()), 1U);
523 // Detects corruption and fails to even begin the update.
524 corruption_detected_
= false;
525 EXPECT_FALSE(store_
->BeginUpdate());
526 EXPECT_TRUE(corruption_detected_
);
529 TEST_F(SafeBrowsingStoreFileTest
, CheckValidity
) {
530 // Empty store is valid.
531 EXPECT_FALSE(base::PathExists(filename_
));
532 ASSERT_TRUE(store_
->BeginUpdate());
533 EXPECT_FALSE(corruption_detected_
);
534 EXPECT_TRUE(store_
->CheckValidity());
535 EXPECT_FALSE(corruption_detected_
);
536 EXPECT_TRUE(store_
->CancelUpdate());
538 // A store with some data is valid.
539 EXPECT_FALSE(base::PathExists(filename_
));
541 EXPECT_TRUE(base::PathExists(filename_
));
542 ASSERT_TRUE(store_
->BeginUpdate());
543 EXPECT_FALSE(corruption_detected_
);
544 EXPECT_TRUE(store_
->CheckValidity());
545 EXPECT_FALSE(corruption_detected_
);
546 EXPECT_TRUE(store_
->CancelUpdate());
549 // Corrupt the header.
550 TEST_F(SafeBrowsingStoreFileTest
, CheckValidityHeader
) {
552 EXPECT_TRUE(base::PathExists(filename_
));
554 // 37 is the most random prime number. It's also past the initial header
555 // struct, somewhere in the chunk list.
556 const size_t kOffset
= 37;
559 base::ScopedFILE
file(base::OpenFile(filename_
, "rb+"));
560 EXPECT_EQ(0, fseek(file
.get(), kOffset
, SEEK_SET
));
561 EXPECT_GE(fputs("hello", file
.get()), 0);
563 ASSERT_FALSE(store_
->BeginUpdate());
564 EXPECT_TRUE(corruption_detected_
);
567 // Corrupt the prefix payload.
568 TEST_F(SafeBrowsingStoreFileTest
, CheckValidityPayload
) {
570 EXPECT_TRUE(base::PathExists(filename_
));
572 // 137 is the second most random prime number. It's also past the header and
573 // chunk-id area. Corrupting the header would fail BeginUpdate() in which
574 // case CheckValidity() cannot be called.
575 const size_t kOffset
= 137;
578 base::ScopedFILE
file(base::OpenFile(filename_
, "rb+"));
579 EXPECT_EQ(0, fseek(file
.get(), kOffset
, SEEK_SET
));
580 EXPECT_GE(fputs("hello", file
.get()), 0);
582 ASSERT_TRUE(store_
->BeginUpdate());
583 EXPECT_FALSE(corruption_detected_
);
584 EXPECT_FALSE(store_
->CheckValidity());
585 EXPECT_TRUE(corruption_detected_
);
586 EXPECT_TRUE(store_
->CancelUpdate());
589 // Corrupt the checksum.
590 TEST_F(SafeBrowsingStoreFileTest
, CheckValidityChecksum
) {
592 EXPECT_TRUE(base::PathExists(filename_
));
594 // An offset from the end of the file which is in the checksum.
595 const int kOffset
= -static_cast<int>(sizeof(base::MD5Digest
));
598 base::ScopedFILE
file(base::OpenFile(filename_
, "rb+"));
599 EXPECT_EQ(0, fseek(file
.get(), kOffset
, SEEK_END
));
600 EXPECT_GE(fputs("hello", file
.get()), 0);
602 ASSERT_TRUE(store_
->BeginUpdate());
603 EXPECT_FALSE(corruption_detected_
);
604 EXPECT_FALSE(store_
->CheckValidity());
605 EXPECT_TRUE(corruption_detected_
);
606 EXPECT_TRUE(store_
->CancelUpdate());
609 TEST_F(SafeBrowsingStoreFileTest
, GetAddPrefixesAndHashes
) {
610 ASSERT_TRUE(store_
->BeginUpdate());
612 EXPECT_TRUE(store_
->BeginChunk());
613 store_
->SetAddChunk(kAddChunk1
);
614 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
615 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash1
.prefix
));
616 EXPECT_TRUE(store_
->WriteAddPrefix(kAddChunk1
, kHash2
.prefix
));
617 EXPECT_TRUE(store_
->FinishChunk());
619 EXPECT_TRUE(store_
->BeginChunk());
620 store_
->SetAddChunk(kAddChunk2
);
621 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk2
));
622 EXPECT_TRUE(store_
->WriteAddHash(kAddChunk2
, kHash4
));
623 EXPECT_TRUE(store_
->FinishChunk());
625 store_
->SetSubChunk(kSubChunk1
);
626 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
627 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk3
, kHash3
.prefix
));
628 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk3
, kHash3
));
629 EXPECT_TRUE(store_
->FinishChunk());
631 // Chunk numbers shouldn't leak over.
632 EXPECT_FALSE(store_
->CheckAddChunk(kSubChunk1
));
633 EXPECT_FALSE(store_
->CheckAddChunk(kAddChunk3
));
634 EXPECT_FALSE(store_
->CheckSubChunk(kAddChunk1
));
636 std::vector
<int> chunks
;
637 store_
->GetAddChunks(&chunks
);
638 ASSERT_EQ(2U, chunks
.size());
639 EXPECT_EQ(kAddChunk1
, chunks
[0]);
640 EXPECT_EQ(kAddChunk2
, chunks
[1]);
642 store_
->GetSubChunks(&chunks
);
643 ASSERT_EQ(1U, chunks
.size());
644 EXPECT_EQ(kSubChunk1
, chunks
[0]);
646 safe_browsing::PrefixSetBuilder builder
;
647 std::vector
<SBAddFullHash
> add_full_hashes_result
;
648 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
650 SBAddPrefixes add_prefixes
;
651 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
652 ASSERT_EQ(2U, add_prefixes
.size());
653 EXPECT_EQ(kAddChunk1
, add_prefixes
[0].chunk_id
);
654 EXPECT_EQ(kHash1
.prefix
, add_prefixes
[0].prefix
);
655 EXPECT_EQ(kAddChunk1
, add_prefixes
[1].chunk_id
);
656 EXPECT_EQ(kHash2
.prefix
, add_prefixes
[1].prefix
);
658 std::vector
<SBAddFullHash
> add_hashes
;
659 EXPECT_TRUE(store_
->GetAddFullHashes(&add_hashes
));
660 ASSERT_EQ(1U, add_hashes
.size());
661 EXPECT_EQ(kAddChunk2
, add_hashes
[0].chunk_id
);
662 EXPECT_TRUE(SBFullHashEqual(kHash4
, add_hashes
[0].full_hash
));
665 // Test that the database handles resharding correctly, both when growing and
667 TEST_F(SafeBrowsingStoreFileTest
, Resharding
) {
668 // Loop through multiple stride boundaries (1<<32, 1<<31, 1<<30, 1<<29).
669 const uint32 kTargetStride
= 1 << 29;
671 // Each chunk will require 8 bytes per prefix, plus 4 bytes for chunk
672 // information. It should be less than |kTargetFootprint| in the
673 // implementation, but high enough to keep the number of rewrites modest (to
674 // keep the test fast).
675 const size_t kPrefixesPerChunk
= 10000;
677 uint32 shard_stride
= 0;
680 // Add a series of chunks, tracking that the stride size changes in a
681 // direction appropriate to increasing file size.
683 ASSERT_TRUE(store_
->BeginUpdate());
685 EXPECT_TRUE(store_
->BeginChunk());
686 store_
->SetAddChunk(chunk_id
);
687 EXPECT_TRUE(store_
->CheckAddChunk(chunk_id
));
688 for (size_t i
= 0; i
< kPrefixesPerChunk
; ++i
) {
689 EXPECT_TRUE(store_
->WriteAddPrefix(chunk_id
, static_cast<SBPrefix
>(i
)));
691 EXPECT_TRUE(store_
->FinishChunk());
693 safe_browsing::PrefixSetBuilder builder
;
694 std::vector
<SBAddFullHash
> add_full_hashes_result
;
695 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
697 SBAddPrefixes add_prefixes
;
698 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
699 ASSERT_EQ(chunk_id
* kPrefixesPerChunk
, add_prefixes
.size());
701 // New stride should be the same, or shifted one right.
702 const uint32 new_shard_stride
= ReadStride();
703 EXPECT_TRUE((new_shard_stride
== shard_stride
) ||
704 ((new_shard_stride
<< 1) == shard_stride
));
705 shard_stride
= new_shard_stride
;
707 } while (!shard_stride
|| shard_stride
> kTargetStride
);
709 // Guard against writing too many chunks. If this gets too big, adjust
710 // |kPrefixesPerChunk|.
711 EXPECT_LT(chunk_id
, 20);
713 // Remove each chunk and check that the stride goes back to 0.
715 ASSERT_TRUE(store_
->BeginUpdate());
716 EXPECT_TRUE(store_
->CheckAddChunk(chunk_id
));
717 EXPECT_FALSE(store_
->CheckAddChunk(chunk_id
+ 1));
718 store_
->DeleteAddChunk(chunk_id
);
720 safe_browsing::PrefixSetBuilder builder
;
721 std::vector
<SBAddFullHash
> add_full_hashes_result
;
722 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
724 // New stride should be the same, or shifted one left.
725 const uint32 new_shard_stride
= ReadStride();
726 EXPECT_TRUE((new_shard_stride
== shard_stride
) ||
727 (new_shard_stride
== (shard_stride
<< 1)));
728 shard_stride
= new_shard_stride
;
730 EXPECT_EQ(0u, shard_stride
);
733 // Test that a golden v7 file can be read by the current code. All platforms
734 // generating v7 files are little-endian, so there is no point to testing this
735 // transition if/when a big-endian port is added.
736 #if defined(ARCH_CPU_LITTLE_ENDIAN)
737 TEST_F(SafeBrowsingStoreFileTest
, Version7
) {
740 // Copy the golden file into temporary storage. The golden file contains:
741 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
742 // - Sub chunk kSubChunk1 containing kHash3.
743 const char kBasename
[] = "FileStoreVersion7";
744 base::FilePath golden_path
;
745 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &golden_path
));
746 golden_path
= golden_path
.AppendASCII("SafeBrowsing");
747 golden_path
= golden_path
.AppendASCII(kBasename
);
748 ASSERT_TRUE(base::CopyFile(golden_path
, filename_
));
750 // Reset the store to make sure it re-reads the file.
751 store_
.reset(new SafeBrowsingStoreFile());
752 store_
->Init(filename_
,
753 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected
,
754 base::Unretained(this)));
756 // Check that the expected prefixes and hashes are in place.
757 SBAddPrefixes add_prefixes
;
758 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
759 ASSERT_EQ(2U, add_prefixes
.size());
760 EXPECT_EQ(kAddChunk1
, add_prefixes
[0].chunk_id
);
761 EXPECT_EQ(kHash1
.prefix
, add_prefixes
[0].prefix
);
762 EXPECT_EQ(kAddChunk1
, add_prefixes
[1].chunk_id
);
763 EXPECT_EQ(kHash2
.prefix
, add_prefixes
[1].prefix
);
765 std::vector
<SBAddFullHash
> add_hashes
;
766 EXPECT_TRUE(store_
->GetAddFullHashes(&add_hashes
));
767 ASSERT_EQ(1U, add_hashes
.size());
768 EXPECT_EQ(kAddChunk1
, add_hashes
[0].chunk_id
);
769 EXPECT_TRUE(SBFullHashEqual(kHash2
, add_hashes
[0].full_hash
));
771 // Attempt an update to make sure things work end-to-end.
772 EXPECT_TRUE(store_
->BeginUpdate());
774 // Still has the chunks expected in the next update.
775 std::vector
<int> chunks
;
776 store_
->GetAddChunks(&chunks
);
777 ASSERT_EQ(1U, chunks
.size());
778 EXPECT_EQ(kAddChunk1
, chunks
[0]);
780 store_
->GetSubChunks(&chunks
);
781 ASSERT_EQ(1U, chunks
.size());
782 EXPECT_EQ(kSubChunk1
, chunks
[0]);
784 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
785 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
787 // Sub chunk kAddChunk1 hash kHash2.
788 store_
->SetSubChunk(kSubChunk2
);
789 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
790 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk1
, kHash2
.prefix
));
791 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk1
, kHash2
));
792 EXPECT_TRUE(store_
->FinishChunk());
795 safe_browsing::PrefixSetBuilder builder
;
796 std::vector
<SBAddFullHash
> add_full_hashes_result
;
797 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
799 // The sub'ed prefix and hash are gone.
800 std::vector
<SBPrefix
> prefixes_result
;
801 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
802 ASSERT_EQ(1U, prefixes_result
.size());
803 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
804 EXPECT_TRUE(add_full_hashes_result
.empty());
809 // Test that a golden v8 file can be read by the current code. All platforms
810 // generating v8 files are little-endian, so there is no point to testing this
811 // transition if/when a big-endian port is added.
812 #if defined(ARCH_CPU_LITTLE_ENDIAN)
813 TEST_F(SafeBrowsingStoreFileTest
, Version8
) {
816 // Copy the golden file into temporary storage. The golden file contains:
817 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
818 // - Sub chunk kSubChunk1 containing kHash3.
819 const char kBasename
[] = "FileStoreVersion8";
820 base::FilePath golden_path
;
821 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &golden_path
));
822 golden_path
= golden_path
.AppendASCII("SafeBrowsing");
823 golden_path
= golden_path
.AppendASCII(kBasename
);
824 ASSERT_TRUE(base::CopyFile(golden_path
, filename_
));
826 // Reset the store to make sure it re-reads the file.
827 store_
.reset(new SafeBrowsingStoreFile());
828 store_
->Init(filename_
,
829 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected
,
830 base::Unretained(this)));
832 // Check that the expected prefixes and hashes are in place.
833 SBAddPrefixes add_prefixes
;
834 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
835 ASSERT_EQ(2U, add_prefixes
.size());
836 EXPECT_EQ(kAddChunk1
, add_prefixes
[0].chunk_id
);
837 EXPECT_EQ(kHash1
.prefix
, add_prefixes
[0].prefix
);
838 EXPECT_EQ(kAddChunk1
, add_prefixes
[1].chunk_id
);
839 EXPECT_EQ(kHash2
.prefix
, add_prefixes
[1].prefix
);
841 std::vector
<SBAddFullHash
> add_hashes
;
842 EXPECT_TRUE(store_
->GetAddFullHashes(&add_hashes
));
843 ASSERT_EQ(1U, add_hashes
.size());
844 EXPECT_EQ(kAddChunk1
, add_hashes
[0].chunk_id
);
845 EXPECT_TRUE(SBFullHashEqual(kHash2
, add_hashes
[0].full_hash
));
847 // Attempt an update to make sure things work end-to-end.
848 EXPECT_TRUE(store_
->BeginUpdate());
850 // Still has the chunks expected in the next update.
851 std::vector
<int> chunks
;
852 store_
->GetAddChunks(&chunks
);
853 ASSERT_EQ(1U, chunks
.size());
854 EXPECT_EQ(kAddChunk1
, chunks
[0]);
856 store_
->GetSubChunks(&chunks
);
857 ASSERT_EQ(1U, chunks
.size());
858 EXPECT_EQ(kSubChunk1
, chunks
[0]);
860 EXPECT_TRUE(store_
->CheckAddChunk(kAddChunk1
));
861 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
863 // Sub chunk kAddChunk1 hash kHash2.
864 store_
->SetSubChunk(kSubChunk2
);
865 EXPECT_TRUE(store_
->CheckSubChunk(kSubChunk1
));
866 EXPECT_TRUE(store_
->WriteSubPrefix(kSubChunk1
, kAddChunk1
, kHash2
.prefix
));
867 EXPECT_TRUE(store_
->WriteSubHash(kSubChunk1
, kAddChunk1
, kHash2
));
868 EXPECT_TRUE(store_
->FinishChunk());
871 safe_browsing::PrefixSetBuilder builder
;
872 std::vector
<SBAddFullHash
> add_full_hashes_result
;
873 EXPECT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
875 // The sub'ed prefix and hash are gone.
876 std::vector
<SBPrefix
> prefixes_result
;
877 builder
.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result
);
878 ASSERT_EQ(1U, prefixes_result
.size());
879 EXPECT_EQ(kHash1
.prefix
, prefixes_result
[0]);
880 EXPECT_TRUE(add_full_hashes_result
.empty());
885 // Test that when the v8 golden file is updated, the add prefix injected from
886 // the full hash is removed. All platforms generating v8 files are
887 // little-endian, so there is no point to testing this transition if/when a
888 // big-endian port is added.
889 #if defined(ARCH_CPU_LITTLE_ENDIAN)
890 TEST_F(SafeBrowsingStoreFileTest
, KnockoutPrefixVolunteers
) {
893 // Copy the golden file into temporary storage. The golden file contains:
894 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
895 // - Sub chunk kSubChunk1 containing kHash3.
896 const char kBasename
[] = "FileStoreVersion8";
897 base::FilePath golden_path
;
898 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA
, &golden_path
));
899 golden_path
= golden_path
.AppendASCII("SafeBrowsing");
900 golden_path
= golden_path
.AppendASCII(kBasename
);
901 ASSERT_TRUE(base::CopyFile(golden_path
, filename_
));
903 // Reset the store to make sure it re-reads the file.
904 store_
.reset(new SafeBrowsingStoreFile());
905 store_
->Init(filename_
,
906 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected
,
907 base::Unretained(this)));
909 // Check that the expected prefixes and hashes are in place.
911 SBAddPrefixes add_prefixes
;
912 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
913 ASSERT_EQ(2U, add_prefixes
.size());
914 EXPECT_EQ(kAddChunk1
, add_prefixes
[0].chunk_id
);
915 EXPECT_EQ(kHash1
.prefix
, add_prefixes
[0].prefix
);
916 EXPECT_EQ(kAddChunk1
, add_prefixes
[1].chunk_id
);
917 EXPECT_EQ(kHash2
.prefix
, add_prefixes
[1].prefix
);
919 std::vector
<SBAddFullHash
> add_hashes
;
920 EXPECT_TRUE(store_
->GetAddFullHashes(&add_hashes
));
921 ASSERT_EQ(1U, add_hashes
.size());
922 EXPECT_EQ(kAddChunk1
, add_hashes
[0].chunk_id
);
923 EXPECT_TRUE(SBFullHashEqual(kHash2
, add_hashes
[0].full_hash
));
928 EXPECT_TRUE(store_
->BeginUpdate());
930 safe_browsing::PrefixSetBuilder builder
;
931 std::vector
<SBAddFullHash
> add_full_hashes_result
;
932 ASSERT_TRUE(store_
->FinishUpdate(&builder
, &add_full_hashes_result
));
935 // Reset the store to make sure it re-reads the file.
936 store_
.reset(new SafeBrowsingStoreFile());
937 store_
->Init(filename_
,
938 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected
,
939 base::Unretained(this)));
941 // |kHash2.prefix| should have dropped.
943 SBAddPrefixes add_prefixes
;
944 EXPECT_TRUE(store_
->GetAddPrefixes(&add_prefixes
));
945 ASSERT_EQ(1U, add_prefixes
.size());
946 EXPECT_EQ(kAddChunk1
, add_prefixes
[0].chunk_id
);
947 EXPECT_EQ(kHash1
.prefix
, add_prefixes
[0].prefix
);
949 std::vector
<SBAddFullHash
> add_hashes
;
950 EXPECT_TRUE(store_
->GetAddFullHashes(&add_hashes
));
951 ASSERT_EQ(1U, add_hashes
.size());
952 EXPECT_EQ(kAddChunk1
, add_hashes
[0].chunk_id
);
953 EXPECT_TRUE(SBFullHashEqual(kHash2
, add_hashes
[0].full_hash
));
958 } // namespace safe_browsing