net.cpp raw

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