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