/ src / test / fuzz / util / net.cpp
net.cpp
  1  // Copyright (c) 2009-2022 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 <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  {
118  }
119  
120  FuzzedSock::~FuzzedSock()
121  {
122      // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
123      // close(m_socket) if m_socket is not INVALID_SOCKET.
124      // Avoid closing an arbitrary file descriptor (m_socket is just a random very high number which
125      // theoretically may concide with a real opened file descriptor).
126      m_socket = INVALID_SOCKET;
127  }
128  
129  FuzzedSock& FuzzedSock::operator=(Sock&& other)
130  {
131      assert(false && "Move of Sock into FuzzedSock not allowed.");
132      return *this;
133  }
134  
135  ssize_t FuzzedSock::Send(const void* data, size_t len, int flags) const
136  {
137      constexpr std::array send_errnos{
138          EACCES,
139          EAGAIN,
140          EALREADY,
141          EBADF,
142          ECONNRESET,
143          EDESTADDRREQ,
144          EFAULT,
145          EINTR,
146          EINVAL,
147          EISCONN,
148          EMSGSIZE,
149          ENOBUFS,
150          ENOMEM,
151          ENOTCONN,
152          ENOTSOCK,
153          EOPNOTSUPP,
154          EPIPE,
155          EWOULDBLOCK,
156      };
157      if (m_fuzzed_data_provider.ConsumeBool()) {
158          return len;
159      }
160      const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
161      if (r == -1) {
162          SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
163      }
164      return r;
165  }
166  
167  ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const
168  {
169      // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
170      // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
171      // returning -1 and setting errno to EAGAIN repeatedly.
172      constexpr std::array recv_errnos{
173          ECONNREFUSED,
174          EAGAIN,
175          EBADF,
176          EFAULT,
177          EINTR,
178          EINVAL,
179          ENOMEM,
180          ENOTCONN,
181          ENOTSOCK,
182          EWOULDBLOCK,
183      };
184      assert(buf != nullptr || len == 0);
185      if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
186          const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
187          if (r == -1) {
188              SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
189          }
190          return r;
191      }
192      std::vector<uint8_t> random_bytes;
193      bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
194      if (m_peek_data.has_value()) {
195          // `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
196          random_bytes.assign({m_peek_data.value()});
197          if ((flags & MSG_PEEK) == 0) {
198              m_peek_data.reset();
199          }
200          pad_to_len_bytes = false;
201      } else if ((flags & MSG_PEEK) != 0) {
202          // New call with `MSG_PEEK`.
203          random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
204          if (!random_bytes.empty()) {
205              m_peek_data = random_bytes[0];
206              pad_to_len_bytes = false;
207          }
208      } else {
209          random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
210              m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
211      }
212      if (random_bytes.empty()) {
213          const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
214          if (r == -1) {
215              SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
216          }
217          return r;
218      }
219      std::memcpy(buf, random_bytes.data(), random_bytes.size());
220      if (pad_to_len_bytes) {
221          if (len > random_bytes.size()) {
222              std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
223          }
224          return len;
225      }
226      if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
227          std::this_thread::sleep_for(std::chrono::milliseconds{2});
228      }
229      return random_bytes.size();
230  }
231  
232  int FuzzedSock::Connect(const sockaddr*, socklen_t) const
233  {
234      // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
235      // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
236      // returning -1 and setting errno to EAGAIN repeatedly.
237      constexpr std::array connect_errnos{
238          ECONNREFUSED,
239          EAGAIN,
240          ECONNRESET,
241          EHOSTUNREACH,
242          EINPROGRESS,
243          EINTR,
244          ENETUNREACH,
245          ETIMEDOUT,
246      };
247      if (m_fuzzed_data_provider.ConsumeBool()) {
248          SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
249          return -1;
250      }
251      return 0;
252  }
253  
254  int FuzzedSock::Bind(const sockaddr*, socklen_t) const
255  {
256      // Have a permanent error at bind_errnos[0] because when the fuzzed data is exhausted
257      // SetFuzzedErrNo() will always set the global errno to bind_errnos[0]. We want to
258      // avoid this method returning -1 and setting errno to a temporary error (like EAGAIN)
259      // repeatedly because proper code should retry on temporary errors, leading to an
260      // infinite loop.
261      constexpr std::array bind_errnos{
262          EACCES,
263          EADDRINUSE,
264          EADDRNOTAVAIL,
265          EAGAIN,
266      };
267      if (m_fuzzed_data_provider.ConsumeBool()) {
268          SetFuzzedErrNo(m_fuzzed_data_provider, bind_errnos);
269          return -1;
270      }
271      return 0;
272  }
273  
274  int FuzzedSock::Listen(int) const
275  {
276      // Have a permanent error at listen_errnos[0] because when the fuzzed data is exhausted
277      // SetFuzzedErrNo() will always set the global errno to listen_errnos[0]. We want to
278      // avoid this method returning -1 and setting errno to a temporary error (like EAGAIN)
279      // repeatedly because proper code should retry on temporary errors, leading to an
280      // infinite loop.
281      constexpr std::array listen_errnos{
282          EADDRINUSE,
283          EINVAL,
284          EOPNOTSUPP,
285      };
286      if (m_fuzzed_data_provider.ConsumeBool()) {
287          SetFuzzedErrNo(m_fuzzed_data_provider, listen_errnos);
288          return -1;
289      }
290      return 0;
291  }
292  
293  std::unique_ptr<Sock> FuzzedSock::Accept(sockaddr* addr, socklen_t* addr_len) const
294  {
295      constexpr std::array accept_errnos{
296          ECONNABORTED,
297          EINTR,
298          ENOMEM,
299      };
300      if (m_fuzzed_data_provider.ConsumeBool()) {
301          SetFuzzedErrNo(m_fuzzed_data_provider, accept_errnos);
302          return std::unique_ptr<FuzzedSock>();
303      }
304      return std::make_unique<FuzzedSock>(m_fuzzed_data_provider);
305  }
306  
307  int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
308  {
309      constexpr std::array getsockopt_errnos{
310          ENOMEM,
311          ENOBUFS,
312      };
313      if (m_fuzzed_data_provider.ConsumeBool()) {
314          SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
315          return -1;
316      }
317      if (opt_val == nullptr) {
318          return 0;
319      }
320      std::memcpy(opt_val,
321                  ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
322                  *opt_len);
323      return 0;
324  }
325  
326  int FuzzedSock::SetSockOpt(int, int, const void*, socklen_t) const
327  {
328      constexpr std::array setsockopt_errnos{
329          ENOMEM,
330          ENOBUFS,
331      };
332      if (m_fuzzed_data_provider.ConsumeBool()) {
333          SetFuzzedErrNo(m_fuzzed_data_provider, setsockopt_errnos);
334          return -1;
335      }
336      return 0;
337  }
338  
339  int FuzzedSock::GetSockName(sockaddr* name, socklen_t* name_len) const
340  {
341      constexpr std::array getsockname_errnos{
342          ECONNRESET,
343          ENOBUFS,
344      };
345      if (m_fuzzed_data_provider.ConsumeBool()) {
346          SetFuzzedErrNo(m_fuzzed_data_provider, getsockname_errnos);
347          return -1;
348      }
349      *name_len = m_fuzzed_data_provider.ConsumeData(name, *name_len);
350      return 0;
351  }
352  
353  bool FuzzedSock::SetNonBlocking() const
354  {
355      constexpr std::array setnonblocking_errnos{
356          EBADF,
357          EPERM,
358      };
359      if (m_fuzzed_data_provider.ConsumeBool()) {
360          SetFuzzedErrNo(m_fuzzed_data_provider, setnonblocking_errnos);
361          return false;
362      }
363      return true;
364  }
365  
366  bool FuzzedSock::IsSelectable() const
367  {
368      return m_selectable;
369  }
370  
371  bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
372  {
373      constexpr std::array wait_errnos{
374          EBADF,
375          EINTR,
376          EINVAL,
377      };
378      if (m_fuzzed_data_provider.ConsumeBool()) {
379          SetFuzzedErrNo(m_fuzzed_data_provider, wait_errnos);
380          return false;
381      }
382      if (occurred != nullptr) {
383          *occurred = m_fuzzed_data_provider.ConsumeBool() ? requested : 0;
384      }
385      return true;
386  }
387  
388  bool FuzzedSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const
389  {
390      for (auto& [sock, events] : events_per_sock) {
391          (void)sock;
392          events.occurred = m_fuzzed_data_provider.ConsumeBool() ? events.requested : 0;
393      }
394      return true;
395  }
396  
397  bool FuzzedSock::IsConnected(std::string& errmsg) const
398  {
399      if (m_fuzzed_data_provider.ConsumeBool()) {
400          return true;
401      }
402      errmsg = "disconnected at random by the fuzzer";
403      return false;
404  }
405  
406  void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept
407  {
408      connman.Handshake(node,
409                        /*successfully_connected=*/fuzzed_data_provider.ConsumeBool(),
410                        /*remote_services=*/ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS),
411                        /*local_services=*/ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS),
412                        /*version=*/fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max()),
413                        /*relay_txs=*/fuzzed_data_provider.ConsumeBool());
414  }