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