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