Skip to content

Commit fa18acb

Browse files
author
MarcoFalke
committed
fuzz: Abort when using global PRNG without re-seed
1 parent fa7809a commit fa18acb

File tree

7 files changed

+77
-2
lines changed

7 files changed

+77
-2
lines changed

src/random.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,9 +671,11 @@ void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept
671671
{
672672
GetRNGState().MakeDeterministic(seed);
673673
}
674+
std::atomic<bool> g_used_g_prng{false}; // Only accessed from tests
674675

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

src/test/fuzz/fuzz.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <netaddress.h>
88
#include <netbase.h>
9+
#include <test/fuzz/util/check_globals.h>
910
#include <test/util/random.h>
1011
#include <test/util/setup_common.h>
1112
#include <util/check.h>
@@ -78,6 +79,12 @@ void FuzzFrameworkRegisterTarget(std::string_view name, TypeTestOneInput target,
7879
static std::string_view g_fuzz_target;
7980
static const TypeTestOneInput* g_test_one_input{nullptr};
8081

82+
inline void test_one_input(FuzzBufferType buffer)
83+
{
84+
CheckGlobals check{};
85+
(*Assert(g_test_one_input))(buffer);
86+
}
87+
8188
const std::function<std::string()> G_TEST_GET_FULL_NAME{[]{
8289
return std::string{g_fuzz_target};
8390
}};
@@ -210,7 +217,6 @@ void signal_handler(int signal)
210217
// This function is used by libFuzzer
211218
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
212219
{
213-
static const auto& test_one_input = *Assert(g_test_one_input);
214220
test_one_input({data, size});
215221
return 0;
216222
}
@@ -227,7 +233,6 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
227233
int main(int argc, char** argv)
228234
{
229235
initialize();
230-
static const auto& test_one_input = *Assert(g_test_one_input);
231236
#ifdef __AFL_LOOP
232237
// Enable AFL persistent mode. Requires compilation using afl-clang-fast++.
233238
// See fuzzing.md for details.

src/test/fuzz/util/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# file COPYING or https://opensource.org/license/mit/.
44

55
add_library(test_fuzz STATIC EXCLUDE_FROM_ALL
6+
check_globals.cpp
67
descriptor.cpp
78
mempool.cpp
89
net.cpp

src/test/fuzz/util/check_globals.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) 2024-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <test/fuzz/util/check_globals.h>
6+
7+
#include <test/util/random.h>
8+
9+
#include <iostream>
10+
#include <memory>
11+
#include <optional>
12+
#include <string>
13+
14+
struct CheckGlobalsImpl {
15+
CheckGlobalsImpl()
16+
{
17+
g_used_g_prng = false;
18+
g_seeded_g_prng_zero = false;
19+
}
20+
~CheckGlobalsImpl()
21+
{
22+
if (g_used_g_prng && !g_seeded_g_prng_zero) {
23+
std::cerr << "\n\n"
24+
"The current fuzz target used the global random state.\n\n"
25+
26+
"This is acceptable, but requires the fuzz target to call \n"
27+
"SeedRandomStateForTest(SeedRand::ZEROS) at the beginning \n"
28+
"of processing the fuzz input.\n\n"
29+
30+
"An alternative solution would be to avoid any use of globals.\n\n"
31+
32+
"Without a solution, fuzz stability and determinism can lead \n"
33+
"to non-reproducible bugs or inefficient fuzzing.\n\n"
34+
<< std::endl;
35+
std::abort(); // Abort, because AFL may try to recover from a std::exit
36+
}
37+
}
38+
};
39+
40+
CheckGlobals::CheckGlobals() : m_impl(std::make_unique<CheckGlobalsImpl>()) {}
41+
CheckGlobals::~CheckGlobals() = default;

src/test/fuzz/util/check_globals.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright (c) 2024-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H
6+
#define BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H
7+
8+
#include <memory>
9+
#include <optional>
10+
#include <string>
11+
12+
struct CheckGlobalsImpl;
13+
struct CheckGlobals {
14+
CheckGlobals();
15+
~CheckGlobals();
16+
std::unique_ptr<CheckGlobalsImpl> m_impl;
17+
};
18+
19+
#endif // BITCOIN_TEST_FUZZ_UTIL_CHECK_GLOBALS_H

src/test/util/random.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include <cstdlib>
1313
#include <iostream>
1414

15+
std::atomic<bool> g_seeded_g_prng_zero{false};
16+
1517
extern void MakeRandDeterministicDANGEROUS(const uint256& seed) noexcept;
1618

1719
void SeedRandomStateForTest(SeedRand seedtype)
@@ -36,6 +38,7 @@ void SeedRandomStateForTest(SeedRand seedtype)
3638
return GetRandHash();
3739
}();
3840

41+
g_seeded_g_prng_zero = seedtype == SeedRand::ZEROS;
3942
const uint256& seed{seedtype == SeedRand::FIXED_SEED ? ctx_seed : uint256::ZERO};
4043
LogInfo("Setting random seed for current tests to %s=%s\n", RANDOM_CTX_SEED, seed.GetHex());
4144
MakeRandDeterministicDANGEROUS(seed);

src/test/util/random.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <random.h>
1010
#include <uint256.h>
1111

12+
#include <atomic>
1213
#include <cstdint>
1314

1415
enum class SeedRand {
@@ -27,6 +28,9 @@ enum class SeedRand {
2728
/** Seed the global RNG state for testing and log the seed value. This affects all randomness, except GetStrongRandBytes(). */
2829
void SeedRandomStateForTest(SeedRand seed);
2930

31+
extern std::atomic<bool> g_seeded_g_prng_zero;
32+
extern std::atomic<bool> g_used_g_prng;
33+
3034
template <RandomNumberGenerator Rng>
3135
inline CAmount RandMoney(Rng&& rng)
3236
{

0 commit comments

Comments
 (0)