/ src / test / fuzz / banman.cpp
banman.cpp
  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      NodeClockContext clock_ctx{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_ctx.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  }