1 // Copyright 2019 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 <stdint.h>
6 #include <memory>
7 #include <string>
8
9 #include "base/files/file_path.h"
10 #include "base/logging.h"
11 #include "base/test/test_simple_task_runner.h"
12 #include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
13 #include "components/safe_browsing/core/db/v4_store.h"
14 #include "components/safe_browsing/core/db/v4_test_util.h"
15
16 namespace safe_browsing {
17
18 const PrefixSize kMinHashPrefixLengthForFuzzing = kMinHashPrefixLength;
19 const PrefixSize kMaxHashPrefixLengthForFuzzing = 8;
20
21 class V4StoreFuzzer {
22 public:
FuzzMergeUpdate(const uint8_t * data,size_t size)23 static int FuzzMergeUpdate(const uint8_t* data, size_t size) {
24 // |prefix_map_old| represents the existing state of the |V4Store|.
25 HashPrefixMap prefix_map_old;
26 // |prefix_map_additions| represents the update being applied.
27 HashPrefixMap prefix_map_additions;
28
29 // Pass 1:
30 // Add a prefix_size->[prefixes] pair in |prefix_map_old|.
31 PopulateHashPrefixMap(&data, &size, &prefix_map_old);
32 // Add a prefix_size->[prefixes] pair in |prefix_map_additions|.
33 PopulateHashPrefixMap(&data, &size, &prefix_map_additions);
34
35 // Pass 2:
36 // Add a prefix_size->[prefixes] pair in |prefix_map_old|.
37 // If the prefix_size is the same as that added in |prefix_map_old| during
38 // Pass 1, the older list of prefixes is lost.
39 PopulateHashPrefixMap(&data, &size, &prefix_map_old);
40 // Add a prefix_size->[prefixes] pair in |prefix_map_additions|.
41 // If the prefix_size is the same as that added in |prefix_map_additions|
42 // during Pass 1, the older list of prefixes is lost.
43 PopulateHashPrefixMap(&data, &size, &prefix_map_additions);
44
45 auto store = std::make_unique<TestV4Store>(
46 base::MakeRefCounted<base::TestSimpleTaskRunner>(), base::FilePath());
47 // Assume no removals.
48 google::protobuf::RepeatedField<google::protobuf::int32> raw_removals;
49 // Empty checksum indicates that the checksum calculation should be skipped.
50 std::string empty_checksum;
51 store->MergeUpdate(prefix_map_old, prefix_map_additions, &raw_removals,
52 empty_checksum);
53 #ifndef NDEBUG
54 DisplayHashPrefixMapDetails(store->hash_prefix_map_);
55 #endif
56
57 return 0;
58 }
59
60 private:
61 // Add a prefix_size->[prefixes] pair in |hash_prefix_map|.
62 // Ensures that length of [prefixes] is a multiple of prefix_size.
63 // If the map already contains a pair with key prefix_size, the existing value
64 // is discarded.
65 // Here's a summary of how the input is parsed:
66 // * First uint8_t is the |prefix_size| to be added.
67 // * Next uint8_t is the length of the list of prefixes.
68 // * It is adjusted to be no greater than the remaining size of |data|.
69 // * It is called as |prefixes_list_size|.
70 // * Next |prefixes_list_size| bytes are added to |hash_prefix_map|
71 // as a list of prefixes of size |prefix_size|.
PopulateHashPrefixMap(const uint8_t ** data,size_t * size,HashPrefixMap * hash_prefix_map)72 static void PopulateHashPrefixMap(const uint8_t** data,
73 size_t* size,
74 HashPrefixMap* hash_prefix_map) {
75 uint8_t datum;
76 if (!GetDatum(data, size, &datum))
77 return;
78
79 // Prefix size is defined to be between |kMinHashPrefixLength| and
80 // |kMaxHashPrefixLength| but we are going to limit them to smaller sizes so
81 // that we have a higher chance of actually populating the
82 // |hash_prefix_map| for smaller inputs.
83 PrefixSize prefix_size = kMinHashPrefixLengthForFuzzing +
84 (datum % (kMaxHashPrefixLengthForFuzzing -
85 kMinHashPrefixLengthForFuzzing + 1));
86
87 if (!GetDatum(data, size, &datum))
88 return;
89 size_t prefixes_list_size = datum;
90 // This |prefixes_list_size| is the length of the list of prefixes to be
91 // added. It can't be larger than the remaining buffer.
92 if (*size < prefixes_list_size) {
93 prefixes_list_size = *size;
94 }
95 std::string prefixes(*data, *data + prefixes_list_size);
96 *size -= prefixes_list_size;
97 *data += prefixes_list_size;
98 V4Store::AddUnlumpedHashes(prefix_size, prefixes, hash_prefix_map);
99 #ifndef NDEBUG
100 DisplayHashPrefixMapDetails(*hash_prefix_map);
101 #endif
102 }
103
GetDatum(const uint8_t ** data,size_t * size,uint8_t * datum)104 static bool GetDatum(const uint8_t** data, size_t* size, uint8_t* datum) {
105 if (*size == 0)
106 return false;
107 *datum = *data[0];
108 (*data)++;
109 (*size)--;
110 return true;
111 }
112
DisplayHashPrefixMapDetails(const HashPrefixMap & hash_prefix_map)113 static void DisplayHashPrefixMapDetails(
114 const HashPrefixMap& hash_prefix_map) {
115 for (const auto& pair : hash_prefix_map) {
116 PrefixSize prefix_size = pair.first;
117 size_t prefixes_length = pair.second.length();
118 DVLOG(5) << __FUNCTION__ << " : " << prefix_size << " : "
119 << prefixes_length;
120 }
121 }
122 };
123
124 } // namespace safe_browsing
125
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)126 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
127 return safe_browsing::V4StoreFuzzer::FuzzMergeUpdate(data, size);
128 }
129