/ 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 <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                      ban_man.IsDiscouraged(ConsumeNetAddr(fuzzed_data_provider));
123                  });
124          }
125          if (!force_read_and_write_to_err) {
126              ban_man.DumpBanlist();
127              SetMockTime(ConsumeTime(fuzzed_data_provider));
128              banmap_t banmap;
129              ban_man.GetBanned(banmap);
130              BanMan ban_man_read{banlist_file, /*client_interface=*/nullptr, /*default_ban_time=*/0};
131              banmap_t banmap_read;
132              ban_man_read.GetBanned(banmap_read);
133              if (!contains_invalid) {
134                  assert(banmap == banmap_read);
135              }
136          }
137      }
138      fs::remove(fs::PathToString(banlist_file + ".json"));
139  }