aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorfanquake <fanquake@gmail.com>2023-11-08 14:10:18 +0000
committerfanquake <fanquake@gmail.com>2023-11-08 14:19:35 +0000
commitf1f3f2d9cc38f1e3793a653ffb6666d46d2b80df (patch)
tree512b493dad8e829a625896cafff2e06c839436e9 /src/test
parent9ad19fc7c7116254015650071c59717a485b1bcb (diff)
parentfabb5046a7365af3079e6e45606d63576bc6ad12 (diff)
downloadbitcoin-f1f3f2d9cc38f1e3793a653ffb6666d46d2b80df.tar.xz
Merge bitcoin/bitcoin#28815: fuzz: Avoid timeout and bloat in fuzz targets
fabb5046a7365af3079e6e45606d63576bc6ad12 fuzz: Avoid timeout and bloat in fuzz targets (MarcoFalke) Pull request description: If the fuzz input contains invalid data *in a loop*, abort early. This will teach the fuzz engine to look for useful data and avoids bloating the fuzz input folder with useless (repeated) data. ACKs for top commit: dergoegge: utACK fabb5046a7365af3079e6e45606d63576bc6ad12 brunoerg: crACK fabb5046a7365af3079e6e45606d63576bc6ad12 Tree-SHA512: 26da100d7558ae6fdd5292fb146d8858b2af8f78c546ca2509b9d27b33a33e9462ecb6035de142f9f36dd5de32f8cbad099d6c7a697902d23e1bb621cd27dc88
Diffstat (limited to 'src/test')
-rw-r--r--src/test/fuzz/bloom_filter.cpp10
-rw-r--r--src/test/fuzz/coins_view.cpp22
-rw-r--r--src/test/fuzz/fuzz.h5
-rw-r--r--src/test/fuzz/policy_estimator.cpp14
-rw-r--r--src/test/fuzz/rpc.cpp37
5 files changed, 60 insertions, 28 deletions
diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp
index 3e303ecc0f..612f113b14 100644
--- a/src/test/fuzz/bloom_filter.cpp
+++ b/src/test/fuzz/bloom_filter.cpp
@@ -10,21 +10,22 @@
#include <uint256.h>
#include <cassert>
-#include <cstdint>
+#include <limits>
#include <optional>
-#include <string>
#include <vector>
FUZZ_TARGET(bloom_filter)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ bool good_data{true};
CBloomFilter bloom_filter{
fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 10000000),
1.0 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max()),
fuzzed_data_provider.ConsumeIntegral<unsigned int>(),
static_cast<unsigned char>(fuzzed_data_provider.PickValueInArray({BLOOM_UPDATE_NONE, BLOOM_UPDATE_ALL, BLOOM_UPDATE_P2PUBKEY_ONLY, BLOOM_UPDATE_MASK}))};
- LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 10000) {
+ LIMITED_WHILE(good_data && fuzzed_data_provider.remaining_bytes() > 0, 10'000)
+ {
CallOneOf(
fuzzed_data_provider,
[&] {
@@ -37,6 +38,7 @@ FUZZ_TARGET(bloom_filter)
[&] {
const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
if (!out_point) {
+ good_data = false;
return;
}
(void)bloom_filter.contains(*out_point);
@@ -47,6 +49,7 @@ FUZZ_TARGET(bloom_filter)
[&] {
const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
if (!u256) {
+ good_data = false;
return;
}
(void)bloom_filter.contains(*u256);
@@ -57,6 +60,7 @@ FUZZ_TARGET(bloom_filter)
[&] {
const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mut_tx) {
+ good_data = false;
return;
}
const CTransaction tx{*mut_tx};
diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp
index b088aa0bd7..1a8570ee8f 100644
--- a/src/test/fuzz/coins_view.cpp
+++ b/src/test/fuzz/coins_view.cpp
@@ -2,26 +2,28 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <chainparams.h>
#include <coins.h>
#include <consensus/amount.h>
#include <consensus/tx_check.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
-#include <key.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
-#include <pubkey.h>
+#include <script/interpreter.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
-#include <validation.h>
+#include <util/hasher.h>
+#include <cassert>
#include <cstdint>
#include <limits>
+#include <memory>
#include <optional>
+#include <stdexcept>
#include <string>
+#include <utility>
#include <vector>
namespace {
@@ -44,12 +46,15 @@ void initialize_coins_view()
FUZZ_TARGET(coins_view, .init = initialize_coins_view)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ bool good_data{true};
+
CCoinsView backend_coins_view;
CCoinsViewCache coins_view_cache{&backend_coins_view, /*deterministic=*/true};
COutPoint random_out_point;
Coin random_coin;
CMutableTransaction random_mutable_transaction;
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
+ LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
+ {
CallOneOf(
fuzzed_data_provider,
[&] {
@@ -95,6 +100,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
[&] {
const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
if (!opt_out_point) {
+ good_data = false;
return;
}
random_out_point = *opt_out_point;
@@ -102,6 +108,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
[&] {
const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
if (!opt_coin) {
+ good_data = false;
return;
}
random_coin = *opt_coin;
@@ -109,6 +116,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
[&] {
const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!opt_mutable_transaction) {
+ good_data = false;
return;
}
random_mutable_transaction = *opt_mutable_transaction;
@@ -116,7 +124,8 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
[&] {
CCoinsMapMemoryResource resource;
CCoinsMap coins_map{0, SaltedOutpointHasher{/*deterministic=*/true}, CCoinsMap::key_equal{}, &resource};
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
+ LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
+ {
CCoinsCacheEntry coins_cache_entry;
coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
if (fuzzed_data_provider.ConsumeBool()) {
@@ -124,6 +133,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
} else {
const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
if (!opt_coin) {
+ good_data = false;
return;
}
coins_cache_entry.coin = *opt_coin;
diff --git a/src/test/fuzz/fuzz.h b/src/test/fuzz/fuzz.h
index 0534f9bcf1..1f0fa5527a 100644
--- a/src/test/fuzz/fuzz.h
+++ b/src/test/fuzz/fuzz.h
@@ -14,6 +14,11 @@
/**
* Can be used to limit a theoretically unbounded loop. This caps the runtime
* to avoid timeouts or OOMs.
+ *
+ * This can be used in combination with a check in the condition to confirm
+ * whether the fuzz engine provided "good" data. If the fuzz input contains
+ * invalid data, the loop aborts early. This will teach the fuzz engine to look
+ * for useful data and avoids bloating the fuzz input folder with useless data.
*/
#define LIMITED_WHILE(condition, limit) \
for (unsigned _count{limit}; (condition) && _count; --_count)
diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp
index 227ee9d2c4..4cbc1b4820 100644
--- a/src/test/fuzz/policy_estimator.cpp
+++ b/src/test/fuzz/policy_estimator.cpp
@@ -6,16 +6,14 @@
#include <policy/fees.h>
#include <policy/fees_args.h>
#include <primitives/transaction.h>
+#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/fuzz/util/mempool.h>
#include <test/util/setup_common.h>
-#include <txmempool.h>
-#include <cstdint>
#include <optional>
-#include <string>
#include <vector>
namespace {
@@ -31,13 +29,17 @@ void initialize_policy_estimator()
FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
{
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ bool good_data{true};
+
CBlockPolicyEstimator block_policy_estimator{FeeestPath(*g_setup->m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES};
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
+ LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
+ {
CallOneOf(
fuzzed_data_provider,
[&] {
const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mtx) {
+ good_data = false;
return;
}
const CTransaction tx{*mtx};
@@ -48,9 +50,11 @@ FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
},
[&] {
std::vector<CTxMemPoolEntry> mempool_entries;
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
+ LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
+ {
const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mtx) {
+ good_data = false;
break;
}
const CTransaction tx{*mtx};
diff --git a/src/test/fuzz/rpc.cpp b/src/test/fuzz/rpc.cpp
index 270cab58e2..4e059c7d0a 100644
--- a/src/test/fuzz/rpc.cpp
+++ b/src/test/fuzz/rpc.cpp
@@ -3,18 +3,14 @@
// 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>
@@ -22,19 +18,23 @@
#include <test/fuzz/util.h>
#include <test/util/setup_common.h>
#include <tinyformat.h>
+#include <uint256.h>
#include <univalue.h>
-#include <util/chaintype.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/time.h>
+#include <algorithm>
+#include <cassert>
#include <cstdint>
+#include <cstdlib>
+#include <exception>
#include <iostream>
#include <memory>
#include <optional>
#include <stdexcept>
-#include <string>
#include <vector>
+enum class ChainType;
namespace {
struct RPCFuzzTestingSetup : public TestingSetup {
@@ -184,7 +184,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"waitfornewblock",
};
-std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
{
const size_t max_string_length = 4096;
const size_t max_base58_bytes_length{64};
@@ -251,6 +251,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// hex encoded block
std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
if (!opt_block) {
+ good_data = false;
return;
}
CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
@@ -261,6 +262,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// hex encoded block header
std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
if (!opt_block_header) {
+ good_data = false;
return;
}
DataStream data_stream{};
@@ -271,6 +273,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// hex encoded tx
std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!opt_tx) {
+ good_data = false;
return;
}
CDataStream data_stream{SER_NETWORK, fuzzed_data_provider.ConsumeBool() ? PROTOCOL_VERSION : (PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)};
@@ -281,6 +284,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// base64 encoded psbt
std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
if (!opt_psbt) {
+ good_data = false;
return;
}
CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
@@ -291,6 +295,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// base58 encoded key
CKey key = ConsumePrivateKey(fuzzed_data_provider);
if (!key.IsValid()) {
+ good_data = false;
return;
}
r = EncodeSecret(key);
@@ -299,6 +304,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// hex encoded pubkey
CKey key = ConsumePrivateKey(fuzzed_data_provider);
if (!key.IsValid()) {
+ good_data = false;
return;
}
r = HexStr(key.GetPubKey());
@@ -306,18 +312,19 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
return r;
}
-std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
{
std::vector<std::string> scalar_arguments;
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
- scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider));
+ LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
+ {
+ scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
}
return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
}
-std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
+std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
{
- return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
+ return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
}
RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
@@ -353,6 +360,7 @@ void initialize_rpc()
FUZZ_TARGET(rpc, .init = initialize_rpc)
{
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ bool good_data{true};
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) {
@@ -363,8 +371,9 @@ FUZZ_TARGET(rpc, .init = initialize_rpc)
return;
}
std::vector<std::string> arguments;
- LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
- arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider));
+ LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
+ {
+ arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
}
try {
rpc_testing_setup->CallRPC(rpc_command, arguments);