banman.cpp raw

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