aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>2024-12-13 14:42:21 +0100
committerMarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>2024-12-16 15:23:56 +0100
commitfa18acb457e91cc0fa6b3640b6b55c6bc61572ee (patch)
treeeef517dc4409c4d73c915fb1acb20507235cf2e0
parentfa7809aeab838752af94c52977936a8c6555d315 (diff)
fuzz: Abort when using global PRNG without re-seed
-rw-r--r--src/random.cpp2
-rw-r--r--src/test/fuzz/fuzz.cpp9
-rw-r--r--src/test/fuzz/util/CMakeLists.txt1
-rw-r--r--src/test/fuzz/util/check_globals.cpp41
-rw-r--r--src/test/fuzz/util/check_globals.h19
-rw-r--r--src/test/util/random.cpp3
-rw-r--r--src/test/util/random.h4
7 files changed, 77 insertions, 2 deletions
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<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);
}
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 <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>
@@ -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};
}};
@@ -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 <test/fuzz/util/check_globals.h>
+
+#include <test/util/random.h>
+
+#include <iostream>
+#include <memory>
+#include <optional>
+#include <string>
+
+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<CheckGlobalsImpl>()) {}
+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 <memory>
+#include <optional>
+#include <string>
+
+struct CheckGlobalsImpl;
+struct CheckGlobals {
+ CheckGlobals();
+ ~CheckGlobals();
+ std::unique_ptr<CheckGlobalsImpl> 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 <cstdlib>
#include <iostream>
+std::atomic<bool> 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 <random.h>
#include <uint256.h>
+#include <atomic>
#include <cstdint>
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<bool> g_seeded_g_prng_zero;
+extern std::atomic<bool> g_used_g_prng;
+
template <RandomNumberGenerator Rng>
inline CAmount RandMoney(Rng&& rng)
{