banman.cpp raw
1 // Copyright (c) 2020-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <banman.h>
6 #include <common/args.h>
7 #include <netaddress.h>
8 #include <test/fuzz/FuzzedDataProvider.h>
9 #include <test/fuzz/fuzz.h>
10 #include <test/fuzz/util.h>
11 #include <test/fuzz/util/net.h>
12 #include <test/util/setup_common.h>
13 #include <test/util/time.h>
14 #include <util/fs.h>
15 #include <util/readwritefile.h>
16
17 #include <cassert>
18 #include <cstdint>
19 #include <limits>
20 #include <string>
21 #include <vector>
22
23 namespace {
24 int64_t ConsumeBanTimeOffset(FuzzedDataProvider& fuzzed_data_provider) noexcept
25 {
26 // Avoid signed integer overflow by capping to int32_t max:
27 // banman.cpp:137:73: runtime error: signed integer overflow: 1591700817 + 9223372036854775807 cannot be represented in type 'long'
28 return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(std::numeric_limits<int64_t>::min(), std::numeric_limits<int32_t>::max());
29 }
30 } // namespace
31
32 void initialize_banman()
33 {
34 static const auto testing_setup = MakeNoLogFileContext<>();
35 }
36
37 static bool operator==(const CBanEntry& lhs, const CBanEntry& rhs)
38 {
39 return lhs.nVersion == rhs.nVersion &&
40 lhs.nCreateTime == rhs.nCreateTime &&
41 lhs.nBanUntil == rhs.nBanUntil;
42 }
43
44 FUZZ_TARGET(banman, .init = initialize_banman)
45 {
46 SeedRandomStateForTest(SeedRand::ZEROS);
47 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
48 FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
49 fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist";
50
51 const bool start_with_corrupted_banlist{fuzzed_data_provider.ConsumeBool()};
52 bool force_read_and_write_to_err{false};
53 if (start_with_corrupted_banlist) {
54 assert(WriteBinaryFile(banlist_file + ".json",
55 fuzzed_data_provider.ConsumeRandomLengthString()));
56 } else {
57 force_read_and_write_to_err = fuzzed_data_provider.ConsumeBool();
58 if (force_read_and_write_to_err) {
59 banlist_file = fs::path{"path"} / "to" / "inaccessible" / "fuzzed_banlist";
60 }
61 }
62
63 {
64 BanMan ban_man{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/ConsumeBanTimeOffset(fuzzed_data_provider)};
65 // The complexity is O(N^2), where N is the input size, because each call
66 // might call DumpBanlist (or other methods that are at least linear
67 // complexity of the input size).
68 bool contains_invalid{false};
69 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
70 {
71 CallOneOf(
72 fuzzed_data_provider,
73 [&] {
74 CNetAddr net_addr{ConsumeNetAddr(fuzzed_data_provider)};
75 if (!net_addr.IsCJDNS() || !net_addr.IsValid()) {
76 const std::optional<CNetAddr>& addr{LookupHost(net_addr.ToStringAddr(), /*fAllowLookup=*/false)};
77 if (addr.has_value() && addr->IsValid()) {
78 net_addr = *addr;
79 } else {
80 contains_invalid = true;
81 }
82 }
83 auto ban_time_offset = ConsumeBanTimeOffset(fuzzed_data_provider);
84 auto since_unix_epoch = fuzzed_data_provider.ConsumeBool();
85 ban_man.Ban(net_addr, ban_time_offset, since_unix_epoch);
86 },
87 [&] {
88 CSubNet subnet{ConsumeSubNet(fuzzed_data_provider)};
89 subnet = LookupSubNet(subnet.ToString());
90 if (!subnet.IsValid()) {
91 contains_invalid = true;
92 }
93 auto ban_time_offset = ConsumeBanTimeOffset(fuzzed_data_provider);
94 auto since_unix_epoch = fuzzed_data_provider.ConsumeBool();
95 ban_man.Ban(subnet, ban_time_offset, since_unix_epoch);
96 },
97 [&] {
98 ban_man.ClearBanned();
99 },
100 [&] {
101 ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider));
102 },
103 [&] {
104 ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider));
105 },
106 [&] {
107 ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider));
108 },
109 [&] {
110 ban_man.Unban(ConsumeSubNet(fuzzed_data_provider));
111 },
112 [&] {
113 banmap_t banmap;
114 ban_man.GetBanned(banmap);
115 },
116 [&] {
117 ban_man.DumpBanlist();
118 },
119 [&] {
120 ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider));
121 },
122 [&] {
123 ban_man.IsDiscouraged(ConsumeNetAddr(fuzzed_data_provider));
124 });
125 }
126 if (!force_read_and_write_to_err) {
127 ban_man.DumpBanlist();
128 clock.set(ConsumeTime(fuzzed_data_provider));
129 banmap_t banmap;
130 ban_man.GetBanned(banmap);
131 BanMan ban_man_read{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/0};
132 banmap_t banmap_read;
133 ban_man_read.GetBanned(banmap_read);
134 if (!contains_invalid) {
135 assert(banmap == banmap_read);
136 }
137 }
138 }
139 fs::remove(fs::PathToString(banlist_file + ".json"));
140 }
141