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