banman.cpp raw

   1  // Copyright (c) 2020-2022 The Limenka 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      SeedRandomStateForTest(SeedRand::ZEROS);
  46      FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
  47      SetMockTime(ConsumeTime(fuzzed_data_provider));
  48      fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist";
  49  
  50      const bool start_with_corrupted_banlist{fuzzed_data_provider.ConsumeBool()};
  51      bool force_read_and_write_to_err{false};
  52      if (start_with_corrupted_banlist) {
  53          assert(WriteBinaryFile(banlist_file + ".json",
  54                                 fuzzed_data_provider.ConsumeRandomLengthString()));
  55      } else {
  56          force_read_and_write_to_err = fuzzed_data_provider.ConsumeBool();
  57          if (force_read_and_write_to_err) {
  58              banlist_file = fs::path{"path"} / "to" / "inaccessible" / "fuzzed_banlist";
  59          }
  60      }
  61  
  62      {
  63          BanMan ban_man{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/ConsumeBanTimeOffset(fuzzed_data_provider)};
  64          // The complexity is O(N^2), where N is the input size, because each call
  65          // might call DumpBanlist (or other methods that are at least linear
  66          // complexity of the input size).
  67          bool contains_invalid{false};
  68          LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 300)
  69          {
  70              CallOneOf(
  71                  fuzzed_data_provider,
  72                  [&] {
  73                      CNetAddr net_addr{ConsumeNetAddr(fuzzed_data_provider)};
  74                      if (!net_addr.IsCJDNS() || !net_addr.IsValid()) {
  75                          const std::optional<CNetAddr>& addr{LookupHost(net_addr.ToStringAddr(), /*fAllowLookup=*/false)};
  76                          if (addr.has_value() && addr->IsValid()) {
  77                              net_addr = *addr;
  78                          } else {
  79                              contains_invalid = true;
  80                          }
  81                      }
  82                      auto ban_time_offset = ConsumeBanTimeOffset(fuzzed_data_provider);
  83                      auto since_unix_epoch = fuzzed_data_provider.ConsumeBool();
  84                      ban_man.Ban(net_addr, ban_time_offset, since_unix_epoch);
  85                  },
  86                  [&] {
  87                      CSubNet subnet{ConsumeSubNet(fuzzed_data_provider)};
  88                      subnet = LookupSubNet(subnet.ToString());
  89                      if (!subnet.IsValid()) {
  90                          contains_invalid = true;
  91                      }
  92                      auto ban_time_offset = ConsumeBanTimeOffset(fuzzed_data_provider);
  93                      auto since_unix_epoch = fuzzed_data_provider.ConsumeBool();
  94                      ban_man.Ban(subnet, ban_time_offset, since_unix_epoch);
  95                  },
  96                  [&] {
  97                      ban_man.ClearBanned();
  98                  },
  99                  [&] {
 100                      ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider));
 101                  },
 102                  [&] {
 103                      ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider));
 104                  },
 105                  [&] {
 106                      ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider));
 107                  },
 108                  [&] {
 109                      ban_man.Unban(ConsumeSubNet(fuzzed_data_provider));
 110                  },
 111                  [&] {
 112                      banmap_t banmap;
 113                      ban_man.GetBanned(banmap);
 114                  },
 115                  [&] {
 116                      ban_man.DumpBanlist();
 117                  },
 118                  [&] {
 119                      ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider));
 120                  });
 121          }
 122          if (!force_read_and_write_to_err) {
 123              ban_man.DumpBanlist();
 124              SetMockTime(ConsumeTime(fuzzed_data_provider));
 125              banmap_t banmap;
 126              ban_man.GetBanned(banmap);
 127              BanMan ban_man_read{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/0};
 128              banmap_t banmap_read;
 129              ban_man_read.GetBanned(banmap_read);
 130              if (!contains_invalid) {
 131                  assert(banmap == banmap_read);
 132              }
 133          }
 134      }
 135      fs::remove(fs::PathToString(banlist_file + ".json"));
 136  }
 137