diff options
Diffstat (limited to 'src/test/fuzz')
35 files changed, 639 insertions, 111 deletions
diff --git a/src/test/fuzz/CMakeLists.txt b/src/test/fuzz/CMakeLists.txt new file mode 100644 index 0000000000..1c7b0d5c25 --- /dev/null +++ b/src/test/fuzz/CMakeLists.txt @@ -0,0 +1,147 @@ +# Copyright (c) 2023-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/license/mit/. + +add_subdirectory(util) + +add_executable(fuzz + addition_overflow.cpp + addrman.cpp + asmap.cpp + asmap_direct.cpp + autofile.cpp + banman.cpp + base_encode_decode.cpp + bech32.cpp + bip324.cpp + bitdeque.cpp + bitset.cpp + block.cpp + block_header.cpp + block_index.cpp + blockfilter.cpp + bloom_filter.cpp + buffered_file.cpp + chain.cpp + checkqueue.cpp + cluster_linearize.cpp + coins_view.cpp + coinscache_sim.cpp + connman.cpp + crypto.cpp + crypto_aes256.cpp + crypto_aes256cbc.cpp + crypto_chacha20.cpp + crypto_chacha20poly1305.cpp + crypto_common.cpp + crypto_diff_fuzz_chacha20.cpp + crypto_hkdf_hmac_sha256_l32.cpp + crypto_poly1305.cpp + cuckoocache.cpp + decode_tx.cpp + descriptor_parse.cpp + deserialize.cpp + eval_script.cpp + feefrac.cpp + fee_rate.cpp + feeratediagram.cpp + fees.cpp + flatfile.cpp + float.cpp + golomb_rice.cpp + headerssync.cpp + hex.cpp + http_request.cpp + i2p.cpp + integer.cpp + key.cpp + key_io.cpp + kitchen_sink.cpp + load_external_block_file.cpp + locale.cpp + merkleblock.cpp + message.cpp + miniscript.cpp + minisketch.cpp + mini_miner.cpp + muhash.cpp + multiplication_overflow.cpp + net.cpp + net_permissions.cpp + netaddress.cpp + netbase_dns_lookup.cpp + node_eviction.cpp + p2p_handshake.cpp + p2p_headers_presync.cpp + p2p_transport_serialization.cpp + package_eval.cpp + parse_hd_keypath.cpp + parse_numbers.cpp + parse_script.cpp + parse_univalue.cpp + partially_downloaded_block.cpp + policy_estimator.cpp + policy_estimator_io.cpp + poolresource.cpp + pow.cpp + prevector.cpp + primitives_transaction.cpp + process_message.cpp + process_messages.cpp + protocol.cpp + psbt.cpp + random.cpp + rbf.cpp + rolling_bloom_filter.cpp + rpc.cpp + script.cpp + script_assets_test_minimizer.cpp + script_descriptor_cache.cpp + script_flags.cpp + script_format.cpp + script_interpreter.cpp + script_ops.cpp + script_parsing.cpp + script_sigcache.cpp + script_sign.cpp + scriptnum_ops.cpp + secp256k1_ec_seckey_import_export_der.cpp + secp256k1_ecdsa_signature_parse_der_lax.cpp + signature_checker.cpp + signet.cpp + socks5.cpp + span.cpp + string.cpp + strprintf.cpp + system.cpp + timeoffsets.cpp + torcontrol.cpp + transaction.cpp + tx_in.cpp + tx_out.cpp + tx_pool.cpp + txorphan.cpp + txrequest.cpp + utxo_snapshot.cpp + utxo_total_supply.cpp + validation_load_mempool.cpp + vecdeque.cpp + versionbits.cpp +) +target_link_libraries(fuzz + core_interface + test_fuzz + bitcoin_cli + bitcoin_common + bitcoin_util + minisketch + leveldb + univalue + secp256k1 + Boost::headers + $<TARGET_NAME_IF_EXISTS:libevent::libevent> +) + +if(ENABLE_WALLET) + add_subdirectory(${PROJECT_SOURCE_DIR}/src/wallet/test/fuzz wallet) +endif() diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index dbec2bc858..593086af21 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -250,19 +250,30 @@ FUZZ_TARGET(addrman, .init = initialize_addrman) LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { addresses.push_back(ConsumeAddress(fuzzed_data_provider)); } - addr_man.Add(addresses, ConsumeNetAddr(fuzzed_data_provider), std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)}); + auto net_addr = ConsumeNetAddr(fuzzed_data_provider); + auto time_penalty = std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)}; + addr_man.Add(addresses, net_addr, time_penalty); }, [&] { - addr_man.Good(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); + auto addr = ConsumeService(fuzzed_data_provider); + auto time = NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}; + addr_man.Good(addr, time); }, [&] { - addr_man.Attempt(ConsumeService(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); + auto addr = ConsumeService(fuzzed_data_provider); + auto count_failure = fuzzed_data_provider.ConsumeBool(); + auto time = NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}; + addr_man.Attempt(addr, count_failure, time); }, [&] { - addr_man.Connected(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}); + auto addr = ConsumeService(fuzzed_data_provider); + auto time = NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}}; + addr_man.Connected(addr, time); }, [&] { - addr_man.SetServices(ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)); + auto addr = ConsumeService(fuzzed_data_provider); + auto n_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); + addr_man.SetServices(addr, n_services); }); } const AddrMan& const_addr_man{addr_man}; @@ -270,12 +281,19 @@ FUZZ_TARGET(addrman, .init = initialize_addrman) if (fuzzed_data_provider.ConsumeBool()) { network = fuzzed_data_provider.PickValueInArray(ALL_NETWORKS); } - (void)const_addr_man.GetAddr( - /*max_addresses=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), - /*max_pct=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), - network, - /*filtered=*/fuzzed_data_provider.ConsumeBool()); - (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network); + auto max_addresses = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096); + auto max_pct = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096); + auto filtered = fuzzed_data_provider.ConsumeBool(); + (void)const_addr_man.GetAddr(max_addresses, max_pct, network, filtered); + + std::unordered_set<Network> nets; + for (const auto& net : ALL_NETWORKS) { + if (fuzzed_data_provider.ConsumeBool()) { + nets.insert(net); + } + } + (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), nets); + std::optional<bool> in_new; if (fuzzed_data_provider.ConsumeBool()) { in_new = fuzzed_data_provider.ConsumeBool(); diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp index 45316b6b21..81761c7bf9 100644 --- a/src/test/fuzz/autofile.cpp +++ b/src/test/fuzz/autofile.cpp @@ -56,7 +56,6 @@ FUZZ_TARGET(autofile) WriteToStream(fuzzed_data_provider, auto_file); }); } - (void)auto_file.Get(); (void)auto_file.IsNull(); if (fuzzed_data_provider.ConsumeBool()) { FILE* f = auto_file.release(); diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp index b26151f63c..4165cc6b2c 100644 --- a/src/test/fuzz/banman.cpp +++ b/src/test/fuzz/banman.cpp @@ -78,7 +78,9 @@ FUZZ_TARGET(banman, .init = initialize_banman) contains_invalid = true; } } - ban_man.Ban(net_addr, ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); + auto ban_time_offset = ConsumeBanTimeOffset(fuzzed_data_provider); + auto since_unix_epoch = fuzzed_data_provider.ConsumeBool(); + ban_man.Ban(net_addr, ban_time_offset, since_unix_epoch); }, [&] { CSubNet subnet{ConsumeSubNet(fuzzed_data_provider)}; @@ -86,7 +88,9 @@ FUZZ_TARGET(banman, .init = initialize_banman) if (!subnet.IsValid()) { contains_invalid = true; } - ban_man.Ban(subnet, ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); + auto ban_time_offset = ConsumeBanTimeOffset(fuzzed_data_provider); + auto since_unix_epoch = fuzzed_data_provider.ConsumeBool(); + ban_man.Ban(subnet, ban_time_offset, since_unix_epoch); }, [&] { ban_man.ClearBanned(); diff --git a/src/test/fuzz/bip324.cpp b/src/test/fuzz/bip324.cpp index 9892e7a81c..f1fa15d8a3 100644 --- a/src/test/fuzz/bip324.cpp +++ b/src/test/fuzz/bip324.cpp @@ -10,6 +10,7 @@ #include <test/fuzz/fuzz.h> #include <test/fuzz/util.h> +#include <algorithm> #include <cstdint> #include <vector> @@ -59,9 +60,9 @@ FUZZ_TARGET(bip324_cipher_roundtrip, .init=Initialize) InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>()); // Compare session IDs and garbage terminators. - assert(initiator.GetSessionID() == responder.GetSessionID()); - assert(initiator.GetSendGarbageTerminator() == responder.GetReceiveGarbageTerminator()); - assert(initiator.GetReceiveGarbageTerminator() == responder.GetSendGarbageTerminator()); + assert(std::ranges::equal(initiator.GetSessionID(), responder.GetSessionID())); + assert(std::ranges::equal(initiator.GetSendGarbageTerminator(), responder.GetReceiveGarbageTerminator())); + assert(std::ranges::equal(initiator.GetReceiveGarbageTerminator(), responder.GetSendGarbageTerminator())); LIMITED_WHILE(provider.remaining_bytes(), 1000) { // Mode: diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp index e30c19b265..a6a042a25c 100644 --- a/src/test/fuzz/buffered_file.cpp +++ b/src/test/fuzz/buffered_file.cpp @@ -25,7 +25,9 @@ FUZZ_TARGET(buffered_file) ConsumeRandomLengthByteVector<std::byte>(fuzzed_data_provider), }; try { - opt_buffered_file.emplace(fuzzed_file, fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096)); + auto n_buf_size = fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096); + auto n_rewind_in = fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(0, 4096); + opt_buffered_file.emplace(fuzzed_file, n_buf_size, n_rewind_in); } catch (const std::ios_base::failure&) { } if (opt_buffered_file && !fuzzed_file.IsNull()) { diff --git a/src/test/fuzz/cluster_linearize.cpp b/src/test/fuzz/cluster_linearize.cpp index 2dfdfbb41d..d91f85d867 100644 --- a/src/test/fuzz/cluster_linearize.cpp +++ b/src/test/fuzz/cluster_linearize.cpp @@ -165,6 +165,23 @@ std::pair<std::vector<ClusterIndex>, bool> SimpleLinearize(const DepGraph<SetTyp return {std::move(linearization), optimal}; } +/** Stitch connected components together in a DepGraph, guaranteeing its corresponding cluster is connected. */ +template<typename BS> +void MakeConnected(DepGraph<BS>& depgraph) +{ + auto todo = BS::Fill(depgraph.TxCount()); + auto comp = depgraph.FindConnectedComponent(todo); + Assume(depgraph.IsConnected(comp)); + todo -= comp; + while (todo.Any()) { + auto nextcomp = depgraph.FindConnectedComponent(todo); + Assume(depgraph.IsConnected(nextcomp)); + depgraph.AddDependency(comp.Last(), nextcomp.First()); + todo -= nextcomp; + comp = nextcomp; + } +} + /** Given a dependency graph, and a todo set, read a topological subset of todo from reader. */ template<typename SetType> SetType ReadTopologicalSet(const DepGraph<SetType>& depgraph, const SetType& todo, SpanReader& reader) @@ -369,6 +386,20 @@ FUZZ_TARGET(clusterlin_components) assert(depgraph.FindConnectedComponent(todo).None()); } +FUZZ_TARGET(clusterlin_make_connected) +{ + // Verify that MakeConnected makes graphs connected. + + SpanReader reader(buffer); + DepGraph<TestBitSet> depgraph; + try { + reader >> Using<DepGraphFormatter>(depgraph); + } catch (const std::ios_base::failure&) {} + MakeConnected(depgraph); + SanityCheck(depgraph); + assert(depgraph.IsConnected()); +} + FUZZ_TARGET(clusterlin_chunking) { // Verify the correctness of the ChunkLinearization function. @@ -398,7 +429,7 @@ FUZZ_TARGET(clusterlin_chunking) SetInfo<TestBitSet> accumulator, best; for (ClusterIndex idx : linearization) { if (todo[idx]) { - accumulator |= SetInfo(depgraph, idx); + accumulator.Set(depgraph, idx); if (best.feerate.IsEmpty() || accumulator.feerate >> best.feerate) { best = accumulator; } @@ -427,6 +458,7 @@ FUZZ_TARGET(clusterlin_ancestor_finder) while (todo.Any()) { // Call the ancestor finder's FindCandidateSet for what remains of the graph. assert(!anc_finder.AllDone()); + assert(todo.Count() == anc_finder.NumRemaining()); auto best_anc = anc_finder.FindCandidateSet(); // Sanity check the result. assert(best_anc.transactions.Any()); @@ -458,6 +490,7 @@ FUZZ_TARGET(clusterlin_ancestor_finder) anc_finder.MarkDone(del_set); } assert(anc_finder.AllDone()); + assert(anc_finder.NumRemaining() == 0); } static constexpr auto MAX_SIMPLE_ITERATIONS = 300000; @@ -468,13 +501,17 @@ FUZZ_TARGET(clusterlin_search_finder) // and comparing with the results from SimpleCandidateFinder, ExhaustiveCandidateFinder, and // AncestorCandidateFinder. - // Retrieve an RNG seed and a depgraph from the fuzz input. + // Retrieve an RNG seed, a depgraph, and whether to make it connected, from the fuzz input. SpanReader reader(buffer); DepGraph<TestBitSet> depgraph; uint64_t rng_seed{0}; + uint8_t make_connected{1}; try { - reader >> Using<DepGraphFormatter>(depgraph) >> rng_seed; + reader >> Using<DepGraphFormatter>(depgraph) >> rng_seed >> make_connected; } catch (const std::ios_base::failure&) {} + // The most complicated graphs are connected ones (other ones just split up). Optionally force + // the graph to be connected. + if (make_connected) MakeConnected(depgraph); // Instantiate ALL the candidate finders. SearchCandidateFinder src_finder(depgraph, rng_seed); @@ -488,6 +525,7 @@ FUZZ_TARGET(clusterlin_search_finder) assert(!smp_finder.AllDone()); assert(!exh_finder.AllDone()); assert(!anc_finder.AllDone()); + assert(anc_finder.NumRemaining() == todo.Count()); // For each iteration, read an iteration count limit from the fuzz input. uint64_t max_iterations = 1; @@ -513,9 +551,17 @@ FUZZ_TARGET(clusterlin_search_finder) assert(found.transactions.IsSupersetOf(depgraph.Ancestors(i) & todo)); } - // At most 2^N-1 iterations can be required: the number of non-empty subsets a graph with N - // transactions has. - assert(iterations_done <= ((uint64_t{1} << todo.Count()) - 1)); + // At most 2^(N-1) iterations can be required: the maximum number of non-empty topological + // subsets a (connected) cluster with N transactions can have. Even when the cluster is no + // longer connected after removing certain transactions, this holds, because the connected + // components are searched separately. + assert(iterations_done <= (uint64_t{1} << (todo.Count() - 1))); + // Additionally, test that no more than sqrt(2^N)+1 iterations are required. This is just + // an empirical bound that seems to hold, without proof. Still, add a test for it so we + // can learn about counterexamples if they exist. + if (iterations_done >= 1 && todo.Count() <= 63) { + Assume((iterations_done - 1) * (iterations_done - 1) <= uint64_t{1} << todo.Count()); + } // Perform quality checks only if SearchCandidateFinder claims an optimal result. if (iterations_done < max_iterations) { @@ -562,6 +608,7 @@ FUZZ_TARGET(clusterlin_search_finder) assert(smp_finder.AllDone()); assert(exh_finder.AllDone()); assert(anc_finder.AllDone()); + assert(anc_finder.NumRemaining() == 0); } FUZZ_TARGET(clusterlin_linearization_chunking) @@ -621,7 +668,7 @@ FUZZ_TARGET(clusterlin_linearization_chunking) SetInfo<TestBitSet> accumulator, best; for (auto j : linearization) { if (todo[j] && !combined[j]) { - accumulator |= SetInfo(depgraph, j); + accumulator.Set(depgraph, j); if (best.feerate.IsEmpty() || accumulator.feerate > best.feerate) { best = accumulator; } @@ -685,14 +732,19 @@ FUZZ_TARGET(clusterlin_linearize) { // Verify the behavior of Linearize(). - // Retrieve an RNG seed, an iteration count, and a depgraph from the fuzz input. + // Retrieve an RNG seed, an iteration count, a depgraph, and whether to make it connected from + // the fuzz input. SpanReader reader(buffer); DepGraph<TestBitSet> depgraph; uint64_t rng_seed{0}; uint64_t iter_count{0}; + uint8_t make_connected{1}; try { - reader >> VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph) >> rng_seed; + reader >> VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph) >> rng_seed >> make_connected; } catch (const std::ios_base::failure&) {} + // The most complicated graphs are connected ones (other ones just split up). Optionally force + // the graph to be connected. + if (make_connected) MakeConnected(depgraph); // Optionally construct an old linearization for it. std::vector<ClusterIndex> old_linearization; @@ -721,12 +773,24 @@ FUZZ_TARGET(clusterlin_linearize) } // If the iteration count is sufficiently high, an optimal linearization must be found. - // Each linearization step can use up to 2^k iterations, with steps k=1..n. That sum is - // 2 * (2^n - 1) + // Each linearization step can use up to 2^(k-1) iterations, with steps k=1..n. That sum is + // 2^n - 1. const uint64_t n = depgraph.TxCount(); - if (n <= 18 && iter_count > 2U * ((uint64_t{1} << n) - 1U)) { + if (n <= 19 && iter_count > (uint64_t{1} << n)) { assert(optimal); } + // Additionally, if the assumption of sqrt(2^k)+1 iterations per step holds, plus ceil(k/4) + // start-up cost per step, plus ceil(n^2/64) start-up cost overall, we can compute the upper + // bound for a whole linearization (summing for k=1..n) using the Python expression + // [sum((k+3)//4 + int(math.sqrt(2**k)) + 1 for k in range(1, n + 1)) + (n**2 + 63) // 64 for n in range(0, 35)]: + static constexpr uint64_t MAX_OPTIMAL_ITERS[] = { + 0, 4, 8, 12, 18, 26, 37, 51, 70, 97, 133, 182, 251, 346, 480, 666, 927, 1296, 1815, 2545, + 3576, 5031, 7087, 9991, 14094, 19895, 28096, 39690, 56083, 79263, 112041, 158391, 223936, + 316629, 447712 + }; + if (n < std::size(MAX_OPTIMAL_ITERS) && iter_count >= MAX_OPTIMAL_ITERS[n]) { + Assume(optimal); + } // If Linearize claims optimal result, run quality tests. if (optimal) { diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index 8f5f6a6071..beefc9d82e 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -91,17 +91,15 @@ FUZZ_TARGET(connman, .init = initialize_connman) (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); }); }, [&] { - (void)connman.GetAddresses( - /*max_addresses=*/fuzzed_data_provider.ConsumeIntegral<size_t>(), - /*max_pct=*/fuzzed_data_provider.ConsumeIntegral<size_t>(), - /*network=*/std::nullopt, - /*filtered=*/fuzzed_data_provider.ConsumeBool()); + auto max_addresses = fuzzed_data_provider.ConsumeIntegral<size_t>(); + auto max_pct = fuzzed_data_provider.ConsumeIntegral<size_t>(); + auto filtered = fuzzed_data_provider.ConsumeBool(); + (void)connman.GetAddresses(max_addresses, max_pct, /*network=*/std::nullopt, filtered); }, [&] { - (void)connman.GetAddresses( - /*requestor=*/random_node, - /*max_addresses=*/fuzzed_data_provider.ConsumeIntegral<size_t>(), - /*max_pct=*/fuzzed_data_provider.ConsumeIntegral<size_t>()); + auto max_addresses = fuzzed_data_provider.ConsumeIntegral<size_t>(); + auto max_pct = fuzzed_data_provider.ConsumeIntegral<size_t>(); + (void)connman.GetAddresses(/*requestor=*/random_node, max_addresses, max_pct); }, [&] { (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>()); diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp index ca8c1cd033..aa478277e3 100644 --- a/src/test/fuzz/crypto.cpp +++ b/src/test/fuzz/crypto.cpp @@ -22,7 +22,9 @@ FUZZ_TARGET(crypto) FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider); if (data.empty()) { - data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>()); + auto new_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096); + auto x = fuzzed_data_provider.ConsumeIntegral<uint8_t>(); + data.resize(new_size, x); } CHash160 hash160; @@ -44,7 +46,9 @@ FUZZ_TARGET(crypto) if (fuzzed_data_provider.ConsumeBool()) { data = ConsumeRandomLengthByteVector(fuzzed_data_provider); if (data.empty()) { - data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>()); + auto new_size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096); + auto x = fuzzed_data_provider.ConsumeIntegral<uint8_t>(); + data.resize(new_size, x); } } diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp index d115a2b7e1..fe47f18923 100644 --- a/src/test/fuzz/crypto_chacha20.cpp +++ b/src/test/fuzz/crypto_chacha20.cpp @@ -28,11 +28,10 @@ FUZZ_TARGET(crypto_chacha20) chacha20.SetKey(key); }, [&] { - chacha20.Seek( - { - fuzzed_data_provider.ConsumeIntegral<uint32_t>(), - fuzzed_data_provider.ConsumeIntegral<uint64_t>() - }, fuzzed_data_provider.ConsumeIntegral<uint32_t>()); + ChaCha20::Nonce96 nonce{ + fuzzed_data_provider.ConsumeIntegral<uint32_t>(), + fuzzed_data_provider.ConsumeIntegral<uint64_t>()}; + chacha20.Seek(nonce, fuzzed_data_provider.ConsumeIntegral<uint32_t>()); }, [&] { std::vector<uint8_t> output(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)); diff --git a/src/test/fuzz/crypto_chacha20poly1305.cpp b/src/test/fuzz/crypto_chacha20poly1305.cpp index 2b39a06094..5e62e6f3df 100644 --- a/src/test/fuzz/crypto_chacha20poly1305.cpp +++ b/src/test/fuzz/crypto_chacha20poly1305.cpp @@ -130,7 +130,7 @@ FUZZ_TARGET(crypto_fschacha20poly1305) // data). InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>()); - LIMITED_WHILE(provider.ConsumeBool(), 10000) + LIMITED_WHILE(provider.ConsumeBool(), 100) { // Mode: // - Bit 0: whether to use single-plain Encrypt/Decrypt; otherwise use a split at prefix. diff --git a/src/test/fuzz/cuckoocache.cpp b/src/test/fuzz/cuckoocache.cpp index 50a71ee03f..f8a5bde3e6 100644 --- a/src/test/fuzz/cuckoocache.cpp +++ b/src/test/fuzz/cuckoocache.cpp @@ -41,7 +41,9 @@ FUZZ_TARGET(cuckoocache) if (fuzzed_data_provider.ConsumeBool()) { cuckoo_cache.insert(fuzzed_data_provider.ConsumeBool()); } else { - cuckoo_cache.contains(fuzzed_data_provider.ConsumeBool(), fuzzed_data_provider.ConsumeBool()); + auto e = fuzzed_data_provider.ConsumeBool(); + auto erase = fuzzed_data_provider.ConsumeBool(); + cuckoo_cache.contains(e, erase); } } fuzzed_data_provider_ptr = nullptr; diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index 6a3f4d6dfe..765daa3db7 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -15,14 +15,24 @@ MockedDescriptorConverter MOCKED_DESC_CONVERTER; /** Test a successfully parsed descriptor. */ -static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy) +static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy, std::optional<bool>& is_ranged, std::optional<bool>& is_solvable) { // Trivial helpers. (void)desc.IsRange(); - const bool is_solvable{desc.IsSolvable()}; (void)desc.IsSingleType(); (void)desc.GetOutputType(); + if (is_ranged.has_value()) { + assert(desc.IsRange() == *is_ranged); + } else { + is_ranged = desc.IsRange(); + } + if (is_solvable.has_value()) { + assert(desc.IsSolvable() == *is_solvable); + } else { + is_solvable = desc.IsSolvable(); + } + // Serialization to string representation. (void)desc.ToString(); (void)desc.ToPrivateString(sig_provider, dummy); @@ -48,7 +58,7 @@ static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_prov const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)}; const auto max_elems{desc.MaxSatisfactionElems()}; // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo). - const bool is_nontop_or_nonsolvable{!is_solvable || !desc.GetOutputType()}; + const bool is_nontop_or_nonsolvable{!*is_solvable || !desc.GetOutputType()}; const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems}; assert(is_input_size_info_set || is_nontop_or_nonsolvable); } @@ -85,7 +95,12 @@ FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse) FlatSigningProvider signing_provider; std::string error; const auto desc = Parse(*descriptor, signing_provider, error); - if (desc) TestDescriptor(*desc, signing_provider, error); + std::optional<bool> is_ranged; + std::optional<bool> is_solvable; + for (const auto& d : desc) { + assert(d); + TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable); + } } } @@ -101,6 +116,11 @@ FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse) std::string error; for (const bool require_checksum : {true, false}) { const auto desc = Parse(descriptor, signing_provider, error, require_checksum); - if (desc) TestDescriptor(*desc, signing_provider, error); + std::optional<bool> is_ranged; + std::optional<bool> is_solvable; + for (const auto& d : desc) { + assert(d); + TestDescriptor(*d, signing_provider, error, is_ranged, is_solvable); + } } } diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index 96283a3e15..bba2dd8e3a 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -49,7 +49,7 @@ static std::vector<const char*> g_args; static void SetArgs(int argc, char** argv) { for (int i = 1; i < argc; ++i) { // Only take into account arguments that start with `--`. The others are for the fuzz engine: - // `fuzz -runs=1 fuzz_seed_corpus/address_deserialize_v2 --checkaddrman=5` + // `fuzz -runs=1 fuzz_corpora/address_deserialize_v2 --checkaddrman=5` if (strlen(argv[i]) > 2 && argv[i][0] == '-' && argv[i][1] == '-') { g_args.push_back(argv[i]); } @@ -106,7 +106,7 @@ void initialize() // randomness during the fuzz test, except: // - GetStrongRandBytes(), which is used for the creation of private key material. // - Creating a BasicTestingSetup or derived class will switch to a random seed. - SeedRandomForTest(SeedRand::ZEROS); + SeedRandomStateForTest(SeedRand::ZEROS); // Terminate immediately if a fuzzing harness ever tries to create a socket. // Individual tests can override this by pointing CreateSock to a mocked alternative. diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp index ebe30c3c1a..3dcf1ed3d5 100644 --- a/src/test/fuzz/hex.cpp +++ b/src/test/fuzz/hex.cpp @@ -12,6 +12,7 @@ #include <util/strencodings.h> #include <util/transaction_identifier.h> +#include <algorithm> #include <cassert> #include <cstdint> #include <string> @@ -22,18 +23,26 @@ FUZZ_TARGET(hex) const std::string random_hex_string(buffer.begin(), buffer.end()); const std::vector<unsigned char> data = ParseHex(random_hex_string); const std::vector<std::byte> bytes{ParseHex<std::byte>(random_hex_string)}; - assert(AsBytes(Span{data}) == Span{bytes}); + assert(std::ranges::equal(AsBytes(Span{data}), bytes)); const std::string hex_data = HexStr(data); if (IsHex(random_hex_string)) { assert(ToLower(random_hex_string) == hex_data); } - (void)IsHexNumber(random_hex_string); if (uint256::FromHex(random_hex_string)) { assert(random_hex_string.length() == 64); assert(Txid::FromHex(random_hex_string)); assert(Wtxid::FromHex(random_hex_string)); + assert(uint256::FromUserHex(random_hex_string)); + } + if (const auto result{uint256::FromUserHex(random_hex_string)}) { + const auto result_string{result->ToString()}; // ToString() returns a fixed-length string without "0x" prefix + assert(result_string.length() == 64); + assert(IsHex(result_string)); + assert(TryParseHex(result_string)); + assert(Txid::FromHex(result_string)); + assert(Wtxid::FromHex(result_string)); + assert(uint256::FromHex(result_string)); } - (void)uint256S(random_hex_string); try { (void)HexToPubKey(random_hex_string); } catch (const UniValue&) { diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index 02c6796d11..b9e3154106 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -69,7 +69,7 @@ FUZZ_TARGET(integer, .init = initialize_integer) const bool b = fuzzed_data_provider.ConsumeBool(); const Consensus::Params& consensus_params = Params().GetConsensus(); - (void)CheckProofOfWork(u256, u32, consensus_params); + (void)CheckProofOfWorkImpl(u256, u32, consensus_params); if (u64 <= MAX_MONEY) { const uint64_t compressed_money_amount = CompressAmount(u64); assert(u64 == DecompressAmount(compressed_money_amount)); @@ -140,7 +140,7 @@ FUZZ_TARGET(integer, .init = initialize_integer) const arith_uint256 au256 = UintToArith256(u256); assert(ArithToUint256(au256) == u256); - assert(uint256S(au256.GetHex()) == u256); + assert(uint256::FromHex(au256.GetHex()).value() == u256); (void)au256.bits(); (void)au256.GetCompact(/* fNegative= */ false); (void)au256.GetCompact(/* fNegative= */ true); diff --git a/src/test/fuzz/message.cpp b/src/test/fuzz/message.cpp index 6763206f72..99bbad6591 100644 --- a/src/test/fuzz/message.cpp +++ b/src/test/fuzz/message.cpp @@ -39,7 +39,9 @@ FUZZ_TARGET(message, .init = initialize_message) } { (void)MessageHash(random_message); - (void)MessageVerify(fuzzed_data_provider.ConsumeRandomLengthString(1024), fuzzed_data_provider.ConsumeRandomLengthString(1024), random_message); + auto address = fuzzed_data_provider.ConsumeRandomLengthString(1024); + auto signature = fuzzed_data_provider.ConsumeRandomLengthString(1024); + (void)MessageVerify(address, signature, random_message); (void)SigningResultString(fuzzed_data_provider.PickValueInArray({SigningResult::OK, SigningResult::PRIVATE_KEY_NOT_AVAILABLE, SigningResult::SIGNING_FAILED})); } } diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index 1f9ed9a064..5b9e168856 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -13,6 +13,8 @@ #include <test/fuzz/util.h> #include <util/strencodings.h> +#include <algorithm> + namespace { using Fragment = miniscript::Fragment; @@ -293,7 +295,7 @@ const struct CheckerContext: BaseSignatureChecker { XOnlyPubKey pk{pubkey}; auto it = TEST_DATA.schnorr_sigs.find(pk); if (it == TEST_DATA.schnorr_sigs.end()) return false; - return it->second.first == sig; + return std::ranges::equal(it->second.first, sig); } bool CheckLockTime(const CScriptNum& nLockTime) const override { return nLockTime.GetInt64() & 1; } bool CheckSequence(const CScriptNum& nSequence) const override { return nSequence.GetInt64() & 1; } diff --git a/src/test/fuzz/p2p_handshake.cpp b/src/test/fuzz/p2p_handshake.cpp index 217655ab70..6c1ed11d45 100644 --- a/src/test/fuzz/p2p_handshake.cpp +++ b/src/test/fuzz/p2p_handshake.cpp @@ -74,7 +74,7 @@ FUZZ_TARGET(p2p_handshake, .init = ::initialize) { CNode& connection = *PickValue(fuzzed_data_provider, peers); if (connection.fDisconnect || connection.fSuccessfullyConnected) { - // Skip if the the connection was disconnected or if the version + // Skip if the connection was disconnected or if the version // handshake was already completed. continue; } diff --git a/src/test/fuzz/p2p_headers_presync.cpp b/src/test/fuzz/p2p_headers_presync.cpp new file mode 100644 index 0000000000..b86d03442d --- /dev/null +++ b/src/test/fuzz/p2p_headers_presync.cpp @@ -0,0 +1,200 @@ +#include <blockencodings.h> +#include <net.h> +#include <net_processing.h> +#include <netmessagemaker.h> +#include <node/peerman_args.h> +#include <pow.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <test/fuzz/util.h> +#include <test/util/net.h> +#include <test/util/script.h> +#include <test/util/setup_common.h> +#include <validation.h> + +namespace { +constexpr uint32_t FUZZ_MAX_HEADERS_RESULTS{16}; + +class HeadersSyncSetup : public TestingSetup +{ + std::vector<CNode*> m_connections; + +public: + HeadersSyncSetup(const ChainType chain_type = ChainType::MAIN, + TestOpts opts = {}) + : TestingSetup(chain_type, opts) + { + PeerManager::Options peerman_opts; + node::ApplyArgsManOptions(*m_node.args, peerman_opts); + peerman_opts.max_headers_result = FUZZ_MAX_HEADERS_RESULTS; + m_node.peerman = PeerManager::make(*m_node.connman, *m_node.addrman, + m_node.banman.get(), *m_node.chainman, + *m_node.mempool, *m_node.warnings, peerman_opts); + + CConnman::Options options; + options.m_msgproc = m_node.peerman.get(); + m_node.connman->Init(options); + } + + void ResetAndInitialize() EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex); + void SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg) + EXCLUSIVE_LOCKS_REQUIRED(NetEventsInterface::g_msgproc_mutex); +}; + +void HeadersSyncSetup::ResetAndInitialize() +{ + m_connections.clear(); + auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman); + connman.StopNodes(); + + NodeId id{0}; + std::vector<ConnectionType> conn_types = { + ConnectionType::OUTBOUND_FULL_RELAY, + ConnectionType::BLOCK_RELAY, + ConnectionType::INBOUND + }; + + for (auto conn_type : conn_types) { + CAddress addr{}; + m_connections.push_back(new CNode(id++, nullptr, addr, 0, 0, addr, "", conn_type, false)); + CNode& p2p_node = *m_connections.back(); + + connman.Handshake( + /*node=*/p2p_node, + /*successfully_connected=*/true, + /*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS), + /*local_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS), + /*version=*/PROTOCOL_VERSION, + /*relay_txs=*/true); + + connman.AddTestNode(p2p_node); + } +} + +void HeadersSyncSetup::SendMessage(FuzzedDataProvider& fuzzed_data_provider, CSerializedNetMsg&& msg) +{ + auto& connman = static_cast<ConnmanTestMsg&>(*m_node.connman); + CNode& connection = *PickValue(fuzzed_data_provider, m_connections); + + connman.FlushSendBuffer(connection); + (void)connman.ReceiveMsgFrom(connection, std::move(msg)); + connection.fPauseSend = false; + try { + connman.ProcessMessagesOnce(connection); + } catch (const std::ios_base::failure&) { + } + m_node.peerman->SendMessages(&connection); +} + +CBlockHeader ConsumeHeader(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits) +{ + CBlockHeader header; + header.nNonce = 0; + // Either use the previous difficulty or let the fuzzer choose + header.nBits = fuzzed_data_provider.ConsumeBool() ? + prev_nbits : + fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0x17058EBE, 0x1D00FFFF); + header.nTime = ConsumeTime(fuzzed_data_provider); + header.hashPrevBlock = prev_hash; + header.nVersion = fuzzed_data_provider.ConsumeIntegral<int32_t>(); + return header; +} + +CBlock ConsumeBlock(FuzzedDataProvider& fuzzed_data_provider, const uint256& prev_hash, uint32_t prev_nbits) +{ + auto header = ConsumeHeader(fuzzed_data_provider, prev_hash, prev_nbits); + // In order to reach the headers acceptance logic, the block is + // constructed in a way that will pass the mutation checks. + CBlock block{header}; + CMutableTransaction tx; + tx.vin.resize(1); + tx.vout.resize(1); + tx.vout[0].nValue = 0; + tx.vin[0].scriptSig.resize(2); + block.vtx.push_back(MakeTransactionRef(tx)); + block.hashMerkleRoot = block.vtx[0]->GetHash(); + return block; +} + +void FinalizeHeader(CBlockHeader& header) +{ + while (!CheckProofOfWork(header.GetHash(), header.nBits, Params().GetConsensus())) { + ++(header.nNonce); + } +} + +// Global setup works for this test as state modification (specifically in the +// block index) would indicate a bug. +HeadersSyncSetup* g_testing_setup; + +void initialize() +{ + static auto setup = MakeNoLogFileContext<HeadersSyncSetup>(ChainType::MAIN, {.extra_args = {"-checkpoints=0"}}); + g_testing_setup = setup.get(); +} +} // namespace + +FUZZ_TARGET(p2p_headers_presync, .init = initialize) +{ + ChainstateManager& chainman = *g_testing_setup->m_node.chainman; + + LOCK(NetEventsInterface::g_msgproc_mutex); + + g_testing_setup->ResetAndInitialize(); + + FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; + + CBlockHeader base{Params().GenesisBlock()}; + SetMockTime(base.nTime); + + // The chain is just a single block, so this is equal to 1 + size_t original_index_size{WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size())}; + + LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) + { + auto finalized_block = [&]() { + CBlock block = ConsumeBlock(fuzzed_data_provider, base.GetHash(), base.nBits); + FinalizeHeader(block); + return block; + }; + + // Send low-work headers, compact blocks, and blocks + CallOneOf( + fuzzed_data_provider, + [&]() NO_THREAD_SAFETY_ANALYSIS { + // Send FUZZ_MAX_HEADERS_RESULTS headers + std::vector<CBlock> headers; + headers.resize(FUZZ_MAX_HEADERS_RESULTS); + for (CBlock& header : headers) { + header = ConsumeHeader(fuzzed_data_provider, base.GetHash(), base.nBits); + FinalizeHeader(header); + base = header; + } + + auto headers_msg = NetMsg::Make(NetMsgType::HEADERS, TX_WITH_WITNESS(headers)); + g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg)); + }, + [&]() NO_THREAD_SAFETY_ANALYSIS { + // Send a compact block + auto block = finalized_block(); + CBlockHeaderAndShortTxIDs cmpct_block{block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()}; + + auto headers_msg = NetMsg::Make(NetMsgType::CMPCTBLOCK, TX_WITH_WITNESS(cmpct_block)); + g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg)); + }, + [&]() NO_THREAD_SAFETY_ANALYSIS { + // Send a block + auto block = finalized_block(); + + auto headers_msg = NetMsg::Make(NetMsgType::BLOCK, TX_WITH_WITNESS(block)); + g_testing_setup->SendMessage(fuzzed_data_provider, std::move(headers_msg)); + }); + } + + // The headers/blocks sent in this test should never be stored, as the chains don't have the work required + // to meet the anti-DoS work threshold. So, if at any point the block index grew in size, then there's a bug + // in the headers pre-sync logic. + assert(WITH_LOCK(cs_main, return chainman.m_blockman.m_block_index.size()) == original_index_size); + + g_testing_setup->m_node.validation_signals->SyncWithValidationInterfaceQueue(); +} diff --git a/src/test/fuzz/p2p_transport_serialization.cpp b/src/test/fuzz/p2p_transport_serialization.cpp index 93f77b6e5b..cf3ef45c0a 100644 --- a/src/test/fuzz/p2p_transport_serialization.cpp +++ b/src/test/fuzz/p2p_transport_serialization.cpp @@ -12,6 +12,7 @@ #include <test/fuzz/util.h> #include <util/chaintype.h> +#include <algorithm> #include <cassert> #include <cstdint> #include <limits> @@ -185,12 +186,12 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa // Compare with expected more. if (expect_more[side].has_value()) assert(!bytes.empty() == *expect_more[side]); // Verify consistency between the two results. - assert(bytes == bytes_next); + assert(std::ranges::equal(bytes, bytes_next)); assert(msg_type == msg_type_next); if (more_nonext) assert(more_next); // Compare with previously reported output. assert(to_send[side].size() <= bytes.size()); - assert(to_send[side] == Span{bytes}.first(to_send[side].size())); + assert(std::ranges::equal(to_send[side], Span{bytes}.first(to_send[side].size()))); to_send[side].resize(bytes.size()); std::copy(bytes.begin(), bytes.end(), to_send[side].begin()); // Remember 'more' results. @@ -278,7 +279,7 @@ void SimulationTest(Transport& initiator, Transport& responder, R& rng, FuzzedDa // The m_type must match what is expected. assert(received.m_type == expected[side].front().m_type); // The data must match what is expected. - assert(MakeByteSpan(received.m_recv) == MakeByteSpan(expected[side].front().data)); + assert(std::ranges::equal(received.m_recv, MakeByteSpan(expected[side].front().data))); expected[side].pop_front(); progress = true; } diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp index a4e1947b9f..2942740395 100644 --- a/src/test/fuzz/policy_estimator.cpp +++ b/src/test/fuzz/policy_estimator.cpp @@ -85,9 +85,18 @@ FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator) }); (void)block_policy_estimator.estimateFee(fuzzed_data_provider.ConsumeIntegral<int>()); EstimationResult result; - (void)block_policy_estimator.estimateRawFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeFloatingPoint<double>(), fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS), fuzzed_data_provider.ConsumeBool() ? &result : nullptr); + auto conf_target = fuzzed_data_provider.ConsumeIntegral<int>(); + auto success_threshold = fuzzed_data_provider.ConsumeFloatingPoint<double>(); + auto horizon = fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS); + auto* result_ptr = fuzzed_data_provider.ConsumeBool() ? &result : nullptr; + (void)block_policy_estimator.estimateRawFee(conf_target, success_threshold, horizon, result_ptr); + FeeCalculation fee_calculation; - (void)block_policy_estimator.estimateSmartFee(fuzzed_data_provider.ConsumeIntegral<int>(), fuzzed_data_provider.ConsumeBool() ? &fee_calculation : nullptr, fuzzed_data_provider.ConsumeBool()); + conf_target = fuzzed_data_provider.ConsumeIntegral<int>(); + auto* fee_calc_ptr = fuzzed_data_provider.ConsumeBool() ? &fee_calculation : nullptr; + auto conservative = fuzzed_data_provider.ConsumeBool(); + (void)block_policy_estimator.estimateSmartFee(conf_target, fee_calc_ptr, conservative); + (void)block_policy_estimator.HighestTargetTracked(fuzzed_data_provider.PickValueInArray(ALL_FEE_ESTIMATE_HORIZONS)); } { diff --git a/src/test/fuzz/poolresource.cpp b/src/test/fuzz/poolresource.cpp index 28bf7175c0..dd8d5b07e5 100644 --- a/src/test/fuzz/poolresource.cpp +++ b/src/test/fuzz/poolresource.cpp @@ -78,7 +78,7 @@ public: { std::vector<std::byte> expect(entry.span.size()); InsecureRandomContext(entry.seed).fillrand(expect); - assert(entry.span == expect); + assert(std::ranges::equal(entry.span, expect)); } void Deallocate(const Entry& entry) diff --git a/src/test/fuzz/pow.cpp b/src/test/fuzz/pow.cpp index 05cdb740e4..dba999ce4f 100644 --- a/src/test/fuzz/pow.cpp +++ b/src/test/fuzz/pow.cpp @@ -80,7 +80,7 @@ FUZZ_TARGET(pow, .init = initialize_pow) { const std::optional<uint256> hash = ConsumeDeserializable<uint256>(fuzzed_data_provider); if (hash) { - (void)CheckProofOfWork(*hash, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), consensus_params); + (void)CheckProofOfWorkImpl(*hash, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), consensus_params); } } } diff --git a/src/test/fuzz/prevector.cpp b/src/test/fuzz/prevector.cpp index aeceb38a58..fffa099391 100644 --- a/src/test/fuzz/prevector.cpp +++ b/src/test/fuzz/prevector.cpp @@ -210,15 +210,20 @@ FUZZ_TARGET(prevector) LIMITED_WHILE(prov.remaining_bytes(), 3000) { switch (prov.ConsumeIntegralInRange<int>(0, 13 + 3 * (test.size() > 0))) { - case 0: - test.insert(prov.ConsumeIntegralInRange<size_t>(0, test.size()), prov.ConsumeIntegral<int>()); - break; + case 0: { + auto position = prov.ConsumeIntegralInRange<size_t>(0, test.size()); + auto value = prov.ConsumeIntegral<int>(); + test.insert(position, value); + } break; case 1: test.resize(std::max(0, std::min(30, (int)test.size() + prov.ConsumeIntegralInRange<int>(0, 4) - 2))); break; - case 2: - test.insert(prov.ConsumeIntegralInRange<size_t>(0, test.size()), 1 + prov.ConsumeBool(), prov.ConsumeIntegral<int>()); - break; + case 2: { + auto position = prov.ConsumeIntegralInRange<size_t>(0, test.size()); + auto count = 1 + prov.ConsumeBool(); + auto value = prov.ConsumeIntegral<int>(); + test.insert(position, count, value); + } break; case 3: { int del = prov.ConsumeIntegralInRange<int>(0, test.size()); int beg = prov.ConsumeIntegralInRange<int>(0, test.size() - del); @@ -255,9 +260,11 @@ FUZZ_TARGET(prevector) case 9: test.clear(); break; - case 10: - test.assign(prov.ConsumeIntegralInRange<size_t>(0, 32767), prov.ConsumeIntegral<int>()); - break; + case 10: { + auto n = prov.ConsumeIntegralInRange<size_t>(0, 32767); + auto value = prov.ConsumeIntegral<int>(); + test.assign(n, value); + } break; case 11: test.swap(); break; @@ -267,9 +274,11 @@ FUZZ_TARGET(prevector) case 13: test.move(); break; - case 14: - test.update(prov.ConsumeIntegralInRange<size_t>(0, test.size() - 1), prov.ConsumeIntegral<int>()); - break; + case 14: { + auto pos = prov.ConsumeIntegralInRange<size_t>(0, test.size() - 1); + auto value = prov.ConsumeIntegral<int>(); + test.update(pos, value); + } break; case 15: test.erase(prov.ConsumeIntegralInRange<size_t>(0, test.size() - 1)); break; diff --git a/src/test/fuzz/script_format.cpp b/src/test/fuzz/script_format.cpp index 10150dcd7f..e26c42ae38 100644 --- a/src/test/fuzz/script_format.cpp +++ b/src/test/fuzz/script_format.cpp @@ -30,5 +30,7 @@ FUZZ_TARGET(script_format, .init = initialize_script_format) (void)ScriptToAsmStr(script, /*fAttemptSighashDecode=*/fuzzed_data_provider.ConsumeBool()); UniValue o1(UniValue::VOBJ); - ScriptToUniv(script, /*out=*/o1, /*include_hex=*/fuzzed_data_provider.ConsumeBool(), /*include_address=*/fuzzed_data_provider.ConsumeBool()); + auto include_hex = fuzzed_data_provider.ConsumeBool(); + auto include_address = fuzzed_data_provider.ConsumeBool(); + ScriptToUniv(script, /*out=*/o1, include_hex, include_address); } diff --git a/src/test/fuzz/script_interpreter.cpp b/src/test/fuzz/script_interpreter.cpp index 5e76443abe..9e3ad02b2e 100644 --- a/src/test/fuzz/script_interpreter.cpp +++ b/src/test/fuzz/script_interpreter.cpp @@ -25,12 +25,18 @@ FUZZ_TARGET(script_interpreter) const CTransaction tx_to{*mtx}; const unsigned int in = fuzzed_data_provider.ConsumeIntegral<unsigned int>(); if (in < tx_to.vin.size()) { - (void)SignatureHash(script_code, tx_to, in, fuzzed_data_provider.ConsumeIntegral<int>(), ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}), nullptr); + auto n_hash_type = fuzzed_data_provider.ConsumeIntegral<int>(); + auto amount = ConsumeMoney(fuzzed_data_provider); + auto sigversion = fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}); + (void)SignatureHash(script_code, tx_to, in, n_hash_type, amount, sigversion, nullptr); const std::optional<CMutableTransaction> mtx_precomputed = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS); if (mtx_precomputed) { const CTransaction tx_precomputed{*mtx_precomputed}; const PrecomputedTransactionData precomputed_transaction_data{tx_precomputed}; - (void)SignatureHash(script_code, tx_to, in, fuzzed_data_provider.ConsumeIntegral<int>(), ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}), &precomputed_transaction_data); + n_hash_type = fuzzed_data_provider.ConsumeIntegral<int>(); + amount = ConsumeMoney(fuzzed_data_provider); + sigversion = fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}); + (void)SignatureHash(script_code, tx_to, in, n_hash_type, amount, sigversion, &precomputed_transaction_data); } } } diff --git a/src/test/fuzz/script_sign.cpp b/src/test/fuzz/script_sign.cpp index 4695bc611b..7725ee699c 100644 --- a/src/test/fuzz/script_sign.cpp +++ b/src/test/fuzz/script_sign.cpp @@ -111,7 +111,10 @@ FUZZ_TARGET(script_sign, .init = initialize_script_sign) } if (n_in < script_tx_to.vin.size()) { SignatureData empty; - (void)SignSignature(provider, ConsumeScript(fuzzed_data_provider), script_tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>(), empty); + auto from_pub_key = ConsumeScript(fuzzed_data_provider); + auto amount = ConsumeMoney(fuzzed_data_provider); + auto n_hash_type = fuzzed_data_provider.ConsumeIntegral<int>(); + (void)SignSignature(provider, from_pub_key, script_tx_to, n_in, amount, n_hash_type, empty); MutableTransactionSignatureCreator signature_creator{tx_to, n_in, ConsumeMoney(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<int>()}; std::vector<unsigned char> vch_sig; CKeyID address; @@ -122,7 +125,9 @@ FUZZ_TARGET(script_sign, .init = initialize_script_sign) } else { address = CKeyID{ConsumeUInt160(fuzzed_data_provider)}; } - (void)signature_creator.CreateSig(provider, vch_sig, address, ConsumeScript(fuzzed_data_provider), fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0})); + auto script_code = ConsumeScript(fuzzed_data_provider); + auto sigversion = fuzzed_data_provider.PickValueInArray({SigVersion::BASE, SigVersion::WITNESS_V0}); + (void)signature_creator.CreateSig(provider, vch_sig, address, script_code, sigversion); } std::map<COutPoint, Coin> coins{ConsumeCoins(fuzzed_data_provider)}; std::map<int, bilingual_str> input_errors; diff --git a/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp index 0435626356..9f84ac9713 100644 --- a/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp +++ b/src/test/fuzz/secp256k1_ec_seckey_import_export_der.cpp @@ -17,7 +17,7 @@ int ec_seckey_export_der(const secp256k1_context* ctx, unsigned char* seckey, si FUZZ_TARGET(secp256k1_ec_seckey_import_export_der) { FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; - secp256k1_context* secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context* secp256k1_context_sign = secp256k1_context_create(SECP256K1_CONTEXT_NONE); { std::vector<uint8_t> out32(32); (void)ec_seckey_import_der(secp256k1_context_sign, out32.data(), ConsumeFixedLengthByteVector(fuzzed_data_provider, CKey::SIZE).data(), CKey::SIZE); diff --git a/src/test/fuzz/socks5.cpp b/src/test/fuzz/socks5.cpp index af81fcb593..17d1787586 100644 --- a/src/test/fuzz/socks5.cpp +++ b/src/test/fuzz/socks5.cpp @@ -41,8 +41,8 @@ FUZZ_TARGET(socks5, .init = initialize_socks5) FuzzedSock fuzzed_sock = ConsumeSock(fuzzed_data_provider); // This Socks5(...) fuzzing harness would have caught CVE-2017-18350 within // a few seconds of fuzzing. - (void)Socks5(fuzzed_data_provider.ConsumeRandomLengthString(512), - fuzzed_data_provider.ConsumeIntegral<uint16_t>(), - fuzzed_data_provider.ConsumeBool() ? &proxy_credentials : nullptr, - fuzzed_sock); + auto str_dest = fuzzed_data_provider.ConsumeRandomLengthString(512); + auto port = fuzzed_data_provider.ConsumeIntegral<uint16_t>(); + auto* auth = fuzzed_data_provider.ConsumeBool() ? &proxy_credentials : nullptr; + (void)Socks5(str_dest, port, auth, fuzzed_sock); } diff --git a/src/test/fuzz/span.cpp b/src/test/fuzz/span.cpp index 8f753948df..cd436d582f 100644 --- a/src/test/fuzz/span.cpp +++ b/src/test/fuzz/span.cpp @@ -30,10 +30,4 @@ FUZZ_TARGET(span) (void)span.subspan(idx, span.size() - idx); (void)span[idx]; } - - std::string another_str = fuzzed_data_provider.ConsumeBytesAsString(32); - const Span<const char> another_span{another_str}; - assert((span <= another_span) != (span > another_span)); - assert((span == another_span) != (span != another_span)); - assert((span >= another_span) != (span < another_span)); } diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index 73ae89b52a..2ab5b7ed39 100644 --- a/src/test/fuzz/system.cpp +++ b/src/test/fuzz/system.cpp @@ -44,23 +44,31 @@ FUZZ_TARGET(system, .init = initialize_system) args_manager.SelectConfigNetwork(fuzzed_data_provider.ConsumeRandomLengthString(16)); }, [&] { - args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); + auto str_arg = fuzzed_data_provider.ConsumeRandomLengthString(16); + auto str_value = fuzzed_data_provider.ConsumeRandomLengthString(16); + args_manager.SoftSetArg(str_arg, str_value); }, [&] { - args_manager.ForceSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); + auto str_arg = fuzzed_data_provider.ConsumeRandomLengthString(16); + auto str_value = fuzzed_data_provider.ConsumeRandomLengthString(16); + args_manager.ForceSetArg(str_arg, str_value); }, [&] { - args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool()); + auto str_arg = fuzzed_data_provider.ConsumeRandomLengthString(16); + auto f_value = fuzzed_data_provider.ConsumeBool(); + args_manager.SoftSetBoolArg(str_arg, f_value); }, [&] { - const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray<OptionsCategory>({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::HIDDEN}); + const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray<OptionsCategory>({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::CLI_COMMANDS, OptionsCategory::IPC, OptionsCategory::HIDDEN}); // Avoid hitting: // common/args.cpp:563: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16)); if (args_manager.GetArgFlags(argument_name) != std::nullopt) { return; } - args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral<unsigned int>() & ~ArgsManager::COMMAND, options_category); + auto help = fuzzed_data_provider.ConsumeRandomLengthString(16); + auto flags = fuzzed_data_provider.ConsumeIntegral<unsigned int>() & ~ArgsManager::COMMAND; + args_manager.AddArg(argument_name, help, flags, options_category); }, [&] { // Avoid hitting: diff --git a/src/test/fuzz/util/CMakeLists.txt b/src/test/fuzz/util/CMakeLists.txt new file mode 100644 index 0000000000..f73a1a83c2 --- /dev/null +++ b/src/test/fuzz/util/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) 2023-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/license/mit/. + +add_library(test_fuzz STATIC EXCLUDE_FROM_ALL + descriptor.cpp + mempool.cpp + net.cpp + ../fuzz.cpp + ../util.cpp +) + +target_link_libraries(test_fuzz + PRIVATE + core_interface + test_util + bitcoin_node + Boost::headers +) + +if(NOT FUZZ_BINARY_LINKS_WITHOUT_MAIN_FUNCTION) + target_compile_definitions(test_fuzz PRIVATE PROVIDE_FUZZ_MAIN_FUNCTION) +endif() diff --git a/src/test/fuzz/util/net.cpp b/src/test/fuzz/util/net.cpp index ca0fd65cae..b02c4edbad 100644 --- a/src/test/fuzz/util/net.cpp +++ b/src/test/fuzz/util/net.cpp @@ -414,10 +414,10 @@ bool FuzzedSock::IsConnected(std::string& errmsg) const void FillNode(FuzzedDataProvider& fuzzed_data_provider, ConnmanTestMsg& connman, CNode& node) noexcept { - connman.Handshake(node, - /*successfully_connected=*/fuzzed_data_provider.ConsumeBool(), - /*remote_services=*/ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS), - /*local_services=*/ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS), - /*version=*/fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max()), - /*relay_txs=*/fuzzed_data_provider.ConsumeBool()); + auto successfully_connected = fuzzed_data_provider.ConsumeBool(); + auto remote_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); + auto local_services = ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS); + auto version = fuzzed_data_provider.ConsumeIntegralInRange<int32_t>(MIN_PEER_PROTO_VERSION, std::numeric_limits<int32_t>::max()); + auto relay_txs = fuzzed_data_provider.ConsumeBool(); + connman.Handshake(node, successfully_connected, remote_services, local_services, version, relay_txs); } diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp index 21c305e222..1241bba8be 100644 --- a/src/test/fuzz/utxo_snapshot.cpp +++ b/src/test/fuzz/utxo_snapshot.cpp @@ -58,7 +58,7 @@ void initialize_chain() auto& chainman{*setup->m_node.chainman}; for (const auto& block : chain) { BlockValidationState dummy; - bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)}; + bool processed{chainman.ProcessNewBlockHeaders({{block->GetBlockHeader()}}, true, dummy)}; Assert(processed); const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))}; Assert(index); @@ -137,7 +137,7 @@ void utxo_snapshot_fuzz(FuzzBufferType buffer) if constexpr (!INVALID) { for (const auto& block : *g_chain) { BlockValidationState dummy; - bool processed{chainman.ProcessNewBlockHeaders({*block}, true, dummy)}; + bool processed{chainman.ProcessNewBlockHeaders({{block->GetBlockHeader()}}, true, dummy)}; Assert(processed); const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))}; Assert(index); |