addrman.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 <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 <time.h> 18 #include <util/asmap.h> 19 #include <util/chaintype.h> 20 21 #include <cassert> 22 #include <cstdint> 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 [[nodiscard]] inline NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept 43 { 44 std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); 45 if (!SanityCheckASMap(asmap, 128)) asmap.clear(); 46 return NetGroupManager(asmap); 47 } 48 49 FUZZ_TARGET(data_stream_addr_man, .init = initialize_addrman) 50 { 51 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; 52 DataStream data_stream = ConsumeDataStream(fuzzed_data_provider); 53 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)}; 54 AddrMan addr_man(netgroupman, /*deterministic=*/false, GetCheckRatio()); 55 try { 56 ReadFromStream(addr_man, data_stream); 57 } catch (const std::exception&) { 58 } 59 } 60 61 /** 62 * Generate a random address. Always returns a valid address. 63 */ 64 CNetAddr RandAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext& fast_random_context) 65 { 66 CNetAddr addr; 67 assert(!addr.IsValid()); 68 for (size_t i = 0; i < 8 && !addr.IsValid(); ++i) { 69 if (fuzzed_data_provider.remaining_bytes() > 1 && fuzzed_data_provider.ConsumeBool()) { 70 addr = ConsumeNetAddr(fuzzed_data_provider); 71 } else { 72 addr = ConsumeNetAddr(fuzzed_data_provider, &fast_random_context); 73 } 74 } 75 76 // Return a dummy IPv4 5.5.5.5 if we generated an invalid address. 77 if (!addr.IsValid()) { 78 in_addr v4_addr = {}; 79 v4_addr.s_addr = 0x05050505; 80 addr = CNetAddr{v4_addr}; 81 } 82 83 return addr; 84 } 85 86 /** Fill addrman with lots of addresses from lots of sources. */ 87 void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider) 88 { 89 // Add a fraction of the addresses to the "tried" table. 90 // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33% 91 const size_t n = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3); 92 93 const size_t num_sources = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50); 94 CNetAddr prev_source; 95 // Generate a FastRandomContext seed to use inside the loops instead of 96 // fuzzed_data_provider. When fuzzed_data_provider is exhausted it 97 // just returns 0. 98 FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)}; 99 for (size_t i = 0; i < num_sources; ++i) { 100 const auto source = RandAddr(fuzzed_data_provider, fast_random_context); 101 const size_t num_addresses = fast_random_context.randrange(500) + 1; // [1..500] 102 103 for (size_t j = 0; j < num_addresses; ++j) { 104 const auto addr = CAddress{CService{RandAddr(fuzzed_data_provider, fast_random_context), 8333}, NODE_NETWORK}; 105 const std::chrono::seconds time_penalty{fast_random_context.randrange(100000001)}; 106 addrman.Add({addr}, source, time_penalty); 107 108 if (n > 0 && addrman.Size() % n == 0) { 109 addrman.Good(addr, Now<NodeSeconds>()); 110 } 111 112 // Add 10% of the addresses from more than one source. 113 if (fast_random_context.randrange(10) == 0 && prev_source.IsValid()) { 114 addrman.Add({addr}, prev_source, time_penalty); 115 } 116 } 117 prev_source = source; 118 } 119 } 120 121 class AddrManDeterministic : public AddrMan 122 { 123 public: 124 explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider) 125 : AddrMan(netgroupman, /*deterministic=*/true, GetCheckRatio()) 126 { 127 WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)}); 128 } 129 130 /** 131 * Compare with another AddrMan. 132 * This compares: 133 * - the values in `mapInfo` (the keys aka ids are ignored) 134 * - vvNew entries refer to the same addresses 135 * - vvTried entries refer to the same addresses 136 */ 137 bool operator==(const AddrManDeterministic& other) const 138 { 139 LOCK2(m_impl->cs, other.m_impl->cs); 140 141 if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew || 142 m_impl->nTried != other.m_impl->nTried) { 143 return false; 144 } 145 146 // Check that all values in `mapInfo` are equal to all values in `other.mapInfo`. 147 // Keys may be different. 148 149 auto addrinfo_hasher = [](const AddrInfo& a) { 150 CSipHasher hasher(0, 0); 151 auto addr_key = a.GetKey(); 152 auto source_key = a.source.GetAddrBytes(); 153 hasher.Write(TicksSinceEpoch<std::chrono::seconds>(a.m_last_success)); 154 hasher.Write(a.nAttempts); 155 hasher.Write(a.nRefCount); 156 hasher.Write(a.fInTried); 157 hasher.Write(a.GetNetwork()); 158 hasher.Write(a.source.GetNetwork()); 159 hasher.Write(addr_key.size()); 160 hasher.Write(source_key.size()); 161 hasher.Write(addr_key); 162 hasher.Write(source_key); 163 return (size_t)hasher.Finalize(); 164 }; 165 166 auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) { 167 return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.m_last_success, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) == 168 std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.m_last_success, rhs.nAttempts, rhs.nRefCount, rhs.fInTried); 169 }; 170 171 using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>; 172 173 const size_t num_addresses{m_impl->mapInfo.size()}; 174 175 Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq}; 176 for (const auto& [id, addr] : m_impl->mapInfo) { 177 addresses.insert(addr); 178 } 179 180 Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq}; 181 for (const auto& [id, addr] : other.m_impl->mapInfo) { 182 other_addresses.insert(addr); 183 } 184 185 if (addresses != other_addresses) { 186 return false; 187 } 188 189 auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) { 190 if (id == -1 && other_id == -1) { 191 return true; 192 } 193 if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) { 194 return false; 195 } 196 return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id); 197 }; 198 199 // Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]` 200 // contains just an id and the address is to be found in `mapInfo.at(id)`. The ids 201 // themselves may differ between `vvNew` and `other.vvNew`. 202 for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) { 203 for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) { 204 if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) { 205 return false; 206 } 207 } 208 } 209 210 // Same for `vvTried`. 211 for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) { 212 for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) { 213 if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) { 214 return false; 215 } 216 } 217 } 218 219 return true; 220 } 221 }; 222 223 FUZZ_TARGET(addrman, .init = initialize_addrman) 224 { 225 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 226 SetMockTime(ConsumeTime(fuzzed_data_provider)); 227 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)}; 228 auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider); 229 if (fuzzed_data_provider.ConsumeBool()) { 230 const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)}; 231 DataStream ds{serialized_data}; 232 try { 233 ds >> *addr_man_ptr; 234 } catch (const std::ios_base::failure&) { 235 addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider); 236 } 237 } 238 AddrManDeterministic& addr_man = *addr_man_ptr; 239 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { 240 CallOneOf( 241 fuzzed_data_provider, 242 [&] { 243 addr_man.ResolveCollisions(); 244 }, 245 [&] { 246 (void)addr_man.SelectTriedCollision(); 247 }, 248 [&] { 249 std::vector<CAddress> addresses; 250 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { 251 addresses.push_back(ConsumeAddress(fuzzed_data_provider)); 252 } 253 addr_man.Add(addresses, ConsumeNetAddr(fuzzed_data_provider), std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)}); 254 }, 255 [&] { 256 addr_man.Good(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); 257 }, 258 [&] { 259 addr_man.Attempt(ConsumeService(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); 260 }, 261 [&] { 262 addr_man.Connected(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); 263 }, 264 [&] { 265 addr_man.SetServices(ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)); 266 }); 267 } 268 const AddrMan& const_addr_man{addr_man}; 269 std::optional<Network> network; 270 if (fuzzed_data_provider.ConsumeBool()) { 271 network = fuzzed_data_provider.PickValueInArray(ALL_NETWORKS); 272 } 273 (void)const_addr_man.GetAddr( 274 /*max_addresses=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), 275 /*max_pct=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), 276 network, 277 /*filtered=*/fuzzed_data_provider.ConsumeBool()); 278 (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network); 279 std::optional<bool> in_new; 280 if (fuzzed_data_provider.ConsumeBool()) { 281 in_new = fuzzed_data_provider.ConsumeBool(); 282 } 283 (void)const_addr_man.Size(network, in_new); 284 DataStream data_stream{}; 285 data_stream << const_addr_man; 286 } 287 288 // Check that serialize followed by unserialize produces the same addrman. 289 FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman) 290 { 291 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 292 SetMockTime(ConsumeTime(fuzzed_data_provider)); 293 294 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)}; 295 AddrManDeterministic addr_man1{netgroupman, fuzzed_data_provider}; 296 AddrManDeterministic addr_man2{netgroupman, fuzzed_data_provider}; 297 298 DataStream data_stream{}; 299 300 FillAddrman(addr_man1, fuzzed_data_provider); 301 data_stream << addr_man1; 302 data_stream >> addr_man2; 303 assert(addr_man1 == addr_man2); 304 }