LCOV - code coverage report
Current view: top level - src/test/fuzz - addrman.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 7 201 3.5 %
Date: 2023-09-26 12:08:55 Functions: 10 32 31.2 %

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

Generated by: LCOV version 1.14