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 <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 ban_man.IsDiscouraged(ConsumeNetAddr(fuzzed_data_provider)); 123 }); 124 } 125 if (!force_read_and_write_to_err) { 126 ban_man.DumpBanlist(); 127 SetMockTime(ConsumeTime(fuzzed_data_provider)); 128 banmap_t banmap; 129 ban_man.GetBanned(banmap); 130 BanMan ban_man_read{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/0}; 131 banmap_t banmap_read; 132 ban_man_read.GetBanned(banmap_read); 133 if (!contains_invalid) { 134 assert(banmap == banmap_read); 135 } 136 } 137 } 138 fs::remove(fs::PathToString(banlist_file + ".json")); 139 }