net.cpp raw

   1  // Copyright (c) 2009-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 <test/fuzz/util/net.h>
   6  
   7  #include <compat/compat.h>
   8  #include <netaddress.h>
   9  #include <node/protocol_version.h>
  10  #include <protocol.h>
  11  #include <test/fuzz/FuzzedDataProvider.h>
  12  #include <test/fuzz/util.h>
  13  #include <test/util/net.h>
  14  #include <util/sock.h>
  15  #include <util/time.h>
  16  
  17  #include <array>
  18  #include <cassert>
  19  #include <cerrno>
  20  #include <cstdint>
  21  #include <cstdlib>
  22  #include <cstring>
  23  #include <thread>
  24  #include <vector>
  25  
  26  class CNode;
  27  
  28  CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext* rand) noexcept
  29  {
  30      struct NetAux {
  31          Network net;
  32          CNetAddr::BIP155Network bip155;
  33          size_t len;
  34      };
  35  
  36      static constexpr std::array<NetAux, 6> nets{
  37          NetAux{.net = Network::NET_IPV4, .bip155 = CNetAddr::BIP155Network::IPV4, .len = ADDR_IPV4_SIZE},
  38          NetAux{.net = Network::NET_IPV6, .bip155 = CNetAddr::BIP155Network::IPV6, .len = ADDR_IPV6_SIZE},
  39          NetAux{.net = Network::NET_ONION, .bip155 = CNetAddr::BIP155Network::TORV3, .len = ADDR_TORV3_SIZE},
  40          NetAux{.net = Network::NET_I2P, .bip155 = CNetAddr::BIP155Network::I2P, .len = ADDR_I2P_SIZE},
  41          NetAux{.net = Network::NET_CJDNS, .bip155 = CNetAddr::BIP155Network::CJDNS, .len = ADDR_CJDNS_SIZE},
  42          NetAux{.net = Network::NET_INTERNAL, .bip155 = CNetAddr::BIP155Network{0}, .len = 0},
  43      };
  44  
  45      const size_t nets_index{rand == nullptr
  46          ? fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, nets.size() - 1)
  47          : static_cast<size_t>(rand->randrange(nets.size()))};
  48  
  49      const auto& aux = nets[nets_index];
  50  
  51      CNetAddr addr;
  52  
  53      if (aux.net == Network::NET_INTERNAL) {
  54          if (rand == nullptr) {
  55              addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
  56          } else {
  57              const auto v = rand->randbytes(32);
  58              addr.SetInternal(std::string{v.begin(), v.end()});
  59          }
  60          return addr;
  61      }
  62  
  63      DataStream s;
  64  
  65      s << static_cast<uint8_t>(aux.bip155);
  66  
  67      std::vector<uint8_t> addr_bytes;
  68      if (rand == nullptr) {
  69          addr_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(aux.len);
  70          addr_bytes.resize(aux.len);
  71      } else {
  72          addr_bytes = rand->randbytes(aux.len);
  73      }
  74      if (aux.net == NET_IPV6 && addr_bytes[0] == CJDNS_PREFIX) { // Avoid generating IPv6 addresses that look like CJDNS.
  75          addr_bytes[0] = 0x55; // Just an arbitrary number, anything != CJDNS_PREFIX would do.
  76      }
  77      if (aux.net == NET_CJDNS) { // Avoid generating CJDNS addresses that don't start with CJDNS_PREFIX because those are !IsValid().
  78          addr_bytes[0] = CJDNS_PREFIX;
  79      }
  80      s << addr_bytes;
  81  
  82      s >> CAddress::V2_NETWORK(addr);
  83  
  84      return addr;
  85  }
  86  
  87  CAddress ConsumeAddress(FuzzedDataProvider& fuzzed_data_provider) noexcept
  88  {
  89      return {ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS), NodeSeconds{std::chrono::seconds{fuzzed_data_provider.ConsumeIntegral<uint32_t>()}}};
  90  }
  91  
  92  template <typename P>
  93  P ConsumeDeserializationParams(FuzzedDataProvider& fuzzed_data_provider) noexcept
  94  {
  95      constexpr std::array ADDR_ENCODINGS{
  96          CNetAddr::Encoding::V1,
  97          CNetAddr::Encoding::V2,
  98      };
  99      constexpr std::array ADDR_FORMATS{
 100          CAddress::Format::Disk,
 101          CAddress::Format::Network,
 102      };
 103      if constexpr (std::is_same_v<P, CNetAddr::SerParams>) {
 104          return P{PickValue(fuzzed_data_provider, ADDR_ENCODINGS)};
 105      }
 106      if constexpr (std::is_same_v<P, CAddress::SerParams>) {
 107          return P{{PickValue(fuzzed_data_provider, ADDR_ENCODINGS)}, PickValue(fuzzed_data_provider, ADDR_FORMATS)};
 108      }
 109  }
 110  template CNetAddr::SerParams ConsumeDeserializationParams(FuzzedDataProvider&) noexcept;
 111  template CAddress::SerParams ConsumeDeserializationParams(FuzzedDataProvider&) noexcept;
 112  
 113  FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
 114      : Sock{fuzzed_data_provider.ConsumeIntegralInRange<SOCKET>(INVALID_SOCKET - 1, INVALID_SOCKET)},
 115        m_fuzzed_data_provider{fuzzed_data_provider},
 116        m_selectable{fuzzed_data_provider.ConsumeBool()},
 117        m_time{MockableSteadyClock::INITIAL_MOCK_TIME}
 118  {
 119      ElapseTime(std::chrono::seconds(0)); // start mocking the steady clock.
 120  }
 121  
 122  FuzzedSock::~FuzzedSock()
 123  {
 124      // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
 125      // close(m_socket) if m_socket is not INVALID_SOCKET.
 126      // Avoid closing an arbitrary file descriptor (m_socket is just a random very high number which
 127      // theoretically may concide with a real opened file descriptor).
 128      m_socket = INVALID_SOCKET;
 129  }
 130  
 131  void FuzzedSock::ElapseTime(std::chrono::milliseconds duration) const
 132  {
 133      m_time += duration;
 134      MockableSteadyClock::SetMockTime(m_time);
 135  }
 136  
 137  FuzzedSock& FuzzedSock::operator=(Sock&& other)
 138  {
 139      assert(false && "Move of Sock into FuzzedSock not allowed.");
 140      return *this;
 141  }
 142  
 143  ssize_t FuzzedSock::Send(const void* data, size_t len, int flags) const
 144  {
 145      constexpr std::array send_errnos{
 146          EACCES,
 147          EAGAIN,
 148          EALREADY,
 149          EBADF,
 150          ECONNRESET,
 151          EDESTADDRREQ,
 152          EFAULT,
 153          EINTR,
 154          EINVAL,
 155          EISCONN,
 156          EMSGSIZE,
 157          ENOBUFS,
 158          ENOMEM,
 159          ENOTCONN,
 160          ENOTSOCK,
 161          EOPNOTSUPP,
 162          EPIPE,
 163          EWOULDBLOCK,
 164      };
 165      if (m_fuzzed_data_provider.ConsumeBool()) {
 166          return len;
 167      }
 168      const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
 169      if (r == -1) {
 170          SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
 171      }
 172      return r;
 173  }
 174  
 175  ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const
 176  {
 177      // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
 178      // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
 179      // returning -1 and setting errno to EAGAIN repeatedly.
 180      constexpr std::array recv_errnos{
 181          ECONNREFUSED,
 182          EAGAIN,
 183          EBADF,
 184          EFAULT,
 185          EINTR,
 186          EINVAL,
 187          ENOMEM,
 188          ENOTCONN,
 189          ENOTSOCK,
 190          EWOULDBLOCK,
 191      };
 192      assert(buf != nullptr || len == 0);
 193  
 194      // Do the latency before any of the "return" statements.
 195      if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
 196          std::this_thread::sleep_for(std::chrono::milliseconds{2});
 197      }
 198  
 199      if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
 200          const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
 201          if (r == -1) {
 202              SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
 203          }
 204          return r;
 205      }
 206  
 207      size_t copied_so_far{0};
 208  
 209      if (!m_peek_data.empty()) {
 210          // `MSG_PEEK` was used in the preceding `Recv()` call, copy the first bytes from `m_peek_data`.
 211          const size_t copy_len{std::min(len, m_peek_data.size())};
 212          std::memcpy(buf, m_peek_data.data(), copy_len);
 213          copied_so_far += copy_len;
 214          if ((flags & MSG_PEEK) == 0) {
 215              m_peek_data.erase(m_peek_data.begin(), m_peek_data.begin() + copy_len);
 216          }
 217      }
 218  
 219      if (copied_so_far == len) {
 220          return copied_so_far;
 221      }
 222  
 223      auto new_data = ConsumeRandomLengthByteVector(m_fuzzed_data_provider, len - copied_so_far);
 224      if (new_data.empty()) return copied_so_far;
 225  
 226      std::memcpy(reinterpret_cast<uint8_t*>(buf) + copied_so_far, new_data.data(), new_data.size());
 227      copied_so_far += new_data.size();
 228  
 229      if ((flags & MSG_PEEK) != 0) {
 230          m_peek_data.insert(m_peek_data.end(), new_data.begin(), new_data.end());
 231      }
 232  
 233      if (copied_so_far == len || m_fuzzed_data_provider.ConsumeBool()) {
 234          return copied_so_far;
 235      }
 236  
 237      // Pad to len bytes.
 238      std::memset(reinterpret_cast<uint8_t*>(buf) + copied_so_far, 0x0, len - copied_so_far);
 239  
 240      return len;
 241  }
 242  
 243  int FuzzedSock::Connect(const sockaddr*, socklen_t) const
 244  {
 245      // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
 246      // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
 247      // returning -1 and setting errno to EAGAIN repeatedly.
 248      constexpr std::array connect_errnos{
 249          ECONNREFUSED,
 250          EAGAIN,
 251          ECONNRESET,
 252          EHOSTUNREACH,
 253          EINPROGRESS,
 254          EINTR,
 255          ENETUNREACH,
 256          ETIMEDOUT,
 257      };
 258      if (m_fuzzed_data_provider.ConsumeBool()) {
 259          SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
 260          return -1;
 261      }
 262      return 0;
 263  }
 264  
 265  int FuzzedSock::Bind(const sockaddr*, socklen_t) const
 266  {
 267      // Have a permanent error at bind_errnos[0] because when the fuzzed data is exhausted
 268      // SetFuzzedErrNo() will always set the global errno to bind_errnos[0]. We want to
 269      // avoid this method returning -1 and setting errno to a temporary error (like EAGAIN)
 270      // repeatedly because proper code should retry on temporary errors, leading to an
 271      // infinite loop.
 272      constexpr std::array bind_errnos{
 273          EACCES,
 274          EADDRINUSE,
 275          EADDRNOTAVAIL,
 276          EAGAIN,
 277      };
 278      if (m_fuzzed_data_provider.ConsumeBool()) {
 279          SetFuzzedErrNo(m_fuzzed_data_provider, bind_errnos);
 280          return -1;
 281      }
 282      return 0;
 283  }
 284  
 285  int FuzzedSock::Listen(int) const
 286  {
 287      // Have a permanent error at listen_errnos[0] because when the fuzzed data is exhausted
 288      // SetFuzzedErrNo() will always set the global errno to listen_errnos[0]. We want to
 289      // avoid this method returning -1 and setting errno to a temporary error (like EAGAIN)
 290      // repeatedly because proper code should retry on temporary errors, leading to an
 291      // infinite loop.
 292      constexpr std::array listen_errnos{
 293          EADDRINUSE,
 294          EINVAL,
 295          EOPNOTSUPP,
 296      };
 297      if (m_fuzzed_data_provider.ConsumeBool()) {
 298          SetFuzzedErrNo(m_fuzzed_data_provider, listen_errnos);
 299          return -1;
 300      }
 301      return 0;
 302  }
 303  
 304  std::unique_ptr<Sock> FuzzedSock::Accept(sockaddr* addr, socklen_t* addr_len) const
 305  {
 306      constexpr std::array accept_errnos{
 307          ECONNABORTED,
 308          EINTR,
 309          ENOMEM,
 310      };
 311      if (m_fuzzed_data_provider.ConsumeBool()) {
 312          SetFuzzedErrNo(m_fuzzed_data_provider, accept_errnos);
 313          return std::unique_ptr<FuzzedSock>();
 314      }
 315      return std::make_unique<FuzzedSock>(m_fuzzed_data_provider);
 316  }
 317  
 318  int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
 319  {
 320      constexpr std::array getsockopt_errnos{
 321          ENOMEM,
 322          ENOBUFS,
 323      };
 324      if (m_fuzzed_data_provider.ConsumeBool()) {
 325          SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
 326          return -1;
 327      }
 328      if (opt_val == nullptr) {
 329          return 0;
 330      }
 331      std::memcpy(opt_val,
 332                  ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
 333                  *opt_len);
 334      return 0;
 335  }
 336  
 337  int FuzzedSock::SetSockOpt(int, int, const void*, socklen_t) const
 338  {
 339      constexpr std::array setsockopt_errnos{
 340          ENOMEM,
 341          ENOBUFS,
 342      };
 343      if (m_fuzzed_data_provider.ConsumeBool()) {
 344          SetFuzzedErrNo(m_fuzzed_data_provider, setsockopt_errnos);
 345          return -1;
 346      }
 347      return 0;
 348  }
 349  
 350  int FuzzedSock::GetSockName(sockaddr* name, socklen_t* name_len) const
 351  {
 352      constexpr std::array getsockname_errnos{
 353          ECONNRESET,
 354          ENOBUFS,
 355      };
 356      if (m_fuzzed_data_provider.ConsumeBool()) {
 357          SetFuzzedErrNo(m_fuzzed_data_provider, getsockname_errnos);
 358          return -1;
 359      }
 360      assert(name_len);
 361      const auto bytes{ConsumeRandomLengthByteVector(m_fuzzed_data_provider, *name_len)};
 362      if (bytes.size() < (int)sizeof(sockaddr)) return -1;
 363      std::memcpy(name, bytes.data(), bytes.size());
 364      *name_len = bytes.size();
 365      return 0;
 366  }
 367  
 368  bool FuzzedSock::SetNonBlocking() const
 369  {
 370      constexpr std::array setnonblocking_errnos{
 371          EBADF,
 372          EPERM,
 373      };
 374      if (m_fuzzed_data_provider.ConsumeBool()) {
 375          SetFuzzedErrNo(m_fuzzed_data_provider, setnonblocking_errnos);
 376          return false;
 377      }
 378      return true;
 379  }
 380  
 381  bool FuzzedSock::IsSelectable() const
 382  {
 383      return m_selectable;
 384  }
 385  
 386  bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
 387  {
 388      constexpr std::array wait_errnos{
 389          EBADF,
 390          EINTR,
 391          EINVAL,
 392      };
 393      if (m_fuzzed_data_provider.ConsumeBool()) {
 394          SetFuzzedErrNo(m_fuzzed_data_provider, wait_errnos);
 395          return false;
 396      }
 397      if (occurred != nullptr) {
 398          // We simulate the requested event as occurred when ConsumeBool()
 399          // returns false. This avoids simulating endless waiting if the
 400          // FuzzedDataProvider runs out of data.
 401          *occurred = m_fuzzed_data_provider.ConsumeBool() ? 0 : requested;
 402      }
 403      ElapseTime(timeout);
 404      return true;
 405  }
 406  
 407  bool FuzzedSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const
 408  {
 409      for (auto& [sock, events] : events_per_sock) {
 410          (void)sock;
 411          // We simulate the requested event as occurred when ConsumeBool()
 412          // returns false. This avoids simulating endless waiting if the
 413          // FuzzedDataProvider runs out of data.
 414          events.occurred = m_fuzzed_data_provider.ConsumeBool() ? 0 : events.requested;
 415      }
 416      ElapseTime(timeout);
 417      return true;
 418  }
 419  
 420  bool FuzzedSock::IsConnected(std::string& errmsg) const
 421  {
 422      if (m_fuzzed_data_provider.ConsumeBool()) {
 423          return true;
 424      }
 425      errmsg = "disconnected at random by the fuzzer";
 426      return false;
 427  }
 428  
 429  void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept
 430  {
 431      auto successfully_connected = fuzzed_data_provider.ConsumeBool();
 432      auto remote_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS);
 433      auto local_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS);
 434      auto version = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max());
 435      auto relay_txs = fuzzed_data_provider.ConsumeBool();
 436      connman.Handshake(node, successfully_connected, remote_services, local_services, version, relay_txs);
 437  }
 438