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