banman.cpp
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 NodeClockContext clock_ctx{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_ctx.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 }