1 // Copyright (c) 2021 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 <pubkey.h>
6 #include <test/fuzz/util.h>
7 #include <test/util/script.h>
8 #include <util/rbf.h>
9 #include <util/time.h>
10 #include <version.h>
11
FuzzedSock(FuzzedDataProvider & fuzzed_data_provider)12 FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
13 : m_fuzzed_data_provider{fuzzed_data_provider}
14 {
15 m_socket = fuzzed_data_provider.ConsumeIntegralInRange<SOCKET>(INVALID_SOCKET - 1, INVALID_SOCKET);
16 }
17
~FuzzedSock()18 FuzzedSock::~FuzzedSock()
19 {
20 // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
21 // Sock::Reset() (not FuzzedSock::Reset()!) which will call CloseSocket(m_socket).
22 // Avoid closing an arbitrary file descriptor (m_socket is just a random very high number which
23 // theoretically may concide with a real opened file descriptor).
24 Reset();
25 }
26
operator =(Sock && other)27 FuzzedSock& FuzzedSock::operator=(Sock&& other)
28 {
29 assert(false && "Move of Sock into FuzzedSock not allowed.");
30 return *this;
31 }
32
Reset()33 void FuzzedSock::Reset()
34 {
35 m_socket = INVALID_SOCKET;
36 }
37
Send(const void * data,size_t len,int flags) const38 ssize_t FuzzedSock::Send(const void* data, size_t len, int flags) const
39 {
40 constexpr std::array send_errnos{
41 EACCES,
42 EAGAIN,
43 EALREADY,
44 EBADF,
45 ECONNRESET,
46 EDESTADDRREQ,
47 EFAULT,
48 EINTR,
49 EINVAL,
50 EISCONN,
51 EMSGSIZE,
52 ENOBUFS,
53 ENOMEM,
54 ENOTCONN,
55 ENOTSOCK,
56 EOPNOTSUPP,
57 EPIPE,
58 EWOULDBLOCK,
59 };
60 if (m_fuzzed_data_provider.ConsumeBool()) {
61 return len;
62 }
63 const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
64 if (r == -1) {
65 SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
66 }
67 return r;
68 }
69
Recv(void * buf,size_t len,int flags) const70 ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const
71 {
72 // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
73 // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
74 // returning -1 and setting errno to EAGAIN repeatedly.
75 constexpr std::array recv_errnos{
76 ECONNREFUSED,
77 EAGAIN,
78 EBADF,
79 EFAULT,
80 EINTR,
81 EINVAL,
82 ENOMEM,
83 ENOTCONN,
84 ENOTSOCK,
85 EWOULDBLOCK,
86 };
87 assert(buf != nullptr || len == 0);
88 if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
89 const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
90 if (r == -1) {
91 SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
92 }
93 return r;
94 }
95 std::vector<uint8_t> random_bytes;
96 bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
97 if (m_peek_data.has_value()) {
98 // `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
99 random_bytes.assign({m_peek_data.value()});
100 if ((flags & MSG_PEEK) == 0) {
101 m_peek_data.reset();
102 }
103 pad_to_len_bytes = false;
104 } else if ((flags & MSG_PEEK) != 0) {
105 // New call with `MSG_PEEK`.
106 random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
107 if (!random_bytes.empty()) {
108 m_peek_data = random_bytes[0];
109 pad_to_len_bytes = false;
110 }
111 } else {
112 random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
113 m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
114 }
115 if (random_bytes.empty()) {
116 const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
117 if (r == -1) {
118 SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
119 }
120 return r;
121 }
122 std::memcpy(buf, random_bytes.data(), random_bytes.size());
123 if (pad_to_len_bytes) {
124 if (len > random_bytes.size()) {
125 std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
126 }
127 return len;
128 }
129 if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
130 std::this_thread::sleep_for(std::chrono::milliseconds{2});
131 }
132 return random_bytes.size();
133 }
134
Connect(const sockaddr *,socklen_t) const135 int FuzzedSock::Connect(const sockaddr*, socklen_t) const
136 {
137 // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
138 // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
139 // returning -1 and setting errno to EAGAIN repeatedly.
140 constexpr std::array connect_errnos{
141 ECONNREFUSED,
142 EAGAIN,
143 ECONNRESET,
144 EHOSTUNREACH,
145 EINPROGRESS,
146 EINTR,
147 ENETUNREACH,
148 ETIMEDOUT,
149 };
150 if (m_fuzzed_data_provider.ConsumeBool()) {
151 SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
152 return -1;
153 }
154 return 0;
155 }
156
GetSockOpt(int level,int opt_name,void * opt_val,socklen_t * opt_len) const157 int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
158 {
159 constexpr std::array getsockopt_errnos{
160 ENOMEM,
161 ENOBUFS,
162 };
163 if (m_fuzzed_data_provider.ConsumeBool()) {
164 SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
165 return -1;
166 }
167 if (opt_val == nullptr) {
168 return 0;
169 }
170 std::memcpy(opt_val,
171 ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
172 *opt_len);
173 return 0;
174 }
175
Wait(std::chrono::milliseconds timeout,Event requested,Event * occurred) const176 bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
177 {
178 constexpr std::array wait_errnos{
179 EBADF,
180 EINTR,
181 EINVAL,
182 };
183 if (m_fuzzed_data_provider.ConsumeBool()) {
184 SetFuzzedErrNo(m_fuzzed_data_provider, wait_errnos);
185 return false;
186 }
187 if (occurred != nullptr) {
188 *occurred = m_fuzzed_data_provider.ConsumeBool() ? requested : 0;
189 }
190 return true;
191 }
192
IsConnected(std::string & errmsg) const193 bool FuzzedSock::IsConnected(std::string& errmsg) const
194 {
195 if (m_fuzzed_data_provider.ConsumeBool()) {
196 return true;
197 }
198 errmsg = "disconnected at random by the fuzzer";
199 return false;
200 }
201
FillNode(FuzzedDataProvider & fuzzed_data_provider,CNode & node,bool init_version)202 void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept
203 {
204 const ServiceFlags remote_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS);
205 const NetPermissionFlags permission_flags = ConsumeWeakEnum(fuzzed_data_provider, ALL_NET_PERMISSION_FLAGS);
206 const int32_t version = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max());
207 const bool filter_txs = fuzzed_data_provider.ConsumeBool();
208
209 node.nServices = remote_services;
210 node.m_permissionFlags = permission_flags;
211 if (init_version) {
212 node.nVersion = version;
213 node.SetCommonVersion(std::min(version, PROTOCOL_VERSION));
214 }
215 if (node.m_tx_relay != nullptr) {
216 LOCK(node.m_tx_relay->cs_filter);
217 node.m_tx_relay->fRelayTxes = filter_txs;
218 }
219 }
220
ConsumeMoney(FuzzedDataProvider & fuzzed_data_provider,const std::optional<CAmount> & max)221 CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::optional<CAmount>& max) noexcept
222 {
223 return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, max.value_or(MAX_MONEY));
224 }
225
ConsumeTime(FuzzedDataProvider & fuzzed_data_provider,const std::optional<int64_t> & min,const std::optional<int64_t> & max)226 int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider, const std::optional<int64_t>& min, const std::optional<int64_t>& max) noexcept
227 {
228 // Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) disables mocktime.
229 static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:01Z");
230 static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z");
231 return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(min.value_or(time_min), max.value_or(time_max));
232 }
233
ConsumeTransaction(FuzzedDataProvider & fuzzed_data_provider,const std::optional<std::vector<uint256>> & prevout_txids,const int max_num_in,const int max_num_out)234 CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, const std::optional<std::vector<uint256>>& prevout_txids, const int max_num_in, const int max_num_out) noexcept
235 {
236 CMutableTransaction tx_mut;
237 const auto p2wsh_op_true = fuzzed_data_provider.ConsumeBool();
238 tx_mut.nVersion = fuzzed_data_provider.ConsumeBool() ?
239 CTransaction::CURRENT_VERSION :
240 fuzzed_data_provider.ConsumeIntegral<int32_t>();
241 tx_mut.nLockTime = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
242 const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, max_num_in);
243 const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, max_num_out);
244 for (int i = 0; i < num_in; ++i) {
245 const auto& txid_prev = prevout_txids ?
246 PickValue(fuzzed_data_provider, *prevout_txids) :
247 ConsumeUInt256(fuzzed_data_provider);
248 const auto index_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, max_num_out);
249 const auto sequence = ConsumeSequence(fuzzed_data_provider);
250 const auto script_sig = p2wsh_op_true ? CScript{} : ConsumeScript(fuzzed_data_provider);
251 CScriptWitness script_wit;
252 if (p2wsh_op_true) {
253 script_wit.stack = std::vector<std::vector<uint8_t>>{WITNESS_STACK_ELEM_OP_TRUE};
254 } else {
255 script_wit = ConsumeScriptWitness(fuzzed_data_provider);
256 }
257 CTxIn in;
258 in.prevout = COutPoint{txid_prev, index_out};
259 in.nSequence = sequence;
260 in.scriptSig = script_sig;
261 in.scriptWitness = script_wit;
262
263 tx_mut.vin.push_back(in);
264 }
265 for (int i = 0; i < num_out; ++i) {
266 const auto amount = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-10, 50 * COIN + 10);
267 const auto script_pk = p2wsh_op_true ?
268 P2WSH_OP_TRUE :
269 ConsumeScript(fuzzed_data_provider, /* max_length */ 128, /* maybe_p2wsh */ true);
270 tx_mut.vout.emplace_back(amount, script_pk);
271 }
272 return tx_mut;
273 }
274
ConsumeScriptWitness(FuzzedDataProvider & fuzzed_data_provider,const size_t max_stack_elem_size)275 CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, const size_t max_stack_elem_size) noexcept
276 {
277 CScriptWitness ret;
278 const auto n_elements = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_stack_elem_size);
279 for (size_t i = 0; i < n_elements; ++i) {
280 ret.stack.push_back(ConsumeRandomLengthByteVector(fuzzed_data_provider));
281 }
282 return ret;
283 }
284
ConsumeScript(FuzzedDataProvider & fuzzed_data_provider,const std::optional<size_t> & max_length,const bool maybe_p2wsh)285 CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length, const bool maybe_p2wsh) noexcept
286 {
287 const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
288 CScript r_script{b.begin(), b.end()};
289 if (maybe_p2wsh && fuzzed_data_provider.ConsumeBool()) {
290 uint256 script_hash;
291 CSHA256().Write(r_script.data(), r_script.size()).Finalize(script_hash.begin());
292 r_script.clear();
293 r_script << OP_0 << ToByteVector(script_hash);
294 }
295 return r_script;
296 }
297
ConsumeSequence(FuzzedDataProvider & fuzzed_data_provider)298 uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept
299 {
300 return fuzzed_data_provider.ConsumeBool() ?
301 fuzzed_data_provider.PickValueInArray({
302 CTxIn::SEQUENCE_FINAL,
303 CTxIn::SEQUENCE_FINAL - 1,
304 MAX_BIP125_RBF_SEQUENCE,
305 }) :
306 fuzzed_data_provider.ConsumeIntegral<uint32_t>();
307 }
308
ConsumeTxDestination(FuzzedDataProvider & fuzzed_data_provider)309 CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept
310 {
311 CTxDestination tx_destination;
312 const size_t call_size{CallOneOf(
313 fuzzed_data_provider,
314 [&] {
315 tx_destination = CNoDestination{};
316 },
317 [&] {
318 tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)};
319 },
320 [&] {
321 tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)};
322 },
323 [&] {
324 tx_destination = WitnessV0ScriptHash{ConsumeUInt256(fuzzed_data_provider)};
325 },
326 [&] {
327 tx_destination = WitnessV0KeyHash{ConsumeUInt160(fuzzed_data_provider)};
328 },
329 [&] {
330 tx_destination = WitnessV1Taproot{XOnlyPubKey{ConsumeUInt256(fuzzed_data_provider)}};
331 },
332 [&] {
333 WitnessUnknown witness_unknown{};
334 witness_unknown.version = fuzzed_data_provider.ConsumeIntegralInRange(2, 16);
335 std::vector<uint8_t> witness_unknown_program_1{fuzzed_data_provider.ConsumeBytes<uint8_t>(40)};
336 if (witness_unknown_program_1.size() < 2) {
337 witness_unknown_program_1 = {0, 0};
338 }
339 witness_unknown.length = witness_unknown_program_1.size();
340 std::copy(witness_unknown_program_1.begin(), witness_unknown_program_1.end(), witness_unknown.program);
341 tx_destination = witness_unknown;
342 })};
343 Assert(call_size == std::variant_size_v<CTxDestination>);
344 return tx_destination;
345 }
346
ConsumeTxMemPoolEntry(FuzzedDataProvider & fuzzed_data_provider,const CTransaction & tx)347 CTxMemPoolEntry ConsumeTxMemPoolEntry(FuzzedDataProvider& fuzzed_data_provider, const CTransaction& tx) noexcept
348 {
349 // Avoid:
350 // policy/feerate.cpp:28:34: runtime error: signed integer overflow: 34873208148477500 * 1000 cannot be represented in type 'long'
351 //
352 // Reproduce using CFeeRate(348732081484775, 10).GetFeePerK()
353 const CAmount fee = std::min<CAmount>(ConsumeMoney(fuzzed_data_provider), std::numeric_limits<CAmount>::max() / static_cast<CAmount>(100000));
354 assert(MoneyRange(fee));
355 const int64_t time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
356 const unsigned int entry_height = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
357 const bool spends_coinbase = fuzzed_data_provider.ConsumeBool();
358 const unsigned int sig_op_cost = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, MAX_BLOCK_SIGOPS_COST);
359 return CTxMemPoolEntry{MakeTransactionRef(tx), fee, time, entry_height, spends_coinbase, sig_op_cost, {}};
360 }
361
ContainsSpentInput(const CTransaction & tx,const CCoinsViewCache & inputs)362 bool ContainsSpentInput(const CTransaction& tx, const CCoinsViewCache& inputs) noexcept
363 {
364 for (const CTxIn& tx_in : tx.vin) {
365 const Coin& coin = inputs.AccessCoin(tx_in.prevout);
366 if (coin.IsSpent()) {
367 return true;
368 }
369 }
370 return false;
371 }
372
ConsumeNetAddr(FuzzedDataProvider & fuzzed_data_provider)373 CNetAddr ConsumeNetAddr(FuzzedDataProvider& fuzzed_data_provider) noexcept
374 {
375 const Network network = fuzzed_data_provider.PickValueInArray({Network::NET_IPV4, Network::NET_IPV6, Network::NET_INTERNAL, Network::NET_ONION});
376 CNetAddr net_addr;
377 if (network == Network::NET_IPV4) {
378 in_addr v4_addr = {};
379 v4_addr.s_addr = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
380 net_addr = CNetAddr{v4_addr};
381 } else if (network == Network::NET_IPV6) {
382 if (fuzzed_data_provider.remaining_bytes() >= 16) {
383 in6_addr v6_addr = {};
384 memcpy(v6_addr.s6_addr, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data(), 16);
385 net_addr = CNetAddr{v6_addr, fuzzed_data_provider.ConsumeIntegral<uint32_t>()};
386 }
387 } else if (network == Network::NET_INTERNAL) {
388 net_addr.SetInternal(fuzzed_data_provider.ConsumeBytesAsString(32));
389 } else if (network == Network::NET_ONION) {
390 net_addr.SetSpecial(fuzzed_data_provider.ConsumeBytesAsString(32));
391 }
392 return net_addr;
393 }
394
open()395 FILE* FuzzedFileProvider::open()
396 {
397 SetFuzzedErrNo(m_fuzzed_data_provider);
398 if (m_fuzzed_data_provider.ConsumeBool()) {
399 return nullptr;
400 }
401 std::string mode;
402 CallOneOf(
403 m_fuzzed_data_provider,
404 [&] {
405 mode = "r";
406 },
407 [&] {
408 mode = "r+";
409 },
410 [&] {
411 mode = "w";
412 },
413 [&] {
414 mode = "w+";
415 },
416 [&] {
417 mode = "a";
418 },
419 [&] {
420 mode = "a+";
421 });
422 #if defined _GNU_SOURCE && !defined __ANDROID__
423 const cookie_io_functions_t io_hooks = {
424 FuzzedFileProvider::read,
425 FuzzedFileProvider::write,
426 FuzzedFileProvider::seek,
427 FuzzedFileProvider::close,
428 };
429 return fopencookie(this, mode.c_str(), io_hooks);
430 #else
431 (void)mode;
432 return nullptr;
433 #endif
434 }
435
read(void * cookie,char * buf,size_t size)436 ssize_t FuzzedFileProvider::read(void* cookie, char* buf, size_t size)
437 {
438 FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
439 SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
440 if (buf == nullptr || size == 0 || fuzzed_file->m_fuzzed_data_provider.ConsumeBool()) {
441 return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
442 }
443 const std::vector<uint8_t> random_bytes = fuzzed_file->m_fuzzed_data_provider.ConsumeBytes<uint8_t>(size);
444 if (random_bytes.empty()) {
445 return 0;
446 }
447 std::memcpy(buf, random_bytes.data(), random_bytes.size());
448 if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)random_bytes.size())) {
449 return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
450 }
451 fuzzed_file->m_offset += random_bytes.size();
452 return random_bytes.size();
453 }
454
write(void * cookie,const char * buf,size_t size)455 ssize_t FuzzedFileProvider::write(void* cookie, const char* buf, size_t size)
456 {
457 FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
458 SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
459 const ssize_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(0, size);
460 if (AdditionOverflow(fuzzed_file->m_offset, (int64_t)n)) {
461 return fuzzed_file->m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
462 }
463 fuzzed_file->m_offset += n;
464 return n;
465 }
466
seek(void * cookie,int64_t * offset,int whence)467 int FuzzedFileProvider::seek(void* cookie, int64_t* offset, int whence)
468 {
469 assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
470 FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
471 SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
472 int64_t new_offset = 0;
473 if (whence == SEEK_SET) {
474 new_offset = *offset;
475 } else if (whence == SEEK_CUR) {
476 if (AdditionOverflow(fuzzed_file->m_offset, *offset)) {
477 return -1;
478 }
479 new_offset = fuzzed_file->m_offset + *offset;
480 } else if (whence == SEEK_END) {
481 const int64_t n = fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 4096);
482 if (AdditionOverflow(n, *offset)) {
483 return -1;
484 }
485 new_offset = n + *offset;
486 }
487 if (new_offset < 0) {
488 return -1;
489 }
490 fuzzed_file->m_offset = new_offset;
491 *offset = new_offset;
492 return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
493 }
494
close(void * cookie)495 int FuzzedFileProvider::close(void* cookie)
496 {
497 FuzzedFileProvider* fuzzed_file = (FuzzedFileProvider*)cookie;
498 SetFuzzedErrNo(fuzzed_file->m_fuzzed_data_provider);
499 return fuzzed_file->m_fuzzed_data_provider.ConsumeIntegralInRange<int>(-1, 0);
500 }
501