Skip to content

Commit f1f3f2d

Browse files
committed
Merge bitcoin#28815: fuzz: Avoid timeout and bloat in fuzz targets
fabb504 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 fabb504 brunoerg: crACK fabb504 Tree-SHA512: 26da100d7558ae6fdd5292fb146d8858b2af8f78c546ca2509b9d27b33a33e9462ecb6035de142f9f36dd5de32f8cbad099d6c7a697902d23e1bb621cd27dc88
2 parents 9ad19fc + fabb504 commit f1f3f2d

File tree

5 files changed

+60
-28
lines changed

5 files changed

+60
-28
lines changed

src/test/fuzz/bloom_filter.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@
1010
#include <uint256.h>
1111

1212
#include <cassert>
13-
#include <cstdint>
13+
#include <limits>
1414
#include <optional>
15-
#include <string>
1615
#include <vector>
1716

1817
FUZZ_TARGET(bloom_filter)
1918
{
2019
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
20+
bool good_data{true};
2121

2222
CBloomFilter bloom_filter{
2323
fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, 10000000),
2424
1.0 / fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, std::numeric_limits<unsigned int>::max()),
2525
fuzzed_data_provider.ConsumeIntegral<unsigned int>(),
2626
static_cast<unsigned char>(fuzzed_data_provider.PickValueInArray({BLOOM_UPDATE_NONE, BLOOM_UPDATE_ALL, BLOOM_UPDATE_P2PUBKEY_ONLY, BLOOM_UPDATE_MASK}))};
27-
LIMITED_WHILE(fuzzed_data_provider.remaining_bytes() > 0, 10000) {
27+
LIMITED_WHILE(good_data && fuzzed_data_provider.remaining_bytes() > 0, 10'000)
28+
{
2829
CallOneOf(
2930
fuzzed_data_provider,
3031
[&] {
@@ -37,6 +38,7 @@ FUZZ_TARGET(bloom_filter)
3738
[&] {
3839
const std::optional<COutPoint> out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
3940
if (!out_point) {
41+
good_data = false;
4042
return;
4143
}
4244
(void)bloom_filter.contains(*out_point);
@@ -47,6 +49,7 @@ FUZZ_TARGET(bloom_filter)
4749
[&] {
4850
const std::optional<uint256> u256 = ConsumeDeserializable<uint256>(fuzzed_data_provider);
4951
if (!u256) {
52+
good_data = false;
5053
return;
5154
}
5255
(void)bloom_filter.contains(*u256);
@@ -57,6 +60,7 @@ FUZZ_TARGET(bloom_filter)
5760
[&] {
5861
const std::optional<CMutableTransaction> mut_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
5962
if (!mut_tx) {
63+
good_data = false;
6064
return;
6165
}
6266
const CTransaction tx{*mut_tx};

src/test/fuzz/coins_view.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,28 @@
22
// Distributed under the MIT software license, see the accompanying
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

5-
#include <chainparams.h>
65
#include <coins.h>
76
#include <consensus/amount.h>
87
#include <consensus/tx_check.h>
98
#include <consensus/tx_verify.h>
109
#include <consensus/validation.h>
11-
#include <key.h>
1210
#include <policy/policy.h>
1311
#include <primitives/transaction.h>
14-
#include <pubkey.h>
12+
#include <script/interpreter.h>
1513
#include <test/fuzz/FuzzedDataProvider.h>
1614
#include <test/fuzz/fuzz.h>
1715
#include <test/fuzz/util.h>
1816
#include <test/util/setup_common.h>
19-
#include <validation.h>
17+
#include <util/hasher.h>
2018

19+
#include <cassert>
2120
#include <cstdint>
2221
#include <limits>
22+
#include <memory>
2323
#include <optional>
24+
#include <stdexcept>
2425
#include <string>
26+
#include <utility>
2527
#include <vector>
2628

2729
namespace {
@@ -44,12 +46,15 @@ void initialize_coins_view()
4446
FUZZ_TARGET(coins_view, .init = initialize_coins_view)
4547
{
4648
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
49+
bool good_data{true};
50+
4751
CCoinsView backend_coins_view;
4852
CCoinsViewCache coins_view_cache{&backend_coins_view, /*deterministic=*/true};
4953
COutPoint random_out_point;
5054
Coin random_coin;
5155
CMutableTransaction random_mutable_transaction;
52-
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
56+
LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
57+
{
5358
CallOneOf(
5459
fuzzed_data_provider,
5560
[&] {
@@ -95,35 +100,40 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view)
95100
[&] {
96101
const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider);
97102
if (!opt_out_point) {
103+
good_data = false;
98104
return;
99105
}
100106
random_out_point = *opt_out_point;
101107
},
102108
[&] {
103109
const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
104110
if (!opt_coin) {
111+
good_data = false;
105112
return;
106113
}
107114
random_coin = *opt_coin;
108115
},
109116
[&] {
110117
const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
111118
if (!opt_mutable_transaction) {
119+
good_data = false;
112120
return;
113121
}
114122
random_mutable_transaction = *opt_mutable_transaction;
115123
},
116124
[&] {
117125
CCoinsMapMemoryResource resource;
118126
CCoinsMap coins_map{0, SaltedOutpointHasher{/*deterministic=*/true}, CCoinsMap::key_equal{}, &resource};
119-
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
127+
LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
128+
{
120129
CCoinsCacheEntry coins_cache_entry;
121130
coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral<unsigned char>();
122131
if (fuzzed_data_provider.ConsumeBool()) {
123132
coins_cache_entry.coin = random_coin;
124133
} else {
125134
const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider);
126135
if (!opt_coin) {
136+
good_data = false;
127137
return;
128138
}
129139
coins_cache_entry.coin = *opt_coin;

src/test/fuzz/fuzz.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
/**
1515
* Can be used to limit a theoretically unbounded loop. This caps the runtime
1616
* to avoid timeouts or OOMs.
17+
*
18+
* This can be used in combination with a check in the condition to confirm
19+
* whether the fuzz engine provided "good" data. If the fuzz input contains
20+
* invalid data, the loop aborts early. This will teach the fuzz engine to look
21+
* for useful data and avoids bloating the fuzz input folder with useless data.
1722
*/
1823
#define LIMITED_WHILE(condition, limit) \
1924
for (unsigned _count{limit}; (condition) && _count; --_count)

src/test/fuzz/policy_estimator.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@
66
#include <policy/fees.h>
77
#include <policy/fees_args.h>
88
#include <primitives/transaction.h>
9+
#include <streams.h>
910
#include <test/fuzz/FuzzedDataProvider.h>
1011
#include <test/fuzz/fuzz.h>
1112
#include <test/fuzz/util.h>
1213
#include <test/fuzz/util/mempool.h>
1314
#include <test/util/setup_common.h>
14-
#include <txmempool.h>
1515

16-
#include <cstdint>
1716
#include <optional>
18-
#include <string>
1917
#include <vector>
2018

2119
namespace {
@@ -31,13 +29,17 @@ void initialize_policy_estimator()
3129
FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
3230
{
3331
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
32+
bool good_data{true};
33+
3434
CBlockPolicyEstimator block_policy_estimator{FeeestPath(*g_setup->m_node.args), DEFAULT_ACCEPT_STALE_FEE_ESTIMATES};
35-
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
35+
LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
36+
{
3637
CallOneOf(
3738
fuzzed_data_provider,
3839
[&] {
3940
const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
4041
if (!mtx) {
42+
good_data = false;
4143
return;
4244
}
4345
const CTransaction tx{*mtx};
@@ -48,9 +50,11 @@ FUZZ_TARGET(policy_estimator, .init = initialize_policy_estimator)
4850
},
4951
[&] {
5052
std::vector<CTxMemPoolEntry> mempool_entries;
51-
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
53+
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
54+
{
5255
const std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
5356
if (!mtx) {
57+
good_data = false;
5458
break;
5559
}
5660
const CTransaction tx{*mtx};

src/test/fuzz/rpc.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,38 @@
33
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
44

55
#include <base58.h>
6-
#include <core_io.h>
76
#include <key.h>
87
#include <key_io.h>
9-
#include <node/context.h>
108
#include <primitives/block.h>
119
#include <primitives/transaction.h>
1210
#include <psbt.h>
13-
#include <rpc/blockchain.h>
1411
#include <rpc/client.h>
1512
#include <rpc/request.h>
1613
#include <rpc/server.h>
17-
#include <rpc/util.h>
1814
#include <span.h>
1915
#include <streams.h>
2016
#include <test/fuzz/FuzzedDataProvider.h>
2117
#include <test/fuzz/fuzz.h>
2218
#include <test/fuzz/util.h>
2319
#include <test/util/setup_common.h>
2420
#include <tinyformat.h>
21+
#include <uint256.h>
2522
#include <univalue.h>
26-
#include <util/chaintype.h>
2723
#include <util/strencodings.h>
2824
#include <util/string.h>
2925
#include <util/time.h>
3026

27+
#include <algorithm>
28+
#include <cassert>
3129
#include <cstdint>
30+
#include <cstdlib>
31+
#include <exception>
3232
#include <iostream>
3333
#include <memory>
3434
#include <optional>
3535
#include <stdexcept>
36-
#include <string>
3736
#include <vector>
37+
enum class ChainType;
3838

3939
namespace {
4040
struct RPCFuzzTestingSetup : public TestingSetup {
@@ -184,7 +184,7 @@ const std::vector<std::string> RPC_COMMANDS_SAFE_FOR_FUZZING{
184184
"waitfornewblock",
185185
};
186186

187-
std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
187+
std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
188188
{
189189
const size_t max_string_length = 4096;
190190
const size_t max_base58_bytes_length{64};
@@ -251,6 +251,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
251251
// hex encoded block
252252
std::optional<CBlock> opt_block = ConsumeDeserializable<CBlock>(fuzzed_data_provider);
253253
if (!opt_block) {
254+
good_data = false;
254255
return;
255256
}
256257
CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
@@ -261,6 +262,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
261262
// hex encoded block header
262263
std::optional<CBlockHeader> opt_block_header = ConsumeDeserializable<CBlockHeader>(fuzzed_data_provider);
263264
if (!opt_block_header) {
265+
good_data = false;
264266
return;
265267
}
266268
DataStream data_stream{};
@@ -271,6 +273,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
271273
// hex encoded tx
272274
std::optional<CMutableTransaction> opt_tx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider);
273275
if (!opt_tx) {
276+
good_data = false;
274277
return;
275278
}
276279
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)
281284
// base64 encoded psbt
282285
std::optional<PartiallySignedTransaction> opt_psbt = ConsumeDeserializable<PartiallySignedTransaction>(fuzzed_data_provider);
283286
if (!opt_psbt) {
287+
good_data = false;
284288
return;
285289
}
286290
CDataStream data_stream{SER_NETWORK, PROTOCOL_VERSION};
@@ -291,6 +295,7 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
291295
// base58 encoded key
292296
CKey key = ConsumePrivateKey(fuzzed_data_provider);
293297
if (!key.IsValid()) {
298+
good_data = false;
294299
return;
295300
}
296301
r = EncodeSecret(key);
@@ -299,25 +304,27 @@ std::string ConsumeScalarRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
299304
// hex encoded pubkey
300305
CKey key = ConsumePrivateKey(fuzzed_data_provider);
301306
if (!key.IsValid()) {
307+
good_data = false;
302308
return;
303309
}
304310
r = HexStr(key.GetPubKey());
305311
});
306312
return r;
307313
}
308314

309-
std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
315+
std::string ConsumeArrayRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
310316
{
311317
std::vector<std::string> scalar_arguments;
312-
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
313-
scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider));
318+
LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
319+
{
320+
scalar_arguments.push_back(ConsumeScalarRPCArgument(fuzzed_data_provider, good_data));
314321
}
315322
return "[\"" + Join(scalar_arguments, "\",\"") + "\"]";
316323
}
317324

318-
std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider)
325+
std::string ConsumeRPCArgument(FuzzedDataProvider& fuzzed_data_provider, bool& good_data)
319326
{
320-
return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider) : ConsumeArrayRPCArgument(fuzzed_data_provider);
327+
return fuzzed_data_provider.ConsumeBool() ? ConsumeScalarRPCArgument(fuzzed_data_provider, good_data) : ConsumeArrayRPCArgument(fuzzed_data_provider, good_data);
321328
}
322329

323330
RPCFuzzTestingSetup* InitializeRPCFuzzTestingSetup()
@@ -353,6 +360,7 @@ void initialize_rpc()
353360
FUZZ_TARGET(rpc, .init = initialize_rpc)
354361
{
355362
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
363+
bool good_data{true};
356364
SetMockTime(ConsumeTime(fuzzed_data_provider));
357365
const std::string rpc_command = fuzzed_data_provider.ConsumeRandomLengthString(64);
358366
if (!g_limit_to_rpc_command.empty() && rpc_command != g_limit_to_rpc_command) {
@@ -363,8 +371,9 @@ FUZZ_TARGET(rpc, .init = initialize_rpc)
363371
return;
364372
}
365373
std::vector<std::string> arguments;
366-
LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
367-
arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider));
374+
LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 100)
375+
{
376+
arguments.push_back(ConsumeRPCArgument(fuzzed_data_provider, good_data));
368377
}
369378
try {
370379
rpc_testing_setup->CallRPC(rpc_command, arguments);

0 commit comments

Comments
 (0)