Skip to content

Commit

Permalink
Merge bitcoin/bitcoin#31486: fuzz: Abort when using global PRNG witho…
Browse files Browse the repository at this point in the history
…ut re-seed

fae63bf fuzz: Clarify that only SeedRandomStateForTest(SeedRand::ZEROS) is allowed (MarcoFalke)
fa18acb fuzz: Abort when using global PRNG without re-seed (MarcoFalke)
fa7809a fuzz: Add missing SeedRandomStateForTest(SeedRand::ZEROS) (MarcoFalke)

Pull request description:

  This is the first step toward improving fuzz stability and determinism (bitcoin/bitcoin#29018).

  A fuzz target using the global test-only PRNG will now abort if the seed is re-used across fuzz inputs.

  Also, temporarily add `SeedRandomStateForTest(SeedRand::ZEROS)` to all affected fuzz targets. This may slow down the libfuzzer leak detector, but it will disable itself after some time, or it can be disabled explicitly with `-detect_leaks=0`.

  In a follow-up, each affected fuzz target can be stripped of the global random use and a local `RandomMixin` (or similar) can be added instead.

  (Can be tested by removing any one of the re-seed calls and observing a fuzz abort)

ACKs for top commit:
  hodlinator:
    ACK fae63bf
  dergoegge:
    utACK fae63bf
  marcofleon:
    Tested ACK fae63bf

Tree-SHA512: 4a0db69af7f715408edf4f8b08b44f34ce12ee2c79d33b336ad19a6e6bd079c4ff7c971af0a3efa428213407c1171f4e2837ec6a2577086c2f94cd15618a0892
  • Loading branch information
ryanofsky committed Dec 17, 2024
2 parents a95a8ba + fae63bf commit a60d570
Show file tree
Hide file tree
Showing 39 changed files with 131 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,9 +671,11 @@ void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept
{
GetRNGState().MakeDeterministic(seed);
}
std::atomic<bool> g_used_g_prng{false}; // Only accessed from tests

void GetRandBytes(Span<unsigned char> bytes) noexcept
{
g_used_g_prng = true;
ProcRand(bytes.data(), bytes.size(), RNGLevel::FAST, /*always_use_real_rng=*/false);
}

Expand Down
3 changes: 3 additions & 0 deletions src/test/fuzz/addrman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ void initialize_addrman()

FUZZ_TARGET(data_stream_addr_man, .init = initialize_addrman)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
DataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
Expand Down Expand Up @@ -113,6 +114,7 @@ void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider)

FUZZ_TARGET(addrman, .init = initialize_addrman)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
Expand Down Expand Up @@ -197,6 +199,7 @@ FUZZ_TARGET(addrman, .init = initialize_addrman)
// Check that serialize followed by unserialize produces the same addrman.
FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));

Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/banman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ static bool operator==(const CBanEntry& lhs, const CBanEntry& rhs)

FUZZ_TARGET(banman, .init = initialize_banman)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
fs::path banlist_file = gArgs.GetDataDirNet() / "fuzzed_banlist";
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/blockfilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/random.h>

#include <cstdint>
#include <optional>
Expand All @@ -14,6 +15,7 @@

