aboutsummaryrefslogtreecommitdiff
path: root/src/test/fuzz
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/fuzz')
-rw-r--r--src/test/fuzz/addrman.cpp5
-rw-r--r--src/test/fuzz/banman.cpp2
-rw-r--r--src/test/fuzz/coins_view.cpp10
-rw-r--r--src/test/fuzz/connman.cpp10
-rwxr-xr-xsrc/test/fuzz/danger_link_all.sh28
-rw-r--r--src/test/fuzz/fee_rate.cpp4
-rw-r--r--src/test/fuzz/fuzz.cpp21
-rw-r--r--src/test/fuzz/i2p.cpp4
-rw-r--r--src/test/fuzz/integer.cpp4
-rw-r--r--src/test/fuzz/net_permissions.cpp4
-rw-r--r--src/test/fuzz/parse_iso8601.cpp2
-rw-r--r--src/test/fuzz/process_message.cpp1
-rw-r--r--src/test/fuzz/process_messages.cpp1
-rw-r--r--src/test/fuzz/psbt.cpp23
-rw-r--r--src/test/fuzz/rpc.cpp359
-rw-r--r--src/test/fuzz/script.cpp56
-rw-r--r--src/test/fuzz/script_assets_test_minimizer.cpp21
-rw-r--r--src/test/fuzz/script_flags.cpp6
-rw-r--r--src/test/fuzz/script_ops.cpp61
-rw-r--r--src/test/fuzz/strprintf.cpp61
-rw-r--r--src/test/fuzz/timedata.cpp4
-rw-r--r--src/test/fuzz/transaction.cpp14
-rw-r--r--src/test/fuzz/tx_pool.cpp80
-rw-r--r--src/test/fuzz/util.cpp207
-rw-r--r--src/test/fuzz/util.h204
-rw-r--r--src/test/fuzz/utxo_snapshot.cpp87
-rw-r--r--src/test/fuzz/versionbits.cpp63
27 files changed, 932 insertions, 410 deletions
diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp
index 0baf30aef6..98ae32a8d0 100644
--- a/src/test/fuzz/addrman.cpp
+++ b/src/test/fuzz/addrman.cpp
@@ -60,7 +60,10 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman)
(void)addr_man.Select(fuzzed_data_provider.ConsumeBool());
},
[&] {
- (void)addr_man.GetAddr(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096));
+ (void)addr_man.GetAddr(
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
+ /* network */ std::nullopt);
},
[&] {
const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp
index 8bf484722c..4a04eed463 100644
--- a/src/test/fuzz/banman.cpp
+++ b/src/test/fuzz/banman.cpp
@@ -34,7 +34,7 @@ FUZZ_TARGET_INIT(banman, initialize_banman)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
- const fs::path banlist_file = GetDataDir() / "fuzzed_banlist.dat";
+ const fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist.dat";
fs::remove(banlist_file);
{
BanMan ban_man{banlist_file, nullptr, ConsumeBanTimeOffset(fuzzed_data_provider)};
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index b21d2eae79..878b5a27da 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -6,6 +6,7 @@
#include <chainparams.h>
#include <chainparamsbase.h>
#include <coins.h>
+#include <consensus/tx_check.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <key.h>
@@ -230,6 +231,11 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
// consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed.
return;
}
+ TxValidationState dummy;
+ if (!CheckTransaction(transaction, dummy)) {
+ // It is not allowed to call CheckTxInputs if CheckTransaction failed
+ return;
+ }
(void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out);
assert(MoneyRange(tx_fee_out));
},
@@ -258,10 +264,10 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view)
(void)GetTransactionSigOpCost(transaction, coins_view_cache, flags);
},
[&] {
- CCoinsStats stats;
+ CCoinsStats stats{CoinStatsHashType::HASH_SERIALIZED};
bool expected_code_path = false;
try {
- (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats, CoinStatsHashType::HASH_SERIALIZED);
+ (void)GetUTXOStats(&coins_view_cache, WITH_LOCK(::cs_main, return std::ref(g_chainman.m_blockman)), stats);
} catch (const std::logic_error&) {
expected_code_path = true;
}
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index acfd5f9797..bbec5943af 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -68,10 +68,16 @@ FUZZ_TARGET_INIT(connman, initialize_connman)
(void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral<NodeId>(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); });
},
[&] {
- (void)connman.GetAddresses(fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>());
+ (void)connman.GetAddresses(
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegral<size_t>(),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegral<size_t>(),
+ /* network */ std::nullopt);
},
[&] {
- (void)connman.GetAddresses(random_node, fuzzed_data_provider.ConsumeIntegral<size_t>(), fuzzed_data_provider.ConsumeIntegral<size_t>());
+ (void)connman.GetAddresses(
+ /* requestor */ random_node,
+ /* max_addresses */ fuzzed_data_provider.ConsumeIntegral<size_t>(),
+ /* max_pct */ fuzzed_data_provider.ConsumeIntegral<size_t>());
},
[&] {
(void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
diff --git a/src/test/fuzz/danger_link_all.sh b/src/test/fuzz/danger_link_all.sh
deleted file mode 100755
index 2ddd00c658..0000000000
--- a/src/test/fuzz/danger_link_all.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-# Copyright (c) 2020 The Bitcoin Core developers
-# Distributed under the MIT software license, see the accompanying
-# file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-export LC_ALL=C.UTF-8
-
-set -e
-
-ROOT_DIR="$(git rev-parse --show-toplevel)"
-
-# Run only once (break make recursion)
-if [ -d "${ROOT_DIR}/lock_fuzz_link_all" ]; then
- exit
-fi
-mkdir "${ROOT_DIR}/lock_fuzz_link_all"
-
-echo "Linking each fuzz target separately."
-for FUZZING_HARNESS in $(PRINT_ALL_FUZZ_TARGETS_AND_ABORT=1 "${ROOT_DIR}/src/test/fuzz/fuzz" | sort -u); do
- echo "Building src/test/fuzz/${FUZZING_HARNESS} ..."
- git checkout -- "${ROOT_DIR}/src/test/fuzz/fuzz.cpp"
- sed -i "s/std::getenv(\"FUZZ\")/\"${FUZZING_HARNESS}\"/g" "${ROOT_DIR}/src/test/fuzz/fuzz.cpp"
- make
- mv "${ROOT_DIR}/src/test/fuzz/fuzz" "${ROOT_DIR}/src/test/fuzz/${FUZZING_HARNESS}"
-done
-git checkout -- "${ROOT_DIR}/src/test/fuzz/fuzz.cpp"
-rmdir "${ROOT_DIR}/lock_fuzz_link_all"
-echo "Successfully built all fuzz targets."
diff --git a/src/test/fuzz/fee_rate.cpp b/src/test/fuzz/fee_rate.cpp
index 2955213635..dff0e58000 100644
--- a/src/test/fuzz/fee_rate.cpp
+++ b/src/test/fuzz/fee_rate.cpp
@@ -20,8 +20,8 @@ FUZZ_TARGET(fee_rate)
const CFeeRate fee_rate{satoshis_per_k};
(void)fee_rate.GetFeePerK();
- const size_t bytes = fuzzed_data_provider.ConsumeIntegral<size_t>();
- if (!MultiplicationOverflow(static_cast<int64_t>(bytes), satoshis_per_k) && bytes <= static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+ const auto bytes = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
+ if (!MultiplicationOverflow(int64_t{bytes}, satoshis_per_k)) {
(void)fee_rate.GetFee(bytes);
}
(void)fee_rate.ToString();
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 1fab46ff13..631c861bb6 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -4,10 +4,15 @@
#include <test/fuzz/fuzz.h>
+#include <netaddress.h>
+#include <netbase.h>
#include <test/util/setup_common.h>
#include <util/check.h>
+#include <util/sock.h>
#include <cstdint>
+#include <exception>
+#include <memory>
#include <unistd.h>
#include <vector>
@@ -29,13 +34,27 @@ static TypeTestOneInput* g_test_one_input{nullptr};
void initialize()
{
+ // Terminate immediately if a fuzzing harness ever tries to create a TCP socket.
+ CreateSock = [](const CService&) -> std::unique_ptr<Sock> { std::terminate(); };
+
+ bool should_abort{false};
if (std::getenv("PRINT_ALL_FUZZ_TARGETS_AND_ABORT")) {
for (const auto& t : FuzzTargets()) {
if (std::get<2>(t.second)) continue;
std::cout << t.first << std::endl;
}
- Assert(false);
+ should_abort = true;
+ }
+ if (const char* out_path = std::getenv("WRITE_ALL_FUZZ_TARGETS_AND_ABORT")) {
+ std::cout << "Writing all fuzz target names to '" << out_path << "'." << std::endl;
+ std::ofstream out_stream(out_path, std::ios::binary);
+ for (const auto& t : FuzzTargets()) {
+ if (std::get<2>(t.second)) continue;
+ out_stream << t.first << std::endl;
+ }
+ should_abort = true;
}
+ Assert(!should_abort);
std::string_view fuzz_target{Assert(std::getenv("FUZZ"))};
const auto it = FuzzTargets().find(fuzz_target);
Assert(it != FuzzTargets().end());
diff --git a/src/test/fuzz/i2p.cpp b/src/test/fuzz/i2p.cpp
index 345d68502a..fb6d23aca5 100644
--- a/src/test/fuzz/i2p.cpp
+++ b/src/test/fuzz/i2p.cpp
@@ -30,14 +30,14 @@ FUZZ_TARGET_INIT(i2p, initialize_i2p)
const CService sam_proxy;
CThreadInterrupt interrupt;
- i2p::sam::Session sess{GetDataDir() / "fuzzed_i2p_private_key", sam_proxy, &interrupt};
+ i2p::sam::Session sess{gArgs.GetDataDirNet() / "fuzzed_i2p_private_key", sam_proxy, &interrupt};
i2p::Connection conn;
if (sess.Listen(conn)) {
if (sess.Accept(conn)) {
try {
- conn.sock->RecvUntilTerminator('\n', 10ms, interrupt, i2p::sam::MAX_MSG_SIZE);
+ (void)conn.sock->RecvUntilTerminator('\n', 10ms, interrupt, i2p::sam::MAX_MSG_SIZE);
} catch (const std::runtime_error&) {
}
}
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index 5bc99ddcb9..e9fa343896 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -122,10 +122,6 @@ FUZZ_TARGET_INIT(integer, initialize_integer)
assert(dynamic_usage == incremental_dynamic_usage * i64s.size());
}
(void)MillisToTimeval(i64);
- const double d = ser_uint64_to_double(u64);
- assert(ser_double_to_uint64(d) == u64);
- const float f = ser_uint32_to_float(u32);
- assert(ser_float_to_uint32(f) == u32);
(void)SighashToStr(uch);
(void)SipHashUint256(u64, u64, u256);
(void)SipHashUint256Extra(u64, u64, u256, u32);
diff --git a/src/test/fuzz/net_permissions.cpp b/src/test/fuzz/net_permissions.cpp
index 544a33047b..6ea79464d0 100644
--- a/src/test/fuzz/net_permissions.cpp
+++ b/src/test/fuzz/net_permissions.cpp
@@ -25,7 +25,7 @@ FUZZ_TARGET(net_permissions)
(void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitebind_permissions.m_flags, net_permission_flags);
assert(NetPermissions::HasFlag(net_whitebind_permissions.m_flags, net_permission_flags));
- (void)NetPermissions::ClearFlag(net_whitebind_permissions.m_flags, net_permission_flags);
+ (void)NetPermissions::ClearFlag(net_whitebind_permissions.m_flags, NetPermissionFlags::Implicit);
(void)NetPermissions::ToStrings(net_whitebind_permissions.m_flags);
}
@@ -35,7 +35,7 @@ FUZZ_TARGET(net_permissions)
(void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags);
(void)NetPermissions::AddFlag(net_whitelist_permissions.m_flags, net_permission_flags);
assert(NetPermissions::HasFlag(net_whitelist_permissions.m_flags, net_permission_flags));
- (void)NetPermissions::ClearFlag(net_whitelist_permissions.m_flags, net_permission_flags);
+ (void)NetPermissions::ClearFlag(net_whitelist_permissions.m_flags, NetPermissionFlags::Implicit);
(void)NetPermissions::ToStrings(net_whitelist_permissions.m_flags);
}
}
diff --git a/src/test/fuzz/parse_iso8601.cpp b/src/test/fuzz/parse_iso8601.cpp
index dcb24ac127..a56f2aa48d 100644
--- a/src/test/fuzz/parse_iso8601.cpp
+++ b/src/test/fuzz/parse_iso8601.cpp
@@ -15,7 +15,7 @@ FUZZ_TARGET(parse_iso8601)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- const int64_t random_time = fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ const int64_t random_time = fuzzed_data_provider.ConsumeIntegral<int32_t>();
const std::string random_string = fuzzed_data_provider.ConsumeRemainingBytesAsString();
const std::string iso8601_datetime = FormatISO8601DateTime(random_time);
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index 96e1cfa08f..7b99193ad0 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -100,7 +100,6 @@ void fuzz_target(FuzzBufferType buffer, const std::string& LIMIT_TO_MESSAGE_TYPE
g_setup->m_node.peerman->SendMessages(&p2p_node);
}
SyncWithValidationInterfaceQueue();
- LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
index 203c0ef8e1..11b236c9bd 100644
--- a/src/test/fuzz/process_messages.cpp
+++ b/src/test/fuzz/process_messages.cpp
@@ -80,6 +80,5 @@ FUZZ_TARGET_INIT(process_messages, initialize_process_messages)
}
}
SyncWithValidationInterfaceQueue();
- LOCK2(::cs_main, g_cs_orphans); // See init.cpp for rationale for implicit locking order requirement
g_setup->m_node.connman->StopNodes();
}
diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp
index d1cc6f9c7e..6c62dd6e48 100644
--- a/src/test/fuzz/psbt.cpp
+++ b/src/test/fuzz/psbt.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <node/psbt.h>
@@ -9,6 +10,7 @@
#include <pubkey.h>
#include <script/script.h>
#include <streams.h>
+#include <util/check.h>
#include <version.h>
#include <cstdint>
@@ -23,10 +25,10 @@ void initialize_psbt()
FUZZ_TARGET_INIT(psbt, initialize_psbt)
{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
PartiallySignedTransaction psbt_mut;
- const std::string raw_psbt{buffer.begin(), buffer.end()};
std::string error;
- if (!DecodeRawPSBT(psbt_mut, raw_psbt, error)) {
+ if (!DecodeRawPSBT(psbt_mut, fuzzed_data_provider.ConsumeRandomLengthString(), error)) {
return;
}
const PartiallySignedTransaction psbt = psbt_mut;
@@ -49,6 +51,7 @@ FUZZ_TARGET_INIT(psbt, initialize_psbt)
(void)PSBTInputSigned(input);
(void)input.IsNull();
}
+ (void)CountPSBTUnsignedInputs(psbt);
for (const PSBTOutput& output : psbt.outputs) {
(void)output.IsNull();
@@ -71,6 +74,20 @@ FUZZ_TARGET_INIT(psbt, initialize_psbt)
const PartiallySignedTransaction psbt_from_tx{result};
}
+ PartiallySignedTransaction psbt_merge;
+ if (!DecodeRawPSBT(psbt_merge, fuzzed_data_provider.ConsumeRandomLengthString(), error)) {
+ psbt_merge = psbt;
+ }
+ psbt_mut = psbt;
+ (void)psbt_mut.Merge(psbt_merge);
+ psbt_mut = psbt;
+ (void)CombinePSBTs(psbt_mut, {psbt_mut, psbt_merge});
psbt_mut = psbt;
- (void)psbt_mut.Merge(psbt);
+ for (unsigned int i = 0; i < psbt_merge.tx->vin.size(); ++i) {
+ (void)psbt_mut.AddInput(psbt_merge.tx->vin[i], psbt_merge.inputs[i]);
+ }
+ for (unsigned int i = 0; i < psbt_merge.tx->vout.size(); ++i) {
+ Assert(psbt_mut.AddOutput(psbt_merge.tx->vout[i], psbt_merge.outputs[i]));
+ }
+ psbt_mut.unknown.insert(psbt_merge.unknown.begin(), psbt_merge.unknown.end());
}
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
new file mode 100644
index 0000000000..9195cc4873
--- /dev/null
+++ b/src/test/fuzz/rpc.cpp
@@ -0,0 +1,359 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <base58.h>
+#include <core_io.h>
+#include <key.h>
+#include <key_io.h>
+#include <node/context.h>
+#include <primitives/block.h>
+#include <primitives/transaction.h>
+#include <psbt.h>
+#include <rpc/blockchain.h>
+#include <rpc/client.h>
+#include <rpc/request.h>
+#include <rpc/server.h>
+#include <rpc/util.h>
+#include <span.h>
+#include <streams.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+#include <tinyformat.h>
+#include <univalue.h>
+#include <util/strencodings.h>
+#include <util/string.h>
+#include <util/time.h>
+
+#include <cstdint>
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace {
+struct RPCFuzzTestingSetup : public TestingSetup {
+ RPCFuzzTestingSetup(const std::string& chain_name, const std::vector<const char*>& extra_args) : TestingSetup{chain_name, extra_args}
+ {
+ }
+
+ UniValue CallRPC(const std::string& rpc_method, const std::vector<std::string>& arguments)
+ {
+ JSONRPCRequest request;
+ request.context = &m_node;
+ request.strMethod = rpc_method;
+ request.params = RPCConvertValues(rpc_method, arguments);
+ return tableRPC.execute(request);
+ }
+
+ std::vector<std::string> GetRPCCommands() const
+ {
+ return tableRPC.listCommands();
+ }
+};
+
+RPCFuzzTestingSetup* rpc_testing_setup = nullptr;
+std::string g_limit_to_rpc_command;
+
+// RPC commands which are not appropriate for fuzzing: such as RPC commands
+// reading or writing to a filename passed as an RPC parameter, RPC commands
+// resulting in network activity, etc.
+const std::vector<std::string> RPC_COMMANDS_NOT_SAFE_FOR_FUZZING{
+ "addconnection", // avoid DNS lookups
+ "addnode", // avoid DNS lookups
+ "addpeeraddress", // avoid DNS lookups
+ "analyzepsbt", // avoid signed integer overflow in CFeeRate::GetFee(unsigned long) (https://github.com/bitcoin/bitcoin/issues/20607)
+ "dumptxoutset", // avoid writing to disk
+ "dumpwallet", // avoid writing to disk
+ "echoipc", // avoid assertion failure (Assertion `"EnsureAnyNodeContext(request.context).init" && check' failed.)
+ "generatetoaddress", // avoid prohibitively slow execution (when `num_blocks` is large)
+ "generatetodescriptor", // avoid prohibitively slow execution (when `nblocks` is large)
+ "gettxoutproof", // avoid prohibitively slow execution
+ "importwallet", // avoid reading from disk
+ "loadwallet", // avoid reading from disk
+ "prioritisetransaction", // avoid signed integer overflow in CTxMemPool::PrioritiseTransaction(uint256 const&, long const&) (https://github.com/bitcoin/bitcoin/issues/20626)
+ "savemempool", // disabled as a precautionary measure: may take a file path argument in the future
+ "setban", // avoid DNS lookups
+ "stop", // avoid shutdown state
+};
+
+// RPC commands which are safe for fuzzing.
+const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
+ "clearbanned",
+ "combinepsbt",
+ "combinerawtransaction",
+ "converttopsbt",
+ "createmultisig",
+ "createpsbt",
+ "createrawtransaction",
+ "decodepsbt",
+ "decoderawtransaction",
+ "decodescript",
+ "deriveaddresses",
+ "disconnectnode",
+ "echo",
+ "echojson",
+ "estimaterawfee",
+ "estimatesmartfee",
+ "finalizepsbt",
+ "generate",
+ "generateblock",
+ "getaddednodeinfo",
+ "getbestblockhash",
+ "getblock",
+ "getblockchaininfo",
+ "getblockcount",
+ "getblockfilter",
+ "getblockhash",
+ "getblockheader",
+ "getblockstats",
+ "getblocktemplate",
+ "getchaintips",
+ "getchaintxstats",
+ "getconnectioncount",
+ "getdescriptorinfo",
+ "getdifficulty",
+ "getindexinfo",
+ "getmemoryinfo",
+ "getmempoolancestors",
+ "getmempooldescendants",
+ "getmempoolentry",
+ "getmempoolinfo",
+ "getmininginfo",
+ "getnettotals",
+ "getnetworkhashps",
+ "getnetworkinfo",
+ "getnodeaddresses",
+ "getpeerinfo",
+ "getrawmempool",
+ "getrawtransaction",
+ "getrpcinfo",
+ "gettxout",
+ "gettxoutsetinfo",
+ "help",
+ "invalidateblock",
+ "joinpsbts",
+ "listbanned",
+ "logging",
+ "mockscheduler",
+ "ping",
+ "preciousblock",
+ "pruneblockchain",
+ "reconsiderblock",
+ "scantxoutset",
+ "sendrawtransaction",
+ "setmocktime",
+ "setnetworkactive",
+ "signmessagewithprivkey",
+ "signrawtransactionwithkey",
+ "submitblock",
+ "submitheader",
+ "syncwithvalidationinterfacequeue",
+ "testmempoolaccept",
+ "uptime",
+ "utxoupdatepsbt",
+ "validateaddress",
+ "verifychain",
+ "verifymessage",
+ "verifytxoutproof",
+ "waitforblock",
+ "waitforblockheight",
+ "waitfornewblock",
+};
+
+std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+{
+ const size_t max_string_length = 4096;
+ const size_t max_base58_bytes_length{64};
+ std::string r;
+ CallOneOf(
+ fuzzed_data_provider,
+ [&] {
+ // string argument
+ r = fuzzed_data_provider.ConsumeRandomLengthString(max_string_length);
+ },
+ [&] {
+ // base64 argument
+ r = EncodeBase64(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
+ },
+ [&] {
+ // hex argument
+ r = HexStr(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
+ },
+ [&] {
+ // bool argument
+ r = fuzzed_data_provider.ConsumeBool() ? "true" : "false";
+ },
+ [&] {
+ // range argument
+ r = "[" + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "," + ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>()) + "]";
+ },
+ [&] {
+ // integral argument (int64_t)
+ r = ToString(fuzzed_data_provider.ConsumeIntegral<int64_t>());
+ },
+ [&] {
+ // integral argument (uint64_t)
+ r = ToString(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ },
+ [&] {
+ // floating point argument
+ r = strprintf("%f", fuzzed_data_provider.ConsumeFloatingPoint<double>());
+ },
+ [&] {
+ // tx destination argument
+ r = EncodeDestination(ConsumeTxDestination(fuzzed_data_provider));
+ },
+ [&] {
+ // uint160 argument
+ r = ConsumeUInt160(fuzzed_data_provider).ToString();
+ },
+ [&] {
+ // uint256 argument
+ r = ConsumeUInt256(fuzzed_data_provider).ToString();
+ },
+ [&] {
+ // base32 argument
+ r = EncodeBase32(fuzzed_data_provider.ConsumeRandomLengthString(max_string_length));
+ },
+ [&] {
+ // base58 argument
+ r = EncodeBase58(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
+ },
+ [&] {
+ // base58 argument with checksum
+ r = EncodeBase58Check(MakeUCharSpan(fuzzed_data_provider.ConsumeRandomLengthString(max_base58_bytes_length)));
+ },
+ [&] {
+ // hex encoded block
+ std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
+ if (!opt_block) {
+ return;
+ }
+ CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
+ data_stream << *opt_block;
+ r = HexStr(data_stream);
+ },
+ [&] {
+ // hex encoded block header
+ std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
+ if (!opt_block_header) {
+ return;
+ }
+ CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
+ data_stream << *opt_block_header;
+ r = HexStr(data_stream);
+ },
+ [&] {
+ // hex encoded tx
+ std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
+ if (!opt_tx) {
+ return;
+ }
+ CDataStream data_stream{SER_NETWORK, fuzzed_data_provider.ConsumeBool() ? PROTOCOL_VERSION : (PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)};
+ data_stream << *opt_tx;
+ r = HexStr(data_stream);
+ },
+ [&] {
+ // base64 encoded psbt
+ std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
+ if (!opt_psbt) {
+ return;
+ }
+ CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
+ data_stream << *opt_psbt;
+ r = EncodeBase64({data_stream.begin(), data_stream.end()});
+ },
+ [&] {
+ // base58 encoded key
+ const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
+ CKey key;
+ key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
+ if (!key.IsValid()) {
+ return;
+ }
+ r = EncodeSecret(key);
+ },
+ [&] {
+ // hex encoded pubkey
+ const std::vector<uint8_t> random_bytes = fuzzed_data_provider.ConsumeBytes<uint8_t>(32);
+ CKey key;
+ key.Set(random_bytes.begin(), random_bytes.end(), fuzzed_data_provider.ConsumeBool());
+ if (!key.IsValid()) {
+ return;
+ }
+ r = HexStr(key.GetPubKey());
+ });
+ return r;
+}
+
+std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+{
+ std::vector<std::string> scalar_arguments;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider));
+ }
+ return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
+}
+
+std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+{
+ return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
+}
+
+RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
+{
+ static const auto setup = MakeNoLogFileContext<RPCFuzzTestingSetup>();
+ SetRPCWarmupFinished();
+ return setup.get();
+}
+}; // namespace
+
+void initialize_rpc()
+{
+ rpc_testing_setup = InitializeRPCFuzzTestingSetup();
+ const std::vector<std::string> supported_rpc_commands = rpc_testing_setup->GetRPCCommands();
+ for (const std::string& rpc_command : supported_rpc_commands) {
+ const bool safe_for_fuzzing = std::find(RPC_COMMANDS_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_SAFE_FOR_FUZZING.end();
+ const bool not_safe_for_fuzzing = std::find(RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_NOT_SAFE_FOR_FUZZING.end();
+ if (!(safe_for_fuzzing || not_safe_for_fuzzing)) {
+ std::cerr << "Error: RPC command \"" << rpc_command << "\" not found in RPC_COMMANDS_SAFE_FOR_FUZZING or RPC_COMMANDS_NOT_SAFE_FOR_FUZZING. Please update " << __FILE__ << ".\n";
+ std::terminate();
+ }
+ if (safe_for_fuzzing && not_safe_for_fuzzing) {
+ std::cerr << "Error: RPC command \"" << rpc_command << "\" found in *both* RPC_COMMANDS_SAFE_FOR_FUZZING and RPC_COMMANDS_NOT_SAFE_FOR_FUZZING. Please update " << __FILE__ << ".\n";
+ std::terminate();
+ }
+ }
+ const char* limit_to_rpc_command_env = std::getenv("LIMIT_TO_RPC_COMMAND");
+ if (limit_to_rpc_command_env != nullptr) {
+ g_limit_to_rpc_command = std::string{limit_to_rpc_command_env};
+ }
+}
+
+FUZZ_TARGET_INIT(rpc, initialize_rpc)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ SetMockTime(ConsumeTime(fuzzed_data_provider));
+ const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
+ if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
+ return;
+ }
+ const bool safe_for_fuzzing = std::find(RPC_COMMANDS_SAFE_FOR_FUZZING.begin(), RPC_COMMANDS_SAFE_FOR_FUZZING.end(), rpc_command) != RPC_COMMANDS_SAFE_FOR_FUZZING.end();
+ if (!safe_for_fuzzing) {
+ return;
+ }
+ std::vector<std::string> arguments;
+ while (fuzzed_data_provider.ConsumeBool()) {
+ arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider));
+ }
+ try {
+ rpc_testing_setup->CallRPC(rpc_command, arguments);
+ } catch (const UniValue&) {
+ } catch (const std::runtime_error&) {
+ }
+}
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index e87ae5b04b..b87bcf2ef5 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -43,7 +43,7 @@ FUZZ_TARGET_INIT(script, initialize_script)
if (!script_opt) return;
const CScript script{*script_opt};
- std::vector<unsigned char> compressed;
+ CompressedScript compressed;
if (CompressScript(script, compressed)) {
const unsigned int size = compressed[0];
compressed.erase(compressed.begin());
@@ -55,22 +55,45 @@ FUZZ_TARGET_INIT(script, initialize_script)
}
CTxDestination address;
- (void)ExtractDestination(script, address);
-
TxoutType type_ret;
std::vector<CTxDestination> addresses;
int required_ret;
- (void)ExtractDestinations(script, type_ret, addresses, required_ret);
-
- const FlatSigningProvider signing_provider;
- (void)InferDescriptor(script, signing_provider);
-
- (void)IsSegWitOutput(signing_provider, script);
-
- (void)IsSolvable(signing_provider, script);
+ bool extract_destinations_ret = ExtractDestinations(script, type_ret, addresses, required_ret);
+ bool extract_destination_ret = ExtractDestination(script, address);
+ if (!extract_destinations_ret) {
+ assert(!extract_destination_ret);
+ if (type_ret == TxoutType::MULTISIG) {
+ assert(addresses.empty() && required_ret == 0);
+ } else {
+ assert(type_ret == TxoutType::PUBKEY ||
+ type_ret == TxoutType::NONSTANDARD ||
+ type_ret == TxoutType::NULL_DATA);
+ }
+ } else {
+ assert(required_ret >= 1 && required_ret <= 16);
+ assert((unsigned long)required_ret == addresses.size());
+ assert(type_ret == TxoutType::MULTISIG || required_ret == 1);
+ }
+ if (type_ret == TxoutType::NONSTANDARD || type_ret == TxoutType::NULL_DATA) {
+ assert(!extract_destinations_ret);
+ }
+ if (!extract_destination_ret) {
+ assert(type_ret == TxoutType::PUBKEY ||
+ type_ret == TxoutType::NONSTANDARD ||
+ type_ret == TxoutType::NULL_DATA ||
+ type_ret == TxoutType::MULTISIG);
+ } else {
+ assert(address == addresses[0]);
+ }
+ if (type_ret == TxoutType::NONSTANDARD ||
+ type_ret == TxoutType::NULL_DATA ||
+ type_ret == TxoutType::MULTISIG) {
+ assert(!extract_destination_ret);
+ }
TxoutType which_type;
bool is_standard_ret = IsStandard(script, which_type);
+ assert(type_ret == which_type);
if (!is_standard_ret) {
assert(which_type == TxoutType::NONSTANDARD ||
which_type == TxoutType::NULL_DATA ||
@@ -87,6 +110,11 @@ FUZZ_TARGET_INIT(script, initialize_script)
which_type == TxoutType::NONSTANDARD);
}
+ const FlatSigningProvider signing_provider;
+ (void)InferDescriptor(script, signing_provider);
+ (void)IsSegWitOutput(signing_provider, script);
+ (void)IsSolvable(signing_provider, script);
+
(void)RecursiveDynamicUsage(script);
std::vector<std::vector<unsigned char>> solutions;
@@ -115,10 +143,12 @@ FUZZ_TARGET_INIT(script, initialize_script)
{
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ CompressedScript compressed_script;
+ compressed_script.assign(bytes.begin(), bytes.end());
// DecompressScript(..., ..., bytes) is not guaranteed to be defined if the bytes vector is too short
- if (bytes.size() >= 32) {
+ if (compressed_script.size() >= 32) {
CScript decompressed_script;
- DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), bytes);
+ DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), compressed_script);
}
}
diff --git a/src/test/fuzz/script_assets_test_minimizer.cpp b/src/test/fuzz/script_assets_test_minimizer.cpp
index 5f07acbcc7..a80338b965 100644
--- a/src/test/fuzz/script_assets_test_minimizer.cpp
+++ b/src/test/fuzz/script_assets_test_minimizer.cpp
@@ -133,8 +133,7 @@ unsigned int ParseScriptFlags(const std::string& str)
std::vector<std::string> words;
boost::algorithm::split(words, str, boost::algorithm::is_any_of(","));
- for (const std::string& word : words)
- {
+ for (const std::string& word : words) {
auto it = FLAG_NAMES.find(word);
if (it == FLAG_NAMES.end()) throw std::runtime_error("Unknown verification flag " + word);
flags |= it->second;
@@ -161,7 +160,7 @@ void Test(const std::string& str)
tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["success"]["witness"]);
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
- MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
+ MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
for (const auto flags : ALL_FLAGS) {
// "final": true tests are valid for all flags. Others are only valid with flags that are
// a subset of test_flags.
@@ -176,7 +175,7 @@ void Test(const std::string& str)
tx.vin[idx].scriptWitness = ScriptWitnessFromJSON(test["failure"]["witness"]);
PrecomputedTransactionData txdata;
txdata.Init(tx, std::vector<CTxOut>(prevouts));
- MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata);
+ MutableTransactionSignatureChecker txcheck(&tx, idx, prevouts[idx].nValue, txdata, MissingDataBehavior::ASSERT_FAIL);
for (const auto flags : ALL_FLAGS) {
// If a test is supposed to fail with test_flags, it should also fail with any superset thereof.
if ((flags & test_flags) == test_flags) {
@@ -186,15 +185,19 @@ void Test(const std::string& str)
}
}
-ECCVerifyHandle handle;
-
-} // namespace
+void test_init()
+{
+ static ECCVerifyHandle handle;
+}
-FUZZ_TARGET_INIT_HIDDEN(script_assets_test_minimizer, FuzzFrameworkEmptyInitFun, /* hidden */ true)
+FUZZ_TARGET_INIT_HIDDEN(script_assets_test_minimizer, test_init, /* hidden */ true)
{
if (buffer.size() < 2 || buffer.back() != '\n' || buffer[buffer.size() - 2] != ',') return;
const std::string str((const char*)buffer.data(), buffer.size() - 2);
try {
Test(str);
- } catch (const std::runtime_error&) {}
+ } catch (const std::runtime_error&) {
+ }
}
+
+} // namespace
diff --git a/src/test/fuzz/script_flags.cpp b/src/test/fuzz/script_flags.cpp
index 387f9c069c..1278dc87d4 100644
--- a/src/test/fuzz/script_flags.cpp
+++ b/src/test/fuzz/script_flags.cpp
@@ -41,6 +41,10 @@ FUZZ_TARGET_INIT(script_flags, initialize_script_flags)
for (unsigned i = 0; i < tx.vin.size(); ++i) {
CTxOut prevout;
ds >> prevout;
+ if (!MoneyRange(prevout.nValue)) {
+ // prevouts should be consensus-valid
+ prevout.nValue = 1;
+ }
spent_outputs.push_back(prevout);
}
PrecomputedTransactionData txdata;
@@ -48,7 +52,7 @@ FUZZ_TARGET_INIT(script_flags, initialize_script_flags)
for (unsigned i = 0; i < tx.vin.size(); ++i) {
const CTxOut& prevout = txdata.m_spent_outputs.at(i);
- const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata};
+ const TransactionSignatureChecker checker{&tx, i, prevout.nValue, txdata, MissingDataBehavior::ASSERT_FAIL};
ScriptError serror;
const bool ret = VerifyScript(tx.vin.at(i).scriptSig, prevout.scriptPubKey, &tx.vin.at(i).scriptWitness, verify_flags, checker, &serror);
diff --git a/src/test/fuzz/script_ops.cpp b/src/test/fuzz/script_ops.cpp
index eb1c808a88..4bc709ed35 100644
--- a/src/test/fuzz/script_ops.cpp
+++ b/src/test/fuzz/script_ops.cpp
@@ -14,55 +14,54 @@
FUZZ_TARGET(script_ops)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
- CScript script = ConsumeScript(fuzzed_data_provider);
+ CScript script_mut = ConsumeScript(fuzzed_data_provider);
while (fuzzed_data_provider.remaining_bytes() > 0) {
CallOneOf(
fuzzed_data_provider,
[&] {
CScript s = ConsumeScript(fuzzed_data_provider);
- script = std::move(s);
+ script_mut = std::move(s);
},
[&] {
const CScript& s = ConsumeScript(fuzzed_data_provider);
- script = s;
+ script_mut = s;
},
[&] {
- script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
+ script_mut << fuzzed_data_provider.ConsumeIntegral<int64_t>();
},
[&] {
- script << ConsumeOpcodeType(fuzzed_data_provider);
+ script_mut << ConsumeOpcodeType(fuzzed_data_provider);
},
[&] {
- script << ConsumeScriptNum(fuzzed_data_provider);
+ script_mut << ConsumeScriptNum(fuzzed_data_provider);
},
[&] {
- script << ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ script_mut << ConsumeRandomLengthByteVector(fuzzed_data_provider);
},
[&] {
- script.clear();
- },
- [&] {
- (void)script.GetSigOpCount(false);
- (void)script.GetSigOpCount(true);
- (void)script.GetSigOpCount(script);
- (void)script.HasValidOps();
- (void)script.IsPayToScriptHash();
- (void)script.IsPayToWitnessScriptHash();
- (void)script.IsPushOnly();
- (void)script.IsUnspendable();
- {
- CScript::const_iterator pc = script.begin();
- opcodetype opcode;
- (void)script.GetOp(pc, opcode);
- std::vector<uint8_t> data;
- (void)script.GetOp(pc, opcode, data);
- (void)script.IsPushOnly(pc);
- }
- {
- int version;
- std::vector<uint8_t> program;
- (void)script.IsWitnessProgram(version, program);
- }
+ script_mut.clear();
});
}
+ const CScript& script = script_mut;
+ (void)script.GetSigOpCount(false);
+ (void)script.GetSigOpCount(true);
+ (void)script.GetSigOpCount(script);
+ (void)script.HasValidOps();
+ (void)script.IsPayToScriptHash();
+ (void)script.IsPayToWitnessScriptHash();
+ (void)script.IsPushOnly();
+ (void)script.IsUnspendable();
+ {
+ CScript::const_iterator pc = script.begin();
+ opcodetype opcode;
+ (void)script.GetOp(pc, opcode);
+ std::vector<uint8_t> data;
+ (void)script.GetOp(pc, opcode, data);
+ (void)script.IsPushOnly(pc);
+ }
+ {
+ int version;
+ std::vector<uint8_t> program;
+ (void)script.IsWitnessProgram(version, program);
+ }
}
diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp
index 2c92b159a5..18de0e1960 100644
--- a/src/test/fuzz/strprintf.cpp
+++ b/src/test/fuzz/strprintf.cpp
@@ -49,67 +49,6 @@ FUZZ_TARGET(str_printf)
// Upstream bug report: https://github.com/c42f/tinyformat/issues/70
try {
- (void)strprintf(format_string, (signed char*)nullptr);
- (void)tinyformat::format(bilingual_string, (signed char*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (unsigned char*)nullptr);
- (void)tinyformat::format(bilingual_string, (unsigned char*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (void*)nullptr);
- (void)tinyformat::format(bilingual_string, (void*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (bool*)nullptr);
- (void)tinyformat::format(bilingual_string, (bool*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (float*)nullptr);
- (void)tinyformat::format(bilingual_string, (float*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (double*)nullptr);
- (void)tinyformat::format(bilingual_string, (double*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (int16_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (int16_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (uint16_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (uint16_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (int32_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (int32_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (uint32_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (uint32_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (int64_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (int64_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
- try {
- (void)strprintf(format_string, (uint64_t*)nullptr);
- (void)tinyformat::format(bilingual_string, (uint64_t*)nullptr);
- } catch (const tinyformat::format_error&) {
- }
-
- try {
CallOneOf(
fuzzed_data_provider,
[&] {
diff --git a/src/test/fuzz/timedata.cpp b/src/test/fuzz/timedata.cpp
index d7fa66298a..f7dc5f433e 100644
--- a/src/test/fuzz/timedata.cpp
+++ b/src/test/fuzz/timedata.cpp
@@ -15,10 +15,12 @@ FUZZ_TARGET(timedata)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const unsigned int max_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
+ // A max_size of 0 implies no limit, so cap the max number of insertions to avoid timeouts
+ auto max_to_insert = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 4000);
// Divide by 2 to avoid signed integer overflow in .median()
const int64_t initial_value = fuzzed_data_provider.ConsumeIntegral<int64_t>() / 2;
CMedianFilter<int64_t> median_filter{max_size, initial_value};
- while (fuzzed_data_provider.remaining_bytes() > 0) {
+ while (fuzzed_data_provider.remaining_bytes() > 0 && --max_to_insert >= 0) {
(void)median_filter.median();
assert(median_filter.size() > 0);
assert(static_cast<size_t>(median_filter.size()) == median_filter.sorted().size());
diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp
index 17e4405a13..ff34cc87b2 100644
--- a/src/test/fuzz/transaction.cpp
+++ b/src/test/fuzz/transaction.cpp
@@ -61,8 +61,11 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction)
return;
}
- TxValidationState state_with_dupe_check;
- (void)CheckTransaction(tx, state_with_dupe_check);
+ {
+ TxValidationState state_with_dupe_check;
+ const bool res{CheckTransaction(tx, state_with_dupe_check)};
+ Assert(res == state_with_dupe_check.IsValid());
+ }
const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE};
std::string reason;
@@ -100,9 +103,6 @@ FUZZ_TARGET_INIT(transaction, initialize_transaction)
(void)IsWitnessStandard(tx, coins_view_cache);
UniValue u(UniValue::VOBJ);
- TxToUniv(tx, /* hashBlock */ {}, /* include_addresses */ true, u);
- TxToUniv(tx, /* hashBlock */ {}, /* include_addresses */ false, u);
- static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
- TxToUniv(tx, u256_max, /* include_addresses */ true, u);
- TxToUniv(tx, u256_max, /* include_addresses */ false, u);
+ TxToUniv(tx, /* hashBlock */ uint256::ZERO, /* include_addresses */ true, u);
+ TxToUniv(tx, /* hashBlock */ uint256::ONE, /* include_addresses */ false, u);
}
diff --git a/src/test/fuzz/tx_pool.cpp b/src/test/fuzz/tx_pool.cpp
index f84d6702a7..ad11f2c5f2 100644
--- a/src/test/fuzz/tx_pool.cpp
+++ b/src/test/fuzz/tx_pool.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <consensus/validation.h>
+#include <miner.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
@@ -20,8 +21,9 @@ std::vector<COutPoint> g_outpoints_coinbase_init_mature;
std::vector<COutPoint> g_outpoints_coinbase_init_immature;
struct MockedTxPool : public CTxMemPool {
- void RollingFeeUpdate()
+ void RollingFeeUpdate() EXCLUSIVE_LOCKS_REQUIRED(!cs)
{
+ LOCK(cs);
lastRollingFeeUpdate = GetTime();
blockSinceLastRollingFeeBump = true;
}
@@ -34,7 +36,7 @@ void initialize_tx_pool()
for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
CTxIn in = MineBlock(g_setup->m_node, P2WSH_OP_TRUE);
- // Remember the txids to avoid expensive disk acess later on
+ // Remember the txids to avoid expensive disk access later on
auto& outpoints = i < COINBASE_MATURITY ?
g_outpoints_coinbase_init_mature :
g_outpoints_coinbase_init_immature;
@@ -77,13 +79,44 @@ void SetMempoolConstraints(ArgsManager& args, FuzzedDataProvider& fuzzed_data_pr
ToString(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, 999)));
}
+void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, CChainState& chainstate)
+{
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ {
+ BlockAssembler::Options options;
+ options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
+ options.blockMinFeeRate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /* max */ COIN)};
+ auto assembler = BlockAssembler{chainstate, *static_cast<CTxMemPool*>(&tx_pool), ::Params(), options};
+ auto block_template = assembler.CreateNewBlock(CScript{} << OP_TRUE);
+ Assert(block_template->block.vtx.size() >= 1);
+ }
+ const auto info_all = tx_pool.infoAll();
+ if (!info_all.empty()) {
+ const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx;
+ WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, /* dummy */ MemPoolRemovalReason::BLOCK));
+ std::vector<uint256> all_txids;
+ tx_pool.queryHashes(all_txids);
+ assert(all_txids.size() < info_all.size());
+ WITH_LOCK(::cs_main, tx_pool.check(chainstate));
+ }
+ SyncWithValidationInterfaceQueue();
+}
+
+void MockTime(FuzzedDataProvider& fuzzed_data_provider, const CChainState& chainstate)
+{
+ const auto time = ConsumeTime(fuzzed_data_provider,
+ chainstate.m_chain.Tip()->GetMedianTimePast() + 1,
+ std::numeric_limits<decltype(chainstate.m_chain.Tip()->nTime)>::max());
+ SetMockTime(time);
+}
+
FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate = node.chainman->ActiveChainstate();
- SetMockTime(ConsumeTime(fuzzed_data_provider));
+ MockTime(fuzzed_data_provider, chainstate);
SetMempoolConstraints(*node.args, fuzzed_data_provider);
// All RBF-spendable outpoints
@@ -163,7 +196,7 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
}();
if (fuzzed_data_provider.ConsumeBool()) {
- SetMockTime(ConsumeTime(fuzzed_data_provider));
+ MockTime(fuzzed_data_provider, chainstate);
}
if (fuzzed_data_provider.ConsumeBool()) {
SetMempoolConstraints(*node.args, fuzzed_data_provider);
@@ -237,23 +270,17 @@ FUZZ_TARGET_INIT(tx_pool_standard, initialize_tx_pool)
}
}
}
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
- const auto info_all = tx_pool.infoAll();
- if (!info_all.empty()) {
- const auto& tx_to_remove = *PickValue(fuzzed_data_provider, info_all).tx;
- WITH_LOCK(tx_pool.cs, tx_pool.removeRecursive(tx_to_remove, /* dummy */ MemPoolRemovalReason::BLOCK));
- std::vector<uint256> all_txids;
- tx_pool.queryHashes(all_txids);
- assert(all_txids.size() < info_all.size());
- WITH_LOCK(::cs_main, tx_pool.check(chainstate));
- }
- SyncWithValidationInterfaceQueue();
+ Finish(fuzzed_data_provider, tx_pool, chainstate);
}
FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
+ auto& chainstate = node.chainman->ActiveChainstate();
+
+ MockTime(fuzzed_data_provider, chainstate);
+ SetMempoolConstraints(*node.args, fuzzed_data_provider);
std::vector<uint256> txids;
for (const auto& outpoint : g_outpoints_coinbase_init_mature) {
@@ -265,11 +292,29 @@ FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
txids.push_back(ConsumeUInt256(fuzzed_data_provider));
}
- CTxMemPool tx_pool{/* estimator */ nullptr, /* check_ratio */ 1};
+ CTxMemPool tx_pool_{/* estimator */ nullptr, /* check_ratio */ 1};
+ MockedTxPool& tx_pool = *static_cast<MockedTxPool*>(&tx_pool_);
while (fuzzed_data_provider.ConsumeBool()) {
const auto mut_tx = ConsumeTransaction(fuzzed_data_provider, txids);
+ if (fuzzed_data_provider.ConsumeBool()) {
+ MockTime(fuzzed_data_provider, chainstate);
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ SetMempoolConstraints(*node.args, fuzzed_data_provider);
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ tx_pool.RollingFeeUpdate();
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const auto& txid = fuzzed_data_provider.ConsumeBool() ?
+ mut_tx.GetHash() :
+ PickValue(fuzzed_data_provider, txids);
+ const auto delta = fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(-50 * COIN, +50 * COIN);
+ tx_pool.PrioritiseTransaction(txid, delta);
+ }
+
const auto tx = MakeTransactionRef(mut_tx);
const bool bypass_limits = fuzzed_data_provider.ConsumeBool();
::fRequireStandard = fuzzed_data_provider.ConsumeBool();
@@ -278,8 +323,7 @@ FUZZ_TARGET_INIT(tx_pool, initialize_tx_pool)
if (accepted) {
txids.push_back(tx->GetHash());
}
-
- SyncWithValidationInterfaceQueue();
}
+ Finish(fuzzed_data_provider, tx_pool, chainstate);
}
} // namespace
diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp
index d786ac1db1..bcf0b0ce72 100644
--- a/src/test/fuzz/util.cpp
+++ b/src/test/fuzz/util.cpp
@@ -5,8 +5,198 @@
#include <test/fuzz/util.h>
#include <test/util/script.h>
#include <util/rbf.h>
+#include <util/time.h>
#include <version.h>
+FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
+ : m_fuzzed_data_provider{fuzzed_data_provider}
+{
+ m_socket = fuzzed_data_provider.ConsumeIntegralInRange<SOCKET>(INVALID_SOCKET - 1, INVALID_SOCKET);
+}
+
+FuzzedSock::~FuzzedSock()
+{
+ // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
+ // Sock::Reset() (not FuzzedSock::Reset()!) which will call CloseSocket(m_socket).
+ // Avoid closing an arbitrary file descriptor (m_socket is just a random very high number which
+ // theoretically may concide with a real opened file descriptor).
+ Reset();
+}
+
+FuzzedSock& FuzzedSock::operator=(Sock&& other)
+{
+ assert(false && "Move of Sock into FuzzedSock not allowed.");
+ return *this;
+}
+
+void FuzzedSock::Reset()
+{
+ m_socket = INVALID_SOCKET;
+}
+
+ssize_t FuzzedSock::Send(const void* data, size_t len, int flags) const
+{
+ constexpr std::array send_errnos{
+ EACCES,
+ EAGAIN,
+ EALREADY,
+ EBADF,
+ ECONNRESET,
+ EDESTADDRREQ,
+ EFAULT,
+ EINTR,
+ EINVAL,
+ EISCONN,
+ EMSGSIZE,
+ ENOBUFS,
+ ENOMEM,
+ ENOTCONN,
+ ENOTSOCK,
+ EOPNOTSUPP,
+ EPIPE,
+ EWOULDBLOCK,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ return len;
+ }
+ const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
+ }
+ return r;
+}
+
+ssize_t FuzzedSock::Recv(void* buf, size_t len, int flags) const
+{
+ // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
+ // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
+ // returning -1 and setting errno to EAGAIN repeatedly.
+ constexpr std::array recv_errnos{
+ ECONNREFUSED,
+ EAGAIN,
+ EBADF,
+ EFAULT,
+ EINTR,
+ EINVAL,
+ ENOMEM,
+ ENOTCONN,
+ ENOTSOCK,
+ EWOULDBLOCK,
+ };
+ assert(buf != nullptr || len == 0);
+ if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
+ const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
+ }
+ return r;
+ }
+ std::vector<uint8_t> random_bytes;
+ bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
+ if (m_peek_data.has_value()) {
+ // `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
+ random_bytes.assign({m_peek_data.value()});
+ if ((flags & MSG_PEEK) == 0) {
+ m_peek_data.reset();
+ }
+ pad_to_len_bytes = false;
+ } else if ((flags & MSG_PEEK) != 0) {
+ // New call with `MSG_PEEK`.
+ random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
+ if (!random_bytes.empty()) {
+ m_peek_data = random_bytes[0];
+ pad_to_len_bytes = false;
+ }
+ } else {
+ random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
+ m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
+ }
+ if (random_bytes.empty()) {
+ const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
+ if (r == -1) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
+ }
+ return r;
+ }
+ std::memcpy(buf, random_bytes.data(), random_bytes.size());
+ if (pad_to_len_bytes) {
+ if (len > random_bytes.size()) {
+ std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
+ }
+ return len;
+ }
+ if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
+ std::this_thread::sleep_for(std::chrono::milliseconds{2});
+ }
+ return random_bytes.size();
+}
+
+int FuzzedSock::Connect(const sockaddr*, socklen_t) const
+{
+ // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
+ // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
+ // returning -1 and setting errno to EAGAIN repeatedly.
+ constexpr std::array connect_errnos{
+ ECONNREFUSED,
+ EAGAIN,
+ ECONNRESET,
+ EHOSTUNREACH,
+ EINPROGRESS,
+ EINTR,
+ ENETUNREACH,
+ ETIMEDOUT,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
+ return -1;
+ }
+ return 0;
+}
+
+int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
+{
+ constexpr std::array getsockopt_errnos{
+ ENOMEM,
+ ENOBUFS,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
+ return -1;
+ }
+ if (opt_val == nullptr) {
+ return 0;
+ }
+ std::memcpy(opt_val,
+ ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
+ *opt_len);
+ return 0;
+}
+
+bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
+{
+ constexpr std::array wait_errnos{
+ EBADF,
+ EINTR,
+ EINVAL,
+ };
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ SetFuzzedErrNo(m_fuzzed_data_provider, wait_errnos);
+ return false;
+ }
+ if (occurred != nullptr) {
+ *occurred = m_fuzzed_data_provider.ConsumeBool() ? requested : 0;
+ }
+ return true;
+}
+
+bool FuzzedSock::IsConnected(std::string& errmsg) const
+{
+ if (m_fuzzed_data_provider.ConsumeBool()) {
+ return true;
+ }
+ errmsg = "disconnected at random by the fuzzer";
+ return false;
+}
void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_version) noexcept
{
@@ -27,6 +217,19 @@ void FillNode(FuzzedDataProvider& fuzzed_data_provider, CNode& node, bool init_v
}
}
+CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::optional<CAmount>& max) noexcept
+{
+ return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, max.value_or(MAX_MONEY));
+}
+
+int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider, const std::optional<int64_t>& min, const std::optional<int64_t>& max) noexcept
+{
+ // Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) disables mocktime.
+ static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:01Z");
+ static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z");
+ return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(min.value_or(time_min), max.value_or(time_max));
+}
+
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
{
CMutableTransaction tx_mut;
@@ -78,13 +281,13 @@ CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, co
return ret;
}
-CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length, const bool maybe_p2wsh) noexcept
+CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length, const bool maybe_p2wsh) noexcept
{
const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
CScript r_script{b.begin(), b.end()};
if (maybe_p2wsh && fuzzed_data_provider.ConsumeBool()) {
uint256 script_hash;
- CSHA256().Write(&r_script[0], r_script.size()).Finalize(script_hash.begin());
+ CSHA256().Write(r_script.data(), r_script.size()).Finalize(script_hash.begin());
r_script.clear();
r_script << OP_0 << ToByteVector(script_hash);
}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index 50d3ac66e5..48b7877896 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -26,7 +26,6 @@
#include <test/util/net.h>
#include <txmempool.h>
#include <uint256.h>
-#include <util/time.h>
#include <version.h>
#include <algorithm>
@@ -58,18 +57,20 @@ auto& PickValue(FuzzedDataProvider& fuzzed_data_provider, Collection& col)
return *it;
}
-[[nodiscard]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+[[nodiscard]] inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
{
- const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length);
+ const std::string s = max_length ?
+ fuzzed_data_provider.ConsumeRandomLengthString(*max_length) :
+ fuzzed_data_provider.ConsumeRandomLengthString();
return {s.begin(), s.end()};
}
-[[nodiscard]] inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+[[nodiscard]] inline std::vector<bool> ConsumeRandomLengthBitVector(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
{
return BytesToBits(ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length));
}
-[[nodiscard]] inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+[[nodiscard]] inline CDataStream ConsumeDataStream(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
{
return CDataStream{ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length), SER_NETWORK, INIT_PROTO_VERSION};
}
@@ -96,7 +97,7 @@ template <typename T>
}
template <typename T>
-[[nodiscard]] inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept
+[[nodiscard]] inline std::optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt) noexcept
{
const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION};
@@ -122,24 +123,15 @@ template <typename WeakEnumType, size_t size>
return static_cast<opcodetype>(fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE));
}
-[[nodiscard]] inline CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider) noexcept
-{
- return fuzzed_data_provider.ConsumeIntegralInRange<CAmount>(0, MAX_MONEY);
-}
+[[nodiscard]] CAmount ConsumeMoney(FuzzedDataProvider& fuzzed_data_provider, const std::optional<CAmount>& max = std::nullopt) noexcept;
-[[nodiscard]] inline int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider) noexcept
-{
- // Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) is a no-op.
- static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:01Z");
- static const int64_t time_max = ParseISO8601DateTime("9999-12-31T23:59:59Z");
- return fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(time_min, time_max);
-}
+[[nodiscard]] int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider, const std::optional<int64_t>& min = std::nullopt, const std::optional<int64_t>& max = std::nullopt) noexcept;
[[nodiscard]] CMutableTransaction ConsumeTransaction(FuzzedDataProvider& fuzzed_data_provider, const std::optional<std::vector<uint256>>& prevout_txids, const int max_num_in = 10, const int max_num_out = 10) noexcept;
[[nodiscard]] CScriptWitness ConsumeScriptWitness(FuzzedDataProvider& fuzzed_data_provider, const size_t max_stack_elem_size = 32) noexcept;
-[[nodiscard]] CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096, const bool maybe_p2wsh = false) noexcept;
+[[nodiscard]] CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider, const std::optional<size_t>& max_length = std::nullopt, const bool maybe_p2wsh = false) noexcept;
[[nodiscard]] uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept;
@@ -575,181 +567,25 @@ class FuzzedSock : public Sock
mutable std::optional<uint8_t> m_peek_data;
public:
- explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
- {
- m_socket = fuzzed_data_provider.ConsumeIntegral<SOCKET>();
- }
+ explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider);
- ~FuzzedSock() override
- {
- // Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
- // Sock::Reset() (not FuzzedSock::Reset()!) which will call CloseSocket(m_socket).
- // Avoid closing an arbitrary file descriptor (m_socket is just a random number which
- // may concide with a real opened file descriptor).
- Reset();
- }
+ ~FuzzedSock() override;
- FuzzedSock& operator=(Sock&& other) override
- {
- assert(false && "Move of Sock into FuzzedSock not allowed.");
- return *this;
- }
+ FuzzedSock& operator=(Sock&& other) override;
- void Reset() override
- {
- m_socket = INVALID_SOCKET;
- }
+ void Reset() override;
- ssize_t Send(const void* data, size_t len, int flags) const override
- {
- constexpr std::array send_errnos{
- EACCES,
- EAGAIN,
- EALREADY,
- EBADF,
- ECONNRESET,
- EDESTADDRREQ,
- EFAULT,
- EINTR,
- EINVAL,
- EISCONN,
- EMSGSIZE,
- ENOBUFS,
- ENOMEM,
- ENOTCONN,
- ENOTSOCK,
- EOPNOTSUPP,
- EPIPE,
- EWOULDBLOCK,
- };
- if (m_fuzzed_data_provider.ConsumeBool()) {
- return len;
- }
- const ssize_t r = m_fuzzed_data_provider.ConsumeIntegralInRange<ssize_t>(-1, len);
- if (r == -1) {
- SetFuzzedErrNo(m_fuzzed_data_provider, send_errnos);
- }
- return r;
- }
+ ssize_t Send(const void* data, size_t len, int flags) const override;
- ssize_t Recv(void* buf, size_t len, int flags) const override
- {
- // Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
- // SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
- // returning -1 and setting errno to EAGAIN repeatedly.
- constexpr std::array recv_errnos{
- ECONNREFUSED,
- EAGAIN,
- EBADF,
- EFAULT,
- EINTR,
- EINVAL,
- ENOMEM,
- ENOTCONN,
- ENOTSOCK,
- EWOULDBLOCK,
- };
- assert(buf != nullptr || len == 0);
- if (len == 0 || m_fuzzed_data_provider.ConsumeBool()) {
- const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
- if (r == -1) {
- SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
- }
- return r;
- }
- std::vector<uint8_t> random_bytes;
- bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
- if (m_peek_data.has_value()) {
- // `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
- random_bytes.assign({m_peek_data.value()});
- if ((flags & MSG_PEEK) == 0) {
- m_peek_data.reset();
- }
- pad_to_len_bytes = false;
- } else if ((flags & MSG_PEEK) != 0) {
- // New call with `MSG_PEEK`.
- random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
- if (!random_bytes.empty()) {
- m_peek_data = random_bytes[0];
- pad_to_len_bytes = false;
- }
- } else {
- random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
- m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
- }
- if (random_bytes.empty()) {
- const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
- if (r == -1) {
- SetFuzzedErrNo(m_fuzzed_data_provider, recv_errnos);
- }
- return r;
- }
- std::memcpy(buf, random_bytes.data(), random_bytes.size());
- if (pad_to_len_bytes) {
- if (len > random_bytes.size()) {
- std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
- }
- return len;
- }
- if (m_fuzzed_data_provider.ConsumeBool() && std::getenv("FUZZED_SOCKET_FAKE_LATENCY") != nullptr) {
- std::this_thread::sleep_for(std::chrono::milliseconds{2});
- }
- return random_bytes.size();
- }
+ ssize_t Recv(void* buf, size_t len, int flags) const override;
- int Connect(const sockaddr*, socklen_t) const override
- {
- // Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
- // SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
- // returning -1 and setting errno to EAGAIN repeatedly.
- constexpr std::array connect_errnos{
- ECONNREFUSED,
- EAGAIN,
- ECONNRESET,
- EHOSTUNREACH,
- EINPROGRESS,
- EINTR,
- ENETUNREACH,
- ETIMEDOUT,
- };
- if (m_fuzzed_data_provider.ConsumeBool()) {
- SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
- return -1;
- }
- return 0;
- }
+ int Connect(const sockaddr*, socklen_t) const override;
- int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
- {
- constexpr std::array getsockopt_errnos{
- ENOMEM,
- ENOBUFS,
- };
- if (m_fuzzed_data_provider.ConsumeBool()) {
- SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
- return -1;
- }
- if (opt_val == nullptr) {
- return 0;
- }
- std::memcpy(opt_val,
- ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
- *opt_len);
- return 0;
- }
+ int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override;
- bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override
- {
- return m_fuzzed_data_provider.ConsumeBool();
- }
+ bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
- bool IsConnected(std::string& errmsg) const override {
- if (m_fuzzed_data_provider.ConsumeBool()) {
- return true;
- }
- errmsg = "disconnected at random by the fuzzer";
- return false;
- }
+ bool IsConnected(std::string& errmsg) const override;
};
[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
diff --git a/src/test/fuzz/utxo_snapshot.cpp b/src/test/fuzz/utxo_snapshot.cpp
new file mode 100644
index 0000000000..6f2bc081c6
--- /dev/null
+++ b/src/test/fuzz/utxo_snapshot.cpp
@@ -0,0 +1,87 @@
+// Copyright (c) 2021 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <chainparams.h>
+#include <consensus/validation.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/mining.h>
+#include <test/util/setup_common.h>
+#include <validation.h>
+#include <validationinterface.h>
+
+namespace {
+
+const std::vector<std::shared_ptr<CBlock>>* g_chain;
+
+void initialize_chain()
+{
+ const auto params{CreateChainParams(ArgsManager{}, CBaseChainParams::REGTEST)};
+ static const auto chain{CreateBlockChain(2 * COINBASE_MATURITY, *params)};
+ g_chain = &chain;
+}
+
+FUZZ_TARGET_INIT(utxo_snapshot, initialize_chain)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ std::unique_ptr<const TestingSetup> setup{MakeNoLogFileContext<const TestingSetup>()};
+ const auto& node = setup->m_node;
+ auto& chainman{*node.chainman};
+
+ const auto snapshot_path = gArgs.GetDataDirNet() / "fuzzed_snapshot.dat";
+
+ Assert(!chainman.SnapshotBlockhash());
+
+ {
+ CAutoFile outfile{fsbridge::fopen(snapshot_path, "wb"), SER_DISK, CLIENT_VERSION};
+ const auto file_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
+ outfile << Span<const uint8_t>{file_data};
+ }
+
+ const auto ActivateFuzzedSnapshot{[&] {
+ CAutoFile infile{fsbridge::fopen(snapshot_path, "rb"), SER_DISK, CLIENT_VERSION};
+ SnapshotMetadata metadata;
+ try {
+ infile >> metadata;
+ } catch (const std::ios_base::failure&) {
+ return false;
+ }
+ return chainman.ActivateSnapshot(infile, metadata, /* in_memory */ true);
+ }};
+
+ if (fuzzed_data_provider.ConsumeBool()) {
+ for (const auto& block : *g_chain) {
+ BlockValidationState dummy;
+ bool processed{chainman.ProcessNewBlockHeaders({*block}, dummy, ::Params())};
+ Assert(processed);
+ const auto* index{WITH_LOCK(::cs_main, return chainman.m_blockman.LookupBlockIndex(block->GetHash()))};
+ Assert(index);
+ }
+ }
+
+ if (ActivateFuzzedSnapshot()) {
+ LOCK(::cs_main);
+ Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash->IsNull());
+ Assert(*chainman.ActiveChainstate().m_from_snapshot_blockhash ==
+ *chainman.SnapshotBlockhash());
+ const auto& coinscache{chainman.ActiveChainstate().CoinsTip()};
+ int64_t chain_tx{};
+ for (const auto& block : *g_chain) {
+ Assert(coinscache.HaveCoin(COutPoint{block->vtx.at(0)->GetHash(), 0}));
+ const auto* index{chainman.m_blockman.LookupBlockIndex(block->GetHash())};
+ const auto num_tx{Assert(index)->nTx};
+ Assert(num_tx == 1);
+ chain_tx += num_tx;
+ }
+ Assert(g_chain->size() == coinscache.GetCacheSize());
+ Assert(chain_tx == chainman.ActiveTip()->nChainTx);
+ } else {
+ Assert(!chainman.SnapshotBlockhash());
+ Assert(!chainman.ActiveChainstate().m_from_snapshot_blockhash);
+ }
+ // Snapshot should refuse to load a second time regardless of validity
+ Assert(!ActivateFuzzedSnapshot());
+}
+} // namespace
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
index 88c1a1a9cb..9186821836 100644
--- a/src/test/fuzz/versionbits.cpp
+++ b/src/test/fuzz/versionbits.cpp
@@ -29,14 +29,16 @@ public:
const int64_t m_end;
const int m_period;
const int m_threshold;
+ const int m_min_activation_height;
const int m_bit;
- TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int bit)
- : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_bit{bit}
+ TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int min_activation_height, int bit)
+ : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_min_activation_height{min_activation_height}, m_bit{bit}
{
assert(m_period > 0);
assert(0 <= m_threshold && m_threshold <= m_period);
assert(0 <= m_bit && m_bit < 32 && m_bit < VERSIONBITS_NUM_BITS);
+ assert(0 <= m_min_activation_height);
}
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); }
@@ -44,6 +46,7 @@ public:
int64_t EndTime(const Consensus::Params& params) const override { return m_end; }
int Period(const Consensus::Params& params) const override { return m_period; }
int Threshold(const Consensus::Params& params) const override { return m_threshold; }
+ int MinActivationHeight(const Consensus::Params& params) const override { return m_min_activation_height; }
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); }
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); }
@@ -144,32 +147,27 @@ FUZZ_TARGET_INIT(versionbits, initialize)
// pick the timestamp to switch based on a block
// note states will change *after* these blocks because mediantime lags
int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
- int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(start_block, period * (max_periods - 3));
+ int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
start_time = block_start_time + start_block * interval;
timeout = block_start_time + end_block * interval;
- assert(start_time <= timeout);
-
// allow for times to not exactly match a block
if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2;
if (fuzzed_data_provider.ConsumeBool()) timeout += interval / 2;
-
- // this may make timeout too early; if so, don't run the test
- if (start_time > timeout) return;
} else {
if (fuzzed_data_provider.ConsumeBool()) {
start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
- timeout = Consensus::BIP9Deployment::NO_TIMEOUT;
always_active_test = true;
} else {
- start_time = 1199145601; // January 1, 2008
- timeout = 1230767999; // December 31, 2008
+ start_time = Consensus::BIP9Deployment::NEVER_ACTIVE;
never_active_test = true;
}
+ timeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>();
}
+ int min_activation = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods);
- TestConditionChecker checker(start_time, timeout, period, threshold, bit);
+ TestConditionChecker checker(start_time, timeout, period, threshold, min_activation, bit);
// Early exit if the versions don't signal sensibly for the deployment
if (!checker.Condition(ver_signal)) return;
@@ -294,28 +292,35 @@ FUZZ_TARGET_INIT(versionbits, initialize)
assert(since == 0);
assert(exp_state == ThresholdState::DEFINED);
assert(current_block->GetMedianTimePast() < checker.m_begin);
- assert(current_block->GetMedianTimePast() < checker.m_end);
break;
case ThresholdState::STARTED:
assert(current_block->GetMedianTimePast() >= checker.m_begin);
- assert(current_block->GetMedianTimePast() < checker.m_end);
if (exp_state == ThresholdState::STARTED) {
assert(blocks_sig < threshold);
+ assert(current_block->GetMedianTimePast() < checker.m_end);
} else {
assert(exp_state == ThresholdState::DEFINED);
}
break;
case ThresholdState::LOCKED_IN:
- assert(exp_state == ThresholdState::STARTED);
- assert(current_block->GetMedianTimePast() < checker.m_end);
- assert(blocks_sig >= threshold);
+ if (exp_state == ThresholdState::LOCKED_IN) {
+ assert(current_block->nHeight + 1 < min_activation);
+ } else {
+ assert(exp_state == ThresholdState::STARTED);
+ assert(blocks_sig >= threshold);
+ }
break;
case ThresholdState::ACTIVE:
+ assert(always_active_test || min_activation <= current_block->nHeight + 1);
assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
break;
case ThresholdState::FAILED:
- assert(current_block->GetMedianTimePast() >= checker.m_end);
- assert(exp_state != ThresholdState::LOCKED_IN && exp_state != ThresholdState::ACTIVE);
+ assert(never_active_test || current_block->GetMedianTimePast() >= checker.m_end);
+ if (exp_state == ThresholdState::STARTED) {
+ assert(blocks_sig < threshold);
+ } else {
+ assert(exp_state == ThresholdState::FAILED);
+ }
break;
default:
assert(false);
@@ -326,26 +331,20 @@ FUZZ_TARGET_INIT(versionbits, initialize)
assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
}
- // "always active" has additional restrictions
if (always_active_test) {
+ // "always active" has additional restrictions
assert(state == ThresholdState::ACTIVE);
assert(exp_state == ThresholdState::ACTIVE);
assert(since == 0);
+ } else if (never_active_test) {
+ // "never active" does too
+ assert(state == ThresholdState::FAILED);
+ assert(exp_state == ThresholdState::FAILED);
+ assert(since == 0);
} else {
- // except for always active, the initial state is always DEFINED
+ // for signalled deployments, the initial state is always DEFINED
assert(since > 0 || state == ThresholdState::DEFINED);
assert(exp_since > 0 || exp_state == ThresholdState::DEFINED);
}
-
- // "never active" does too
- if (never_active_test) {
- assert(state == ThresholdState::FAILED);
- assert(since == period);
- if (exp_since == 0) {
- assert(exp_state == ThresholdState::DEFINED);
- } else {
- assert(exp_state == ThresholdState::FAILED);
- }
- }
}
} // namespace