0
0
Fork 0
mirror of https://github.com/bitcoin/bitcoin.git synced 2025-02-08 10:31:50 -05:00

fuzz: Avoid timeout and bloat in fuzz targets

Also, fix iwyu
This commit is contained in:
MarcoFalke 2023-11-07 17:46:41 +01:00
parent 82ea4e787c
commit fabb5046a7
No known key found for this signature in database
5 changed files with 60 additions and 28 deletions

View file

@ -10,21 +10,22 @@
#include <uint256.h> #include <uint256.h>
#include <cassert> #include <cassert>
#include <cstdint> #include <limits>
#include <optional> #include <optional>
#include <string>
#include <vector> #include <vector>
FUZZ_TARGET(bloom_filter) FUZZ_TARGET(bloom_filter)
{ {
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
bool good_data{true};
CBloomFilter bloom_filter{ CBloomFilter bloom_filter{
fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 10000000), fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 10000000),
1.0 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max()), 1.0 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max()),
fuzzed_data_provider.ConsumeIntegral<unsigned int>(), 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}))}; 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( CallOneOf(
fuzzed_data_provider, fuzzed_data_provider,
[&] { [&] {
@ -37,6 +38,7 @@ FUZZ_TARGET(bloom_filter)
[&] { [&] {
const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider); const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
if (!out_point) { if (!out_point) {
good_data = false;
return; return;
} }
(void)bloom_filter.contains(*out_point); (void)bloom_filter.contains(*out_point);
@ -47,6 +49,7 @@ FUZZ_TARGET(bloom_filter)
[&] { [&] {
const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider); const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
if (!u256) { if (!u256) {
good_data = false;
return; return;
} }
(void)bloom_filter.contains(*u256); (void)bloom_filter.contains(*u256);
@ -57,6 +60,7 @@ FUZZ_TARGET(bloom_filter)
[&] { [&] {
const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mut_tx) { if (!mut_tx) {
good_data = false;
return; return;
} }
const CTransaction tx{*mut_tx}; const CTransaction tx{*mut_tx};

View file

@ -2,26 +2,28 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
#include <coins.h> #include <coins.h>
#include <consensus/amount.h> #include <consensus/amount.h>
#include <consensus/tx_check.h> #include <consensus/tx_check.h>
#include <consensus/tx_verify.h> #include <consensus/tx_verify.h>
#include <consensus/validation.h> #include <consensus/validation.h>
#include <key.h>
#include <policy/policy.h> #include <policy/policy.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <pubkey.h> #include <script/interpreter.h>
#include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h> #include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h> #include <test/fuzz/util.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <validation.h> #include <util/hasher.h>
#include <cassert>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
#include <memory>
#include <optional> #include <optional>
#include <stdexcept>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
namespace { namespace {
@ -44,12 +46,15 @@ void initialize_coins_view()
FUZZ_TARGET(coins_view, .init = initialize_coins_view) FUZZ_TARGET(coins_view, .init = initialize_coins_view)
{ {
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
bool good_data{true};
CCoinsView backend_coins_view; CCoinsView backend_coins_view;
CCoinsViewCache coins_view_cache{&backend_coins_view, /*deterministic=*/true}; CCoinsViewCache coins_view_cache{&backend_coins_view, /*deterministic=*/true};
COutPoint random_out_point; COutPoint random_out_point;
Coin random_coin; Coin random_coin;
CMutableTransaction random_mutable_transaction; CMutableTransaction random_mutable_transaction;
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) { LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
{
CallOneOf( CallOneOf(
fuzzed_data_provider, 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); const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
if (!opt_out_point) { if (!opt_out_point) {
good_data = false;
return; return;
} }
random_out_point = *opt_out_point; 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); const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
if (!opt_coin) { if (!opt_coin) {
good_data = false;
return; return;
} }
random_coin = *opt_coin; 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); const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!opt_mutable_transaction) { if (!opt_mutable_transaction) {
good_data = false;
return; return;
} }
random_mutable_transaction = *opt_mutable_transaction; random_mutable_transaction = *opt_mutable_transaction;
@ -116,7 +124,8 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
[&] { [&] {
CCoinsMapMemoryResource resource; CCoinsMapMemoryResource resource;
CCoinsMap coins_map{0, SaltedOutpointHasher{/*deterministic=*/true}, CCoinsMap::key_equal{}, &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; CCoinsCacheEntry coins_cache_entry;
coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>(); coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
if (fuzzed_data_provider.ConsumeBool()) { if (fuzzed_data_provider.ConsumeBool()) {
@ -124,6 +133,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
} else { } else {
const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
if (!opt_coin) { if (!opt_coin) {
good_data = false;
return; return;
} }
coins_cache_entry.coin = *opt_coin; coins_cache_entry.coin = *opt_coin;

View file

@ -14,6 +14,11 @@
/** /**
* Can be used to limit a theoretically unbounded loop. This caps the runtime * Can be used to limit a theoretically unbounded loop. This caps the runtime
* to avoid timeouts or OOMs. * 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) \ #define LIMITED_WHILE(condition, limit) \
for (unsigned _count{limit}; (condition) && _count; --_count) for (unsigned _count{limit}; (condition) && _count; --_count)

View file

@ -6,16 +6,14 @@
#include <policy/fees.h> #include <policy/fees.h>
#include <policy/fees_args.h> #include <policy/fees_args.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h> #include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h> #include <test/fuzz/util.h>
#include <test/fuzz/util/mempool.h> #include <test/fuzz/util/mempool.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <txmempool.h>
#include <cstdint>
#include <optional> #include <optional>
#include <string>
#include <vector> #include <vector>
namespace { namespace {
@ -31,13 +29,17 @@ void initialize_policy_estimator()
FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator) FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
{ {
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); 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}; 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( CallOneOf(
fuzzed_data_provider, fuzzed_data_provider,
[&] { [&] {
const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mtx) { if (!mtx) {
good_data = false;
return; return;
} }
const CTransaction tx{*mtx}; const CTransaction tx{*mtx};
@ -48,9 +50,11 @@ FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
}, },
[&] { [&] {
std::vector<CTxMemPoolEntry> mempool_entries; 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); const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!mtx) { if (!mtx) {
good_data = false;
break; break;
} }
const CTransaction tx{*mtx}; const CTransaction tx{*mtx};

View file

@ -3,18 +3,14 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <base58.h> #include <base58.h>
#include <core_io.h>
#include <key.h> #include <key.h>
#include <key_io.h> #include <key_io.h>
#include <node/context.h>
#include <primitives/block.h> #include <primitives/block.h>
#include <primitives/transaction.h> #include <primitives/transaction.h>
#include <psbt.h> #include <psbt.h>
#include <rpc/blockchain.h>
#include <rpc/client.h> #include <rpc/client.h>
#include <rpc/request.h> #include <rpc/request.h>
#include <rpc/server.h> #include <rpc/server.h>
#include <rpc/util.h>
#include <span.h> #include <span.h>
#include <streams.h> #include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/FuzzedDataProvider.h>
@ -22,19 +18,23 @@
#include <test/fuzz/util.h> #include <test/fuzz/util.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
#include <tinyformat.h> #include <tinyformat.h>
#include <uint256.h>
#include <univalue.h> #include <univalue.h>
#include <util/chaintype.h>
#include <util/strencodings.h> #include <util/strencodings.h>
#include <util/string.h> #include <util/string.h>
#include <util/time.h> #include <util/time.h>
#include <algorithm>
#include <cassert>
#include <cstdint> #include <cstdint>
#include <cstdlib>
#include <exception>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <vector> #include <vector>
enum class ChainType;
namespace { namespace {
struct RPCFuzzTestingSetup : public TestingSetup { struct RPCFuzzTestingSetup : public TestingSetup {
@ -184,7 +184,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
"waitfornewblock", "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_string_length = 4096;
const size_t max_base58_bytes_length{64}; const size_t max_base58_bytes_length{64};
@ -251,6 +251,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// hex encoded block // hex encoded block
std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider); std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
if (!opt_block) { if (!opt_block) {
good_data = false;
return; return;
} }
CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION}; CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
@ -261,6 +262,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// hex encoded block header // hex encoded block header
std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider); std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
if (!opt_block_header) { if (!opt_block_header) {
good_data = false;
return; return;
} }
DataStream data_stream{}; DataStream data_stream{};
@ -271,6 +273,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// hex encoded tx // hex encoded tx
std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider); std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
if (!opt_tx) { if (!opt_tx) {
good_data = false;
return; return;
} }
CDataStream data_stream{SER_NETWORK, fuzzed_data_provider.ConsumeBool() ? PROTOCOL_VERSION : (PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)}; 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 // base64 encoded psbt
std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider); std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
if (!opt_psbt) { if (!opt_psbt) {
good_data = false;
return; return;
} }
CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION}; CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
@ -291,6 +295,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// base58 encoded key // base58 encoded key
CKey key = ConsumePrivateKey(fuzzed_data_provider); CKey key = ConsumePrivateKey(fuzzed_data_provider);
if (!key.IsValid()) { if (!key.IsValid()) {
good_data = false;
return; return;
} }
r = EncodeSecret(key); r = EncodeSecret(key);
@ -299,6 +304,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
// hex encoded pubkey // hex encoded pubkey
CKey key = ConsumePrivateKey(fuzzed_data_provider); CKey key = ConsumePrivateKey(fuzzed_data_provider);
if (!key.IsValid()) { if (!key.IsValid()) {
good_data = false;
return; return;
} }
r = HexStr(key.GetPubKey()); r = HexStr(key.GetPubKey());
@ -306,18 +312,19 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
return r; 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; std::vector<std::string> scalar_arguments;
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) { LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider)); {
scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
} }
return "[\"" + Join(scalar_arguments, "\",\"") + "\"]"; 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() RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
@ -353,6 +360,7 @@ void initialize_rpc()
FUZZ_TARGET(rpc, .init = initialize_rpc) FUZZ_TARGET(rpc, .init = initialize_rpc)
{ {
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
bool good_data{true};
SetMockTime(ConsumeTime(fuzzed_data_provider)); SetMockTime(ConsumeTime(fuzzed_data_provider));
const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64); const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) { 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; return;
} }
std::vector<std::string> arguments; std::vector<std::string> arguments;
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) { LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider)); {
arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
} }
try { try {
rpc_testing_setup->CallRPC(rpc_command, arguments); rpc_testing_setup->CallRPC(rpc_command, arguments);