banman.cpp
1 // Copyright (c) 2020-2022 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 <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 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; 46 SetMockTime(ConsumeTime(fuzzed_data_provider)); 47 fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist"; 48 49 const bool start_with_corrupted_banlist{fuzzed_data_provider.ConsumeBool()}; 50 bool force_read_and_write_to_err{false}; 51 if (start_with_corrupted_banlist) { 52 assert(WriteBinaryFile(banlist_file + ".json", 53 fuzzed_data_provider.ConsumeRandomLengthString())); 54 } else { 55 force_read_and_write_to_err = fuzzed_data_provider.ConsumeBool(); 56 if (force_read_and_write_to_err) { 57 banlist_file = fs::path{"path"} / "to" / "inaccessible" / "fuzzed_banlist"; 58 } 59 } 60 61 { 62 BanMan ban_man{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/ConsumeBanTimeOffset(fuzzed_data_provider)}; 63 // The complexity is O(N^2), where N is the input size, because each call 64 // might call DumpBanlist (or other methods that are at least linear 65 // complexity of the input size). 66 bool contains_invalid{false}; 67 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300) 68 { 69 CallOneOf( 70 fuzzed_data_provider, 71 [&] { 72 CNetAddr net_addr{ConsumeNetAddr(fuzzed_data_provider)}; 73 if (!net_addr.IsCJDNS() || !net_addr.IsValid()) { 74 const std::optional<CNetAddr>& addr{LookupHost(net_addr.ToStringAddr(), /*fAllowLookup=*/false)}; 75 if (addr.has_value() && addr->IsValid()) { 76 net_addr = *addr; 77 } else { 78 contains_invalid = true; 79 } 80 } 81 ban_man.Ban(net_addr, ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); 82 }, 83 [&] { 84 CSubNet subnet{ConsumeSubNet(fuzzed_data_provider)}; 85 subnet = LookupSubNet(subnet.ToString()); 86 if (!subnet.IsValid()) { 87 contains_invalid = true; 88 } 89 ban_man.Ban(subnet, ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); 90 }, 91 [&] { 92 ban_man.ClearBanned(); 93 }, 94 [&] { 95 ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider)); 96 }, 97 [&] { 98 ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider)); 99 }, 100 [&] { 101 ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider)); 102 }, 103 [&] { 104 ban_man.Unban(ConsumeSubNet(fuzzed_data_provider)); 105 }, 106 [&] { 107 banmap_t banmap; 108 ban_man.GetBanned(banmap); 109 }, 110 [&] { 111 ban_man.DumpBanlist(); 112 }, 113 [&] { 114 ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider)); 115 }); 116 } 117 if (!force_read_and_write_to_err) { 118 ban_man.DumpBanlist(); 119 SetMockTime(ConsumeTime(fuzzed_data_provider)); 120 banmap_t banmap; 121 ban_man.GetBanned(banmap); 122 BanMan ban_man_read{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/0}; 123 banmap_t banmap_read; 124 ban_man_read.GetBanned(banmap_read); 125 if (!contains_invalid) { 126 assert(banmap == banmap_read); 127 } 128 } 129 } 130 fs::remove(fs::PathToString(banlist_file + ".json")); 131 }