LCOV - code coverage report
Current view: top level - src/test/fuzz - addrman.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 6 203 3.0 %
Date: 2023-11-10 23:46:46 Functions: 3 31 9.7 %
Branches: 6 326 1.8 %

           Branch data     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         [ #  # ]:          0 :                                                                {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                 :          0 : 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