FUZZ_TARGET(blockfilter)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const std::optional<BlockFilter> block_filter = ConsumeDeserializable<BlockFilter>(fuzzed_data_provider);
if (!block_filter) {
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/connman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void initialize_connman()

FUZZ_TARGET(connman, .init = initialize_connman)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
SetMockTime(ConsumeTime(fuzzed_data_provider));
auto netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
Expand Down
9 changes: 7 additions & 2 deletions src/test/fuzz/fuzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <netaddress.h>
#include <netbase.h>
#include <test/fuzz/util/check_globals.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <util/check.h>
Expand Down Expand Up @@ -78,6 +79,12 @@ void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target,
static std::string_view g_fuzz_target;
static const TypeTestOneInput* g_test_one_input{nullptr};

inline void test_one_input(FuzzBufferType buffer)
{
CheckGlobals check{};
(*Assert(g_test_one_input))(buffer);
}

const std::function<std::string()> G_TEST_GET_FULL_NAME{[]{
return std::string{g_fuzz_target};
}};
Expand Down Expand Up @@ -210,7 +217,6 @@ void signal_handler(int signal)
// This function is used by libFuzzer
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
static const auto& test_one_input = *Assert(g_test_one_input);
test_one_input({data, size});
return 0;
}
Expand All @@ -227,7 +233,6 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
int main(int argc, char** argv)
{
initialize();
static const auto& test_one_input = *Assert(g_test_one_input);
#ifdef __AFL_LOOP
// Enable AFL persistent mode. Requires compilation using afl-clang-fast++.
// See fuzzing.md for details.
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/golomb_rice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/random.h>
#include <util/bytevectorhash.h>
#include <util/golombrice.h>

Expand Down Expand Up @@ -42,6 +43,7 @@ std::vector<uint64_t> BuildHashedSet(const std::unordered_set<std::vector<uint8_

FUZZ_TARGET(golomb_rice)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
std::vector<uint8_t> golomb_rice_data;
std::vector<uint64_t> encoded_deltas;
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/headerssync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class FuzzedHeadersSyncState : public HeadersSyncState

FUZZ_TARGET(headers_sync_state, .init = initialize_headers_sync_state_fuzz)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
auto mock_time{ConsumeTime(fuzzed_data_provider)};

Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/i2p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ void initialize_i2p()

FUZZ_TARGET(i2p, .init = initialize_i2p)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};

SetMockTime(ConsumeTime(fuzzed_data_provider));
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/key.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/random.h>
#include <util/chaintype.h>
#include <util/strencodings.h>

Expand All @@ -38,6 +39,7 @@ void initialize_key()

FUZZ_TARGET(key, .init = initialize_key)
{
SeedRandomStateForTest(SeedRand::ZEROS);
const CKey key = [&] {
CKey k;
k.Set(buffer.begin(), buffer.end(), true);
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/mini_miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ void initialize_miner()
// Test that the MiniMiner can run with various outpoints and feerates.
FUZZ_TARGET(mini_miner, .init = initialize_miner)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
bilingual_str error;
CTxMemPool pool{CTxMemPool::Options{}, error};
Expand Down Expand Up @@ -112,6 +113,7 @@ FUZZ_TARGET(mini_miner, .init = initialize_miner)
// Test that MiniMiner and BlockAssembler build the same block given the same transactions and constraints.
FUZZ_TARGET(mini_miner_selection, .init = initialize_miner)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
bilingual_str error;
CTxMemPool pool{CTxMemPool::Options{}, error};
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/netaddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util/net.h>
#include <test/util/random.h>

#include <cassert>
#include <cstdint>
#include <vector>

FUZZ_TARGET(netaddress)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

const CNetAddr net_addr = ConsumeNetAddr(fuzzed_data_provider);
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/p2p_handshake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ void initialize()

FUZZ_TARGET(p2p_handshake, .init = ::initialize)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

ConnmanTestMsg& connman = static_cast<ConnmanTestMsg&>(*g_setup->m_node.connman);
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/p2p_headers_presync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ void initialize()

FUZZ_TARGET(p2p_headers_presync, .init = initialize)
{
SeedRandomStateForTest(SeedRand::ZEROS);
ChainstateManager& chainman = *g_testing_setup->m_node.chainman;

LOCK(NetEventsInterface::g_msgproc_mutex);
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/package_eval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ std::optional<COutPoint> GetChildEvictingPrevout(const CTxMemPool& tx_pool)

FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
Expand Down Expand Up @@ -346,6 +347,7 @@ FUZZ_TARGET(ephemeral_package_eval, .init = initialize_tx_pool)

FUZZ_TARGET(tx_package_eval, .init = initialize_tx_pool)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/partially_downloaded_block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ PartiallyDownloadedBlock::CheckBlockFn FuzzedCheckBlock(std::optional<BlockValid

FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};

auto block{ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS)};
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/process_message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ void initialize_process_message()

FUZZ_TARGET(process_message, .init = initialize_process_message)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

ConnmanTestMsg& connman = *static_cast<ConnmanTestMsg*>(g_setup->m_node.connman.get());
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/process_messages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ void initialize_process_messages()

FUZZ_TARGET(process_messages, .init = initialize_process_messages)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

ConnmanTestMsg& connman = *static_cast<ConnmanTestMsg*>(g_setup->m_node.connman.get());
Expand Down
7 changes: 4 additions & 3 deletions src/test/fuzz/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
// 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>
#include <psbt.h>
#include <pubkey.h>
#include <script/script.h>
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/util/random.h>
#include <util/check.h>

#include <cstdint>
Expand All @@ -23,6 +23,7 @@ using node::PSBTInputAnalysis;

FUZZ_TARGET(psbt)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
PartiallySignedTransaction psbt_mut;
std::string error;
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/rbf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ void initialize_package_rbf()

FUZZ_TARGET(rbf, .init = initialize_rbf)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));
std::optional<CMutableTransaction> mtx = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS);
Expand Down Expand Up @@ -92,6 +93,7 @@ FUZZ_TARGET(rbf, .init = initialize_rbf)

FUZZ_TARGET(package_rbf, .init = initialize_package_rbf)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
SetMockTime(ConsumeTime(fuzzed_data_provider));

Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/rolling_bloom_filter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/random.h>
#include <uint256.h>

#include <cassert>
Expand All @@ -16,6 +17,7 @@

FUZZ_TARGET(rolling_bloom_filter)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

CRollingBloomFilter rolling_bloom_filter{
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ void initialize_rpc()

FUZZ_TARGET(rpc, .init = initialize_rpc)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
bool good_data{true};
SetMockTime(ConsumeTime(fuzzed_data_provider));
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/script_sigcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ void initialize_script_sigcache()

FUZZ_TARGET(script_sigcache, .init = initialize_script_sigcache)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

const auto max_sigcache_bytes{fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, DEFAULT_SIGNATURE_CACHE_BYTES)};
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/secp256k1_ecdsa_signature_parse_der_lax.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/util/random.h>

#include <cstdint>
#include <vector>
Expand All @@ -16,6 +17,7 @@ int ecdsa_signature_parse_der_lax(secp256k1_ecdsa_signature* sig, const unsigned

FUZZ_TARGET(secp256k1_ecdsa_signature_parse_der_lax)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
const std::vector<uint8_t> signature_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
if (signature_bytes.data() == nullptr) {
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <primitives/transaction.h>
#include <streams.h>
#include <test/fuzz/fuzz.h>
#include <test/util/random.h>
#include <univalue.h>
#include <util/chaintype.h>
#include <util/rbf.h>
Expand All @@ -28,6 +29,7 @@ void initialize_transaction()

FUZZ_TARGET(transaction, .init = initialize_transaction)
{
SeedRandomStateForTest(SeedRand::ZEROS);
DataStream ds{buffer};
bool valid_tx = true;
const CTransaction tx = [&] {
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/tx_pool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ void CheckATMPInvariants(const MempoolAcceptResult& res, bool txid_in_mempool, b

FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
Expand Down Expand Up @@ -368,6 +369,7 @@ FUZZ_TARGET(tx_pool_standard, .init = initialize_tx_pool)

FUZZ_TARGET(tx_pool, .init = initialize_tx_pool)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
const auto& node = g_setup->m_node;
auto& chainstate{static_cast<DummyChainState&>(node.chainman->ActiveChainstate())};
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/txdownloadman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ void CheckPackageToValidate(const node::PackageToValidate& package_to_validate,

FUZZ_TARGET(txdownloadman, .init = initialize)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

// Initialize txdownloadman
Expand Down Expand Up @@ -294,6 +295,7 @@ static void CheckInvariants(const node::TxDownloadManagerImpl& txdownload_impl,

FUZZ_TARGET(txdownloadman_impl, .init = initialize)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());

// Initialize a TxDownloadManagerImpl
Expand Down
1 change: 1 addition & 0 deletions src/test/fuzz/util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# file COPYING or https://opensource.org/license/mit/.

add_library(test_fuzz STATIC EXCLUDE_FROM_ALL
check_globals.cpp
descriptor.cpp
mempool.cpp
net.cpp
Expand Down
Loading

0 comments on commit a60d570

Please sign in to comment.