From fa18acb457e91cc0fa6b3640b6b55c6bc61572ee Mon Sep 17 00:00:00 2001 From: MarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz> Date: Fri, 13 Dec 2024 14:42:21 +0100 Subject: fuzz: Abort when using global PRNG without re-seed --- src/random.cpp | 2 ++ src/test/fuzz/fuzz.cpp | 9 ++++++-- src/test/fuzz/util/CMakeLists.txt | 1 + src/test/fuzz/util/check_globals.cpp | 41 ++++++++++++++++++++++++++++++++++++ src/test/fuzz/util/check_globals.h | 19 +++++++++++++++++ src/test/util/random.cpp | 3 +++ src/test/util/random.h | 4 ++++ 7 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/test/fuzz/util/check_globals.cpp create mode 100644 src/test/fuzz/util/check_globals.h diff --git a/src/random.cpp b/src/random.cpp index 9b5131023f..4265c48022 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -671,9 +671,11 @@ void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept { GetRNGState().MakeDeterministic(seed); } +std::atomic g_used_g_prng{false}; // Only accessed from tests void GetRandBytes(Span bytes) noexcept { + g_used_g_prng = true; ProcRand(bytes.data(), bytes.size(), RNGLevel::FAST, /*always_use_real_rng=*/false); } diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index 3c81e0f3fe..e4e4723c74 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -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 G_TEST_GET_FULL_NAME{[]{ return std::string{g_fuzz_target}; }}; @@ -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; } @@ -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. diff --git a/src/test/fuzz/util/CMakeLists.txt b/src/test/fuzz/util/CMakeLists.txt index f73a1a83c2..878286b0f4 100644 --- a/src/test/fuzz/util/CMakeLists.txt +++ b/src/test/fuzz/util/CMakeLists.txt @@ -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 diff --git a/src/test/fuzz/util/check_globals.cpp b/src/test/fuzz/util/check_globals.cpp new file mode 100644 index 0000000000..4b74ddedd6 --- /dev/null +++ b/src/test/fuzz/util/check_globals.cpp @@ -0,0 +1,41 @@ +// Copyright (c) 2024-present 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 + +#include + +#include +#include +#include +#include + +struct CheckGlobalsImpl { + CheckGlobalsImpl() + { + g_used_g_prng = false; + g_seeded_g_prng_zero = false; + } + ~CheckGlobalsImpl() + { + if (g_used_g_prng && !g_seeded_g_prng_zero) { + std::cerr << "\n\n" + "The current fuzz target used the global random state.\n\n" + + "This is acceptable, but requires the fuzz target to call \n" + "SeedRandomStateForTest(SeedRand::ZEROS) at the beginning \n" + "of processing the fuzz input.\n\n" + + "An alternative solution would be to avoid any use of globals.\n\n" + + "Without a solution, fuzz stability and determinism can lead \n" + "to non-reproducible bugs or inefficient fuzzing.\n\n" + << std::endl; + std::abort(); // Abort, because AFL may try to recover from a std::exit + } + } +}; + +CheckGlobals::CheckGlobals() : m_impl(std::make_unique()) {} +CheckGlobals::~CheckGlobals() = default; diff --git a/src/test/fuzz/util/check_globals.h b/src/test/fuzz/util/check_globals.h new file mode 100644 index 0000000000..79f247535a --- /dev/null +++ b/src/test/fuzz/util/check_globals.h @@ -0,0 +1,19 @@ +// Copyright (c) 2024-present The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H +#define BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H + +#include +#include +#include + +struct CheckGlobalsImpl; +struct CheckGlobals { + CheckGlobals(); + ~CheckGlobals(); + std::unique_ptr m_impl; +}; + +#endif // BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H diff --git a/src/test/util/random.cpp b/src/test/util/random.cpp index 32d785e45d..8757ab36fb 100644 --- a/src/test/util/random.cpp +++ b/src/test/util/random.cpp @@ -12,6 +12,8 @@ #include #include +std::atomic g_seeded_g_prng_zero{false}; + extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept; void SeedRandomStateForTest(SeedRand seedtype) @@ -36,6 +38,7 @@ void SeedRandomStateForTest(SeedRand seedtype) return GetRandHash(); }(); + g_seeded_g_prng_zero = seedtype == SeedRand::ZEROS; const uint256& seed{seedtype == SeedRand::FIXED_SEED ? ctx_seed : uint256::ZERO}; LogInfo("Setting random seed for current tests to %s=%s\n", RANDOM_CTX_SEED, seed.GetHex()); MakeRandDeterministicDANGEROUS(seed); diff --git a/src/test/util/random.h b/src/test/util/random.h index 441150e666..47bc7a18f9 100644 --- a/src/test/util/random.h +++ b/src/test/util/random.h @@ -9,6 +9,7 @@ #include #include +#include #include enum class SeedRand { @@ -27,6 +28,9 @@ enum class SeedRand { /** Seed the global RNG state for testing and log the seed value. This affects all randomness, except GetStrongRandBytes(). */ void SeedRandomStateForTest(SeedRand seed); +extern std::atomic g_seeded_g_prng_zero; +extern std::atomic g_used_g_prng; + template inline CAmount RandMoney(Rng&& rng) { -- cgit v1.2.3