/ src / test / fuzz / banman.cpp
banman.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 <banman.h>
  6  #include <common/args.h>
  7  #include <netaddress.h>
  8  #include <test/fuzz/FuzzedDataProvider.h>
  9  #include <test/fuzz/fuzz.h>
 10  #include <test/fuzz/util.h>
 11  #include <test/fuzz/util/net.h>
 12  #include <test/util/setup_common.h>
 13  #include <util/fs.h>
 14  #include <util/readwritefile.h>
 15  
 16  #include <cassert>
 17  #include <cstdint>
 18  #include <limits>
 19  #include <string>
 20  #include <vector>
 21  
 22  namespace {
 23  int64_t ConsumeBanTimeOffset(FuzzedDataProvider& fuzzed_data_provider) noexcept
 24  {
 25      // Avoid signed integer overflow by capping to int32_t max:
 26      // banman.cpp:137:73: runtime error: signed integer overflow: 1591700817 + 9223372036854775807 cannot be represented in type 'long'
 27      return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(std::numeric_limits<int64_t>::min(), std::numeric_limits<int32_t>::max());
 28  }
 29  } // namespace
 30  
 31  void initialize_banman()
 32  {
 33      static const auto testing_setup = MakeNoLogFileContext<>();
 34  }
 35  
 36  static bool operator==(const CBanEntry& lhs, const CBanEntry& rhs)
 37  {
 38      return lhs.nVersion == rhs.nVersion &&
 39             lhs.nCreateTime == rhs.nCreateTime &&
 40             lhs.nBanUntil == rhs.nBanUntil;
 41  }
 42  
 43  FUZZ_TARGET(banman, .init = initialize_banman)
 44  {
 45      FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
 46      SetMockTime(ConsumeTime(fuzzed_data_provider));
 47      fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist";
 48  
 49      const bool start_with_corrupted_banlist{fuzzed_data_provider.ConsumeBool()};
 50      bool force_read_and_write_to_err{false};
 51      if (start_with_corrupted_banlist) {
 52          assert(WriteBinaryFile(banlist_file + ".json",
 53                                 fuzzed_data_provider.ConsumeRandomLengthString()));
 54      } else {
 55          force_read_and_write_to_err = fuzzed_data_provider.ConsumeBool();
 56          if (force_read_and_write_to_err) {
 57              banlist_file = fs::path{"path"} / "to" / "inaccessible" / "fuzzed_banlist";
 58          }
 59      }
 60  
 61      {
 62          BanMan ban_man{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/ConsumeBanTimeOffset(fuzzed_data_provider)};
 63          // The complexity is O(N^2), where N is the input size, because each call
 64          // might call DumpBanlist (or other methods that are at least linear
 65          // complexity of the input size).
 66          bool contains_invalid{false};
 67          LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
 68          {
 69              CallOneOf(
 70                  fuzzed_data_provider,
 71                  [&] {
 72                      CNetAddr net_addr{ConsumeNetAddr(fuzzed_data_provider)};
 73                      if (!net_addr.IsCJDNS() || !net_addr.IsValid()) {
 74                          const std::optional<CNetAddr>& addr{LookupHost(net_addr.ToStringAddr(), /*fAllowLookup=*/false)};
 75                          if (addr.has_value() && addr->IsValid()) {
 76                              net_addr = *addr;
 77                          } else {
 78                              contains_invalid = true;
 79                          }
 80                      }
 81                      ban_man.Ban(net_addr, ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool());
 82                  },
 83                  [&] {
 84                      CSubNet subnet{ConsumeSubNet(fuzzed_data_provider)};
 85                      subnet = LookupSubNet(subnet.ToString());
 86                      if (!subnet.IsValid()) {
 87                          contains_invalid = true;
 88                      }
 89                      ban_man.Ban(subnet, ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool());
 90                  },
 91                  [&] {
 92                      ban_man.ClearBanned();
 93                  },
 94                  [&] {
 95                      ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider));
 96                  },
 97                  [&] {
 98                      ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider));
 99                  },
100                  [&] {
101                      ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider));
102                  },
103                  [&] {
104                      ban_man.Unban(ConsumeSubNet(fuzzed_data_provider));
105                  },
106                  [&] {
107                      banmap_t banmap;
108                      ban_man.GetBanned(banmap);
109                  },
110                  [&] {
111                      ban_man.DumpBanlist();
112                  },
113                  [&] {
114                      ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider));
115                  });
116          }
117          if (!force_read_and_write_to_err) {
118              ban_man.DumpBanlist();
119              SetMockTime(ConsumeTime(fuzzed_data_provider));
120              banmap_t banmap;
121              ban_man.GetBanned(banmap);
122              BanMan ban_man_read{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/0};
123              banmap_t banmap_read;
124              ban_man_read.GetBanned(banmap_read);
125              if (!contains_invalid) {
126                  assert(banmap == banmap_read);
127              }
128          }
129      }
130      fs::remove(fs::PathToString(banlist_file + ".json"));
131  }