addrman.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 <addrdb.h> 6 #include <addrman.h> 7 #include <addrman_impl.h> 8 #include <chainparams.h> 9 #include <common/args.h> 10 #include <merkleblock.h> 11 #include <random.h> 12 #include <test/fuzz/FuzzedDataProvider.h> 13 #include <test/fuzz/fuzz.h> 14 #include <test/fuzz/util.h> 15 #include <test/fuzz/util/net.h> 16 #include <test/util/setup_common.h> 17 #include <test/util/time.h> 18 #include <util/asmap.h> 19 #include <util/chaintype.h> 20 21 #include <cassert> 22 #include <cstdint> 23 #include <ctime> 24 #include <optional> 25 #include <string> 26 #include <vector> 27 28 namespace { 29 const BasicTestingSetup* g_setup; 30 31 int32_t GetCheckRatio() 32 { 33 return std::clamp<int32_t>(g_setup->m_node.args->GetIntArg("-checkaddrman", 0), 0, 1000000); 34 } 35 } // namespace 36 37 void initialize_addrman() 38 { 39 static const auto testing_setup = MakeNoLogFileContext<>(ChainType::REGTEST); 40 g_setup = testing_setup.get(); 41 } 42 43 FUZZ_TARGET(data_stream_addr_man, .init = initialize_addrman) 44 { 45 SeedRandomStateForTest(SeedRand::ZEROS); 46 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; 47 DataStream data_stream = ConsumeDataStream(fuzzed_data_provider); 48 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)}; 49 AddrMan addr_man(netgroupman, /*deterministic=*/false, GetCheckRatio()); 50 try { 51 ReadFromStream(addr_man, data_stream); 52 } catch (const std::exception&) { 53 } 54 } 55 56 /** 57 * Generate a random address. Always returns a valid address. 58 */ 59 CNetAddr RandAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext& fast_random_context) 60 { 61 CNetAddr addr; 62 assert(!addr.IsValid()); 63 for (size_t i = 0; i < 8 && !addr.IsValid(); ++i) { 64 if (fuzzed_data_provider.remaining_bytes() > 1 && fuzzed_data_provider.ConsumeBool()) { 65 addr = ConsumeNetAddr(fuzzed_data_provider); 66 } else { 67 addr = ConsumeNetAddr(fuzzed_data_provider, &fast_random_context); 68 } 69 } 70 71 // Return a dummy IPv4 5.5.5.5 if we generated an invalid address. 72 if (!addr.IsValid()) { 73 in_addr v4_addr = {}; 74 v4_addr.s_addr = 0x05050505; 75 addr = CNetAddr{v4_addr}; 76 } 77 78 return addr; 79 } 80 81 /** Fill addrman with lots of addresses from lots of sources. */ 82 void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider) 83 { 84 // Add a fraction of the addresses to the "tried" table. 85 // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33% 86 const size_t n = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3); 87 88 const size_t num_sources = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50); 89 CNetAddr prev_source; 90 // Generate a FastRandomContext seed to use inside the loops instead of 91 // fuzzed_data_provider. When fuzzed_data_provider is exhausted it 92 // just returns 0. 93 FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)}; 94 for (size_t i = 0; i < num_sources; ++i) { 95 const auto source = RandAddr(fuzzed_data_provider, fast_random_context); 96 const size_t num_addresses = fast_random_context.randrange(500) + 1; // [1..500] 97 98 for (size_t j = 0; j < num_addresses; ++j) { 99 const auto addr = CAddress{CService{RandAddr(fuzzed_data_provider, fast_random_context), 8333}, NODE_NETWORK}; 100 const std::chrono::seconds time_penalty{fast_random_context.randrange(100000001)}; 101 addrman.Add({addr}, source, time_penalty); 102 103 if (n > 0 && addrman.Size() % n == 0) { 104 addrman.Good(addr, Now<NodeSeconds>()); 105 } 106 107 // Add 10% of the addresses from more than one source. 108 if (fast_random_context.randrange(10) == 0 && prev_source.IsValid()) { 109 addrman.Add({addr}, prev_source, time_penalty); 110 } 111 } 112 prev_source = source; 113 } 114 } 115 116 FUZZ_TARGET(addrman, .init = initialize_addrman) 117 { 118 SeedRandomStateForTest(SeedRand::ZEROS); 119 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 120 NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider)}; 121 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)}; 122 auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio()); 123 if (fuzzed_data_provider.ConsumeBool()) { 124 const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; 125 DataStream ds{serialized_data}; 126 try { 127 ds >> *addr_man_ptr; 128 } catch (const std::ios_base::failure&) { 129 addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider, GetCheckRatio()); 130 } 131 } 132 AddrManDeterministic& addr_man = *addr_man_ptr; 133 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { 134 CallOneOf( 135 fuzzed_data_provider, 136 [&] { 137 addr_man.ResolveCollisions(); 138 }, 139 [&] { 140 (void)addr_man.SelectTriedCollision(); 141 }, 142 [&] { 143 std::vector<CAddress> addresses; 144 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { 145 addresses.push_back(ConsumeAddress(fuzzed_data_provider)); 146 } 147 auto net_addr = ConsumeNetAddr(fuzzed_data_provider); 148 auto time_penalty = ConsumeDuration<std::chrono::seconds>(fuzzed_data_provider, /*min=*/0s, /*max=*/100000000s); 149 addr_man.Add(addresses, net_addr, time_penalty); 150 }, 151 [&] { 152 auto addr = ConsumeService(fuzzed_data_provider); 153 auto time = ConsumeTime(fuzzed_data_provider); 154 addr_man.Good(addr, time); 155 }, 156 [&] { 157 auto addr = ConsumeService(fuzzed_data_provider); 158 auto count_failure = fuzzed_data_provider.ConsumeBool(); 159 auto time = ConsumeTime(fuzzed_data_provider); 160 addr_man.Attempt(addr, count_failure, time); 161 }, 162 [&] { 163 auto addr = ConsumeService(fuzzed_data_provider); 164 auto time = ConsumeTime(fuzzed_data_provider); 165 addr_man.Connected(addr, time); 166 }, 167 [&] { 168 auto addr = ConsumeService(fuzzed_data_provider); 169 auto n_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); 170 addr_man.SetServices(addr, n_services); 171 }); 172 } 173 const AddrMan& const_addr_man{addr_man}; 174 std::optional<Network> network; 175 if (fuzzed_data_provider.ConsumeBool()) { 176 network = fuzzed_data_provider.PickValueInArray(ALL_NETWORKS); 177 } 178 auto max_addresses = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096); 179 auto max_pct = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 100); 180 auto filtered = fuzzed_data_provider.ConsumeBool(); 181 (void)const_addr_man.GetAddr(max_addresses, max_pct, network, filtered); 182 183 std::unordered_set<Network> nets; 184 for (const auto& net : ALL_NETWORKS) { 185 if (fuzzed_data_provider.ConsumeBool()) { 186 nets.insert(net); 187 } 188 } 189 (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), nets); 190 191 std::optional<bool> in_new; 192 if (fuzzed_data_provider.ConsumeBool()) { 193 in_new = fuzzed_data_provider.ConsumeBool(); 194 } 195 (void)const_addr_man.Size(network, in_new); 196 DataStream data_stream{}; 197 data_stream << const_addr_man; 198 } 199 200 // Check that serialize followed by unserialize produces the same addrman. 201 FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman) 202 { 203 SeedRandomStateForTest(SeedRand::ZEROS); 204 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 205 NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider)}; 206 207 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)}; 208 AddrManDeterministic addr_man1{netgroupman, fuzzed_data_provider, GetCheckRatio()}; 209 AddrManDeterministic addr_man2{netgroupman, fuzzed_data_provider, GetCheckRatio()}; 210 211 DataStream data_stream{}; 212 213 FillAddrman(addr_man1, fuzzed_data_provider); 214 data_stream << addr_man1; 215 data_stream >> addr_man2; 216 assert(addr_man1 == addr_man2); 217 }