fuzz: enable running fuzz test cases in Debug mode #32113

pull ajtowns wants to merge 2 commits into bitcoin:master from ajtowns:202503-debug-fuzz changing 7 files +79 −16
  1. ajtowns commented at 3:37 pm on March 21, 2025: contributor

    When building with

    BUILD_FOR_FUZZING=OFF
    BUILD_FUZZ_BINARY=ON
    CMAKE_BUILD_TYPE=Debug
    

    allow the fuzz binary to execute given test cases (without actual fuzzing) to make it easier to reproduce fuzz test failures in a more normal debug build.

    In Debug builds, deterministic fuzz behaviour is controlled via a runtime variable, which is normally false, but set to true automatically in the fuzz binary, unless the FUZZ_NONDETERMINISM environment variable is set.

  2. DrahtBot commented at 3:37 pm on March 21, 2025: contributor

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Code Coverage & Benchmarks

    For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32113.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK maflcko, marcofleon, ryanofsky

    If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

    Conflicts

    No conflicts as of last run.

  3. DrahtBot added the label Tests on Mar 21, 2025
  4. ajtowns commented at 3:39 pm on March 21, 2025: contributor
    Related historical PRs: #31191 #24472
  5. maflcko commented at 3:55 pm on March 21, 2025: member

    I don’t think the current patch will work. G_FUZZING influences more than just the behavior of the asserts/assumes. For example:

    • POW checks are different
    • The task runner is different
    • The random seeding is different

    So with this patch it looks like you are instead opting into non-reproducible fuzz behavior.

    In debug mode the performance doesn’t matter, so a possible alternative fix could be to just make G_FUZZING a runtime bool again?

  6. ajtowns commented at 5:58 am on March 22, 2025: contributor

    I don’t think the current patch will work. G_FUZZING influences more than just the behavior of the asserts/assumes. For example:

    * POW checks are different
    * The task runner is different
    * The random seeding is different
    

    Yes – the same fuzz seed will in many cases execute different code paths for the fuzzer when compiled in Debug mode without BUILD_FOR_FUZZING set. But that’s a feature not a bug – the idea is to be able to still run the fuzz tests with a relatively normal production-like build that doesn’t completely special-case out POW checks, random seeding, etc.

    So with this patch it looks like you are instead opting into non-reproducible fuzz behavior.

    This is only for builds where fuzzing isn’t actually happening. ie, you’re getting a seed from a real fuzz run, then retry that seed against a more normal build.

    In debug mode the performance doesn’t matter, so a possible alternative fix could be to just make G_FUZZING a runtime bool again?

    You could make the rng, pow checks etc conditional on something like this, I suppose, which would get you optimal performance in both fuzzing and production builds, and I think pretty good performance in debug builds?

     0inline bool FuzzDetermism()
     1{
     2    if constexpr (G_FUZZING) {
     3         return true;
     4    } else if constexpr (!G_ABORT_ON_FAILED_ASSUME) {
     5        return false;
     6    } else {
     7        return DynamicFuzzDeterminism();
     8    }
     9}
    10
    11bool DynamicFuzzDeterminism()
    12{
    13    static bool fuzz_det = std::getenv("FUZZ_DETERMINISM");
    14    return fuzz_det;    
    15}
    
  7. maflcko commented at 6:16 am on March 22, 2025: member

    But that’s a feature not a bug – the idea is to be able to still run the fuzz tests with a relatively normal production-like build that doesn’t completely special-case out POW checks, random seeding, etc.

    My fear is that some bugs are not reproducible. (I know this is mostly theoretical, because we haven’t seen such a case yet, but still it would be a bit disappointing if someone spent hours trying to reproduce a fuzz run, when it is impossible in a normal debug build)

    inline bool FuzzDetermism()

    lgtm

  8. ajtowns force-pushed on Mar 22, 2025
  9. ajtowns commented at 7:11 am on March 22, 2025: contributor

    lgtm

    Okay, done. How’s that look?

  10. in src/util/check.cpp:39 in e8d04ba086 outdated
    32@@ -33,3 +33,9 @@ void assertion_fail(std::string_view file, int line, std::string_view func, std:
    33     fwrite(str.data(), 1, str.size(), stderr);
    34     std::abort();
    35 }
    36+
    37+bool EnableFuzzDeterminismDynamic()
    38+{
    39+    static bool fuzz_determinism = std::getenv("FUZZ_DETERMINISM") != nullptr;
    


    maflcko commented at 7:19 am on March 22, 2025:

    nit: Forgot to mention this, but to me it seems simpler to have the program automatically set this. Otherwise, it is just busy work for the dev.

    in src/test/fuzz/fuzz.cpp you can set it to true, and default it to false, no?

    This is also the code prior to the changes in https://github.com/bitcoin/bitcoin/pull/31191/files


    ajtowns commented at 9:28 am on March 22, 2025:
    Oh yeah, that’s obviously better.
  11. maflcko approved
  12. ajtowns force-pushed on Mar 22, 2025
  13. ajtowns force-pushed on Mar 22, 2025
  14. in src/test/fuzz/fuzz.cpp:160 in 1718da848a outdated
    157+        if (std::getenv("FUZZ_NONDETERMINISM")) {
    158+            std::cerr << "Warning: FUZZ_NONDETERMINISM env var set, results may be inconsistent with fuzz build" << std::endl;
    159+        } else {
    160+            g_enable_dynamic_fuzz_determinism = true;
    161+            assert(EnableFuzzDeterminism());
    162+        }
    


    maflcko commented at 8:39 am on March 24, 2025:

    Is there a use-case for FUZZ_NONDETERMINISM? IIRC the goal of this change was to achieve fuzz determinism, so adding non-determinism could be left for a follow-up? I’d even argue that it may be better to not add at all, because it is such an edge case that no one will be using it. However, it existing, implies that reviewers will have to take it into account every time, which doesn’t seem worth it.

    If you remove it, you can also move the assert(EnableFuzzDeterminism()); before Assert(!g_test_one_input);, so that it is executed unconditionally.

    edit: nit: The g_enable_dynamic_fuzz_determinism could probably be a plain bool (without atomic).


    ajtowns commented at 11:09 am on March 24, 2025:
    Personally, I’d prefer to run the fuzz corpora against the actual logic not the stubbed out logic, so I’d personally rather run with FUZZ_NONDETERMINISM set. I don’t see why reviewers would have to take this into account at all; it’s just an alternate way to exercise test code, it has no effect on production code.
  15. maflcko commented at 11:31 am on March 24, 2025: member

    lgtm ACK 1718da848a0a7591f0eab086e159f1e9e0f2c59b

    This should also address the concern by @ryanofsky in #31191 (comment) to some extent IIUC. Of course, it is still impossible to execute a fuzz test case normally in a release-mode non-fuzz build, but this pull offers one workaround:

    • Picking a debug-mode build to build all binaries and manually overriding the cxx-flags to increase the optimization level to the level of a release-mode build.

    This workaround could also be used to revert 72ab35a6d09c4f115908a3335d24eb03fd154bf6 and merge the split CI tasks back into one.

  16. in src/util/check.h:43 in 1718da848a outdated
    34+inline bool EnableFuzzDeterminism()
    35+{
    36+    if constexpr (G_FUZZING) {
    37+        return true;
    38+    } else if constexpr (!G_ABORT_ON_FAILED_ASSUME) {
    39+        return false;
    


    ryanofsky commented at 2:52 pm on March 24, 2025:

    In commit “fuzz: enable running fuzz test cases in Debug mode” (1718da848a0a7591f0eab086e159f1e9e0f2c59b)

    I don’t think I the understand reason for ABORT_ON_FAILED_ASSUME affecting return value of EnableFuzzDeterminism(), and trying to force non-determinism when assume doesn’t abort. It seems like this behavior might cause the assert(EnableFuzzDeterminism()) to fail, but also this code might just be easier to understand if fuzz determinism only depended on the FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION build option and FUZZ_NONDETERMINISM environment variable and didn’t have this third factor.

    Is the reason maybe that this is trying to optimize code and avoid needing to access the g_enable_dynamic_fuzz_determinism variable in release builds? IMO would be good to either remove the !G_ABORT_ON_FAILED_ASSUME condition or document why it’s here.


    maflcko commented at 8:17 am on March 26, 2025:

    Is the reason maybe that this is trying to optimize code and avoid needing to access the g_enable_dynamic_fuzz_determinism variable in release builds?

    Yes, that’s the reason as I understand it. I am happy to re-ack a simple doc fixup like:

    0return false; // Allow optimized code for release builds (non-fuzz and non-debug).
    

    ajtowns commented at 9:14 am on March 26, 2025:

    I don’t think I the understand reason for ABORT_ON_FAILED_ASSUME affecting return value of EnableFuzzDeterminism(), and trying to force non-determinism when assume doesn’t abort.

    The initial thought was actually more that the running the fuzz tests without Assumes being checked isn’t going to be anywhere near as thorough, particularly influenced by the logic documented in #32100. Added some comment text on this.


    ryanofsky commented at 2:04 pm on April 3, 2025:

    re: #32113 (review)

    Thanks, new comment definitely helps and makes it clear why it is safe to return false here. The thing comment doesn’t explain is why the else if constexpr (!G_ABORT_ON_FAILED_ASSUME) block is needed at all. It seems like if you removed it behavior would be the same because g_enable_dynamic_fuzz_determinism would already be false. (because it is false by default and fuzz binary won’t run at all in this case so won’t set g_enable_dynamic_fuzz_determinism to true).

    So maybe the best rationale for keeping this check is the one Marco gave, as an optimization to make CheckProofOfWork() faster in release builds, since CheckProofOfWork() is the only non-test function running in release build that calls EnableFuzzDeterminism().

    I am not really sure the constexpr optimizations in this PR are really worth the complexity they add, but they seem fine and reasonable to keep to be conservative.


    maflcko commented at 2:48 pm on April 3, 2025:
    Another reason to have the constexpr condition here, even if it is redundant and possibly doesn’t result in a speed-up, is that it serves as a belt-and-suspenders when g_enable_dynamic_fuzz_determinism is erroneously set in the release build, or a reviewer wonders if such thing is possible. Having a plain hard-coded return false is a clear and easy answer for humans and compilers.

    maflcko commented at 3:05 pm on April 3, 2025:
    Also, just because there may not be a performance difference today, doesn’t mean there will be none in the future. So just to avoid to having to touch all of this code again in the future, it seems best to keep this constexpr.

    ryanofsky commented at 0:32 am on April 23, 2025:

    re: #32113

    Also, just because there may not be a performance difference today, doesn’t mean there will be none in the future.

    Am dubious of this. In general I’d think we’d want to have as few differences as possible between fuzzing and non-fuzzing code code paths and right now there is only 1 difference (outside of test code) in CheckProofOfWork(). Even if we were going to add more differences it’s hard to imagine wanting to add them in hot code paths where could be a noticeable effect on performance. So I think the simplification I suggested collapsing confusing EnableFuzzDeterminism() G_FUZZING_BUILD and g_enable_dynamic_fuzz_determinism definitions into a simple g_fuzzing variable would make this code a lot simpler and be an improvement. But it could easily be done later as a followup.

  17. ryanofsky approved
  18. ryanofsky commented at 2:53 pm on March 24, 2025: contributor
    Concept & approach ACK. The changes here all seem reasonable and I get abstractly how it could be useful to run fuzz binary without fuzzing compile options and instrumentation. But I only have a vague idea of how this should get used in practice. It would be helpful to add some documentation to https://github.com/bitcoin/bitcoin/blob/master/doc/fuzzing.md to say how/when you could run the fuzz binary in debug mode, and when you would might want to set the FUZZ_NONDETERMINISM environment variable.
  19. ajtowns force-pushed on Mar 26, 2025
  20. maflcko commented at 9:18 am on March 26, 2025: member

    re-review-only-ACK 1f072cfb108d8f71daf6281d97353ff755af9569

    Only change is a comment

  21. DrahtBot requested review from ryanofsky on Mar 26, 2025
  22. ajtowns commented at 2:09 pm on March 26, 2025: contributor

    But I only have a vague idea of how this should get used in practice. It would be helpful to add some documentation to https://github.com/bitcoin/bitcoin/blob/master/doc/fuzzing.md to say how/when you could run the fuzz binary in debug mode, and when you would might want to set the FUZZ_NONDETERMINISM environment variable.

    I think there’s two reasons to do this:

    • It’s an easier way to reproduce most fuzzer crashes (ie, ones that don’t need a sanitizer to trigger); just grab your existing debug build, and run

      0$ FUZZ=process_message build_debug/bin/fuzz corpus/1bc91feec9fc00b107d97dc225a9f2cdaa078eb6`
      

      and it’s also compatible with gdb

      0$ FUZZ=process_message gdb build_debug/bin/fuzz
      1run corpus/1bc91feec9fc00b107d97dc225a9f2cdaa078eb6
      
    • It lets you include the fuzz test corpora (assuming they all pass) in a regular code coverage build, something like:

      0PRINT_ALL_FUZZ_TARGETS_AND_ABORT=1 ../build/bin/fuzz  | 
      1  while read tgt; do
      2     d=qa-assets/fuzz_corpora/$tgt;
      3     if [ -d "$d" ]; then
      4       (echo "*** $tgt ***"; 
      5        time FUZZ=$tgt ../build/bin/fuzz $d) >OUT_$tgt.log 2>&1 &
      6     else
      7       echo SKIPPED $tgt -- no corpus;
      8     fi;
      9   done
      

    Not really sure what should go in the docs or not, hopefully the above is enough explanation if someone else wants to add something.

  23. maflcko commented at 2:28 pm on March 26, 2025: member
    • It lets you include the fuzz test corpora (assuming they all pass) in a regular code coverage build, something like:

    Maybe I am missing something, but I guess you meant “debug mode coverage build” instead of “regular code coverage build”, because the regular coverage build has neither fuzz, nor debug enabled. For me it looks something like:

    0C++ compiler .......................... GNU 14.2.0, /usr/bin/g++
    1CMAKE_BUILD_TYPE ...................... Coverage
    2Preprocessor defined macros ........... 
    3C++ compiler flags .................... -fprofile-update=atomic -g -Og --coverage -std=c++20 -fPIC -fno-extended-identifiers -fdebug-prefix-map=/tmp/cirrus-ci-build/bitcoin-core/src=. -fmacro-prefix-map=/tmp/cirrus-ci-build/bitcoin-core/src=. -fstack-reuse=none -Wall -Wextra -Wformat -Wformat-security -Wvla -Wredundant-decls -Wdate-time -Wduplicated-branches -Wduplicated-cond -Wlogical-op -Woverloaded-virtual -Wsuggest-override -Wimplicit-fallthrough -Wunreachable-code -Wbidi-chars=any -Wundef -Wno-unused-parameter -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 -Wstack-protector -fstack-protector-all -fcf-protection=full -fstack-clash-protection
    
  24. ajtowns commented at 5:18 pm on March 26, 2025: contributor
    • It lets you include the fuzz test corpora (assuming they all pass) in a regular code coverage build, something like:

    Maybe I am missing something, but I guess you meant “debug mode coverage build” instead of “regular code coverage build”, because the regular coverage build has neither fuzz, nor debug enabled. For me it looks something like:

    Sure; I meant “regular” just in “it builds all the binaries and you can run non-fuzz tests”.

  25. fanquake requested review from dergoegge on Mar 27, 2025
  26. fanquake requested review from marcofleon on Mar 27, 2025
  27. DrahtBot added the label Needs rebase on Apr 2, 2025
  28. ajtowns force-pushed on Apr 3, 2025
  29. ajtowns commented at 0:26 am on April 3, 2025: contributor
    Rebased over #32158
  30. DrahtBot removed the label Needs rebase on Apr 3, 2025
  31. DrahtBot added the label CI failed on Apr 3, 2025
  32. DrahtBot removed the label CI failed on Apr 3, 2025
  33. maflcko commented at 11:24 am on April 3, 2025: member

    almost c94e02c1b2017ac054eb6a373ca5f31c589bc601 👻

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: almost c94e02c1b2017ac054eb6a373ca5f31c589bc601 👻
    3CsF0qA4lNGlpxYIz0VgrSJMXtzy9EYYMBa7p+DelIzWTd4uuySTkqFOubu2FN4Z8V7SDQSk94TASNla7aH5ACw==
    

    There are several issues:

    • A silent merge conflict was ignored, and silent merge conflicts can happen in the future again.
    • G_FUZZING is confusing, because it doesn’t say anything about whether fuzzing is happening right now, rather whether the exe was built for fuzzing.

    My recommendation would be to just rename G_FUZZING to G_FUZZ_BUILD to fix all issues.

  34. ryanofsky approved
  35. ryanofsky commented at 2:27 pm on April 3, 2025: contributor

    Code review ACK c94e02c1b2017ac054eb6a373ca5f31c589bc601. It seems like a nice change to let the fuzz binary run in debug builds, and I found explanation of the motivation in #32113 (comment) very helpful.

    I still do think the new options could be documented better, so attempted to write some documentation (feel free to use this or adapt or ignore if you think it’s not helpful):

     0--- a/doc/fuzzing.md
     1+++ b/doc/fuzzing.md
     2@@ -143,6 +143,37 @@ If you find coverage increasing inputs when fuzzing you are highly encouraged to
     3 
     4 Every single pull request submitted against the Bitcoin Core repo is automatically tested against all inputs in the [`bitcoin-core/qa-assets`](https://github.com/bitcoin-core/qa-assets) repo. Contributing new coverage increasing inputs is an easy way to help make Bitcoin Core more robust.
     5 
     6+## Building and debugging fuzz tests
     7+
     8+There are 3 ways fuzz tests can be built:
     9+
    10+1. With `-DBUILD_FOR_FUZZING=ON` which forces on fuzz determinism (skipping
    11+   proof of work checks, disabling random number seeding, disabling clock time)
    12+   and causes `Assume()` checks to abort on failure.
    13+
    14+   This is the normal way to run fuzz tests and generate new inputs. Because
    15+   determinism is hardcoded on in this build, only the fuzz binary can be built
    16+   and all other binaries are disabled.
    17+
    18+2. With `-DBUILD_FUZZ_BINARY=ON -DCMAKE_BUILD_TYPE=Debug` which causes
    19+   `Assume()` checks to abort on failure, and enables fuzz determinism, but
    20+   makes it optional.
    21+
    22+   Determinism is turned on in the fuzz binary by default, but can be turned off
    23+   by setting the `FUZZ_NONDETERMINISM` environment variable to any value, which
    24+   may be useful for running fuzz tests with code that deterministic execution
    25+   would otherwise skip.
    26+
    27+   Since `BUILD_FUZZ_BINARY`, unlike `BUILD_FOR_FUZZING`, does not hardcode on
    28+   determinism, this allows non-fuzz binaries to coexist in the same build,
    29+   making it possible to reproduce fuzz test failures in a normal build.
    30+
    31+3. With `-DBUILD_FUZZ_BINARY=ON -DCMAKE_BUILD_TYPE=Release`. In this build, the
    32+   fuzz binary will build but refuse to run, because in release builds
    33+   determinism is forced off and `Assume()` checks do not abort, so running the
    34+   tests would not be useful. This build is only useful for ensuring fuzz tests
    35+   compile and link.
    36+
    37 ## macOS hints for libFuzzer
    38 
    39 The default Clang/LLVM version supplied by Apple on macOS does not include
    

    I also think the implementation is probably more complicated than it needs to be because of the constexpr optimizations it is doing. But Ihis could be simplified later and I can understand wanting to be conservative about performance.

  36. DrahtBot requested review from maflcko on Apr 3, 2025
  37. ryanofsky commented at 2:48 pm on April 3, 2025: contributor

    re: #32113#pullrequestreview-2739879114

    I also think the implementation is probably more complicated than it needs to be because of the constexpr optimizations it is doing. But Ihis could be simplified later and I can understand wanting to be conservative about performance.

    Here is the simplification I have in mind. I do not think would be bad for performance because the only release code checking the g_fuzzing variable here is in CheckProofOfWork(). But I could be wrong about something and in any case these changes could be a followup and shouldn’t block this PR:

      0--- a/CMakeLists.txt
      1+++ b/CMakeLists.txt
      2@@ -230,7 +230,7 @@ if(BUILD_FOR_FUZZING)
      3   set(BUILD_FUZZ_BINARY ON)
      4 
      5   target_compile_definitions(core_interface INTERFACE
      6-    FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
      7+    BUILD_FOR_FUZZING
      8   )
      9 endif()
     10 
     11--- a/src/pow.cpp
     12+++ b/src/pow.cpp
     13@@ -139,7 +139,7 @@ bool PermittedDifficultyTransition(const Consensus::Params& params, int64_t heig
     14 // the most significant bit of the last byte of the hash is set.
     15 bool CheckProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params& params)
     16 {
     17-    if (EnableFuzzDeterminism()) return (hash.data()[31] & 0x80) == 0;
     18+    if (g_fuzzing) return (hash.data()[31] & 0x80) == 0;
     19     return CheckProofOfWorkImpl(hash, nBits, params);
     20 }
     21 
     22--- a/src/test/fuzz/fuzz.cpp
     23+++ b/src/test/fuzz/fuzz.cpp
     24@@ -147,17 +147,15 @@ static void initialize()
     25         std::cerr << "No fuzz target compiled for " << g_fuzz_target << "." << std::endl;
     26         std::exit(EXIT_FAILURE);
     27     }
     28-    if constexpr (!G_FUZZING && !G_ABORT_ON_FAILED_ASSUME) {
     29+    if constexpr (!G_ABORT_ON_FAILED_ASSUME) {
     30         std::cerr << "Must compile with -DBUILD_FOR_FUZZING=ON or in Debug mode to execute a fuzz target." << std::endl;
     31         std::exit(EXIT_FAILURE);
     32     }
     33-    if (!EnableFuzzDeterminism()) {
     34-        if (std::getenv("FUZZ_NONDETERMINISM")) {
     35-            std::cerr << "Warning: FUZZ_NONDETERMINISM env var set, results may be inconsistent with fuzz build" << std::endl;
     36-        } else {
     37-            g_enable_dynamic_fuzz_determinism = true;
     38-            assert(EnableFuzzDeterminism());
     39-        }
     40+    if (std::getenv("FUZZ_NONDETERMINISM")) {
     41+        std::cerr << "Warning: FUZZ_NONDETERMINISM env var set, results may be inconsistent with fuzz build" << std::endl;
     42+    } else {
     43+        g_fuzzing = true;
     44+        assert(g_fuzzing);
     45     }
     46     Assert(!g_test_one_input);
     47     g_test_one_input = &it->second.test_one_input;
     48--- a/src/test/util/random.cpp
     49+++ b/src/test/util/random.cpp
     50@@ -25,7 +25,7 @@ void SeedRandomStateForTest(SeedRand seedtype)
     51     // no longer truly random. It should be enough to get the seed once for the
     52     // process.
     53     static const auto g_ctx_seed = []() -> std::optional<uint256> {
     54-        if (EnableFuzzDeterminism()) return {};
     55+        if (g_fuzzing) return {};
     56         // If RANDOM_CTX_SEED is set, use that as seed.
     57         if (const char* num{std::getenv(RANDOM_CTX_SEED)}) {
     58             if (auto num_parsed{uint256::FromUserHex(num)}) {
     59@@ -40,7 +40,7 @@ void SeedRandomStateForTest(SeedRand seedtype)
     60     }();
     61 
     62     g_seeded_g_prng_zero = seedtype == SeedRand::ZEROS;
     63-    if (EnableFuzzDeterminism()) {
     64+    if (g_fuzzing) {
     65         Assert(g_seeded_g_prng_zero); // Only SeedRandomStateForTest(SeedRand::ZEROS) is allowed in fuzz tests
     66         Assert(!g_used_g_prng);       // The global PRNG must not have been used before SeedRandomStateForTest(SeedRand::ZEROS)
     67     }
     68--- a/src/test/util/setup_common.cpp
     69+++ b/src/test/util/setup_common.cpp
     70@@ -112,7 +112,7 @@ static void ExitFailure(std::string_view str_err)
     71 BasicTestingSetup::BasicTestingSetup(const ChainType chainType, TestOpts opts)
     72     : m_args{}
     73 {
     74-    if (!EnableFuzzDeterminism()) {
     75+    if (!g_fuzzing) {
     76         SeedRandomForTest(SeedRand::FIXED_SEED);
     77     }
     78     m_node.shutdown_signal = &m_interrupt;
     79@@ -203,7 +203,7 @@ BasicTestingSetup::~BasicTestingSetup()
     80 {
     81     m_node.ecc_context.reset();
     82     m_node.kernel.reset();
     83-    if (!EnableFuzzDeterminism()) {
     84+    if (!g_fuzzing) {
     85         SetMockTime(0s); // Reset mocktime for following tests
     86     }
     87     LogInstance().DisconnectTestLogger();
     88@@ -229,7 +229,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts)
     89         m_node.scheduler->m_service_thread = std::thread(util::TraceThread, "scheduler", [&] { m_node.scheduler->serviceQueue(); });
     90         m_node.validation_signals =
     91             // Use synchronous task runner while fuzzing to avoid non-determinism
     92-            EnableFuzzDeterminism() ?
     93+            g_fuzzing ?
     94                 std::make_unique<ValidationSignals>(std::make_unique<util::ImmediateTaskRunner>()) :
     95                 std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(*m_node.scheduler));
     96         {
     97@@ -256,7 +256,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts)
     98             .notifications = *m_node.notifications,
     99             .signals = m_node.validation_signals.get(),
    100             // Use no worker threads while fuzzing to avoid non-determinism
    101-            .worker_threads_num = G_FUZZING ? 0 : 2,
    102+            .worker_threads_num = g_fuzzing ? 0 : 2,
    103         };
    104         if (opts.min_validation_cache) {
    105             chainman_opts.script_execution_cache_bytes = 0;
    106--- a/src/util/check.cpp
    107+++ b/src/util/check.cpp
    108@@ -34,4 +34,4 @@ void assertion_fail(std::string_view file, int line, std::string_view func, std:
    109     std::abort();
    110 }
    111 
    112-std::atomic<bool> g_enable_dynamic_fuzz_determinism{false};
    113+std::atomic<bool> g_fuzzing{false};
    114--- a/src/util/check.h
    115+++ b/src/util/check.h
    116@@ -14,37 +14,15 @@
    117 #include <string_view>
    118 #include <utility>
    119 
    120-constexpr bool G_FUZZING{
    121-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    122-    true
    123-#else
    124-    false
    125-#endif
    126-};
    127 constexpr bool G_ABORT_ON_FAILED_ASSUME{
    128-#ifdef ABORT_ON_FAILED_ASSUME
    129+#if defined(ABORT_ON_FAILED_ASSUME) || defined(BUILD_FOR_FUZZING)
    130     true
    131 #else
    132     false
    133 #endif
    134 };
    135 
    136-extern std::atomic<bool> g_enable_dynamic_fuzz_determinism;
    137-
    138-inline bool EnableFuzzDeterminism()
    139-{
    140-    if constexpr (G_FUZZING) {
    141-        return true;
    142-    } else if constexpr (!G_ABORT_ON_FAILED_ASSUME) {
    143-        // Running fuzz tests is always disabled if Assume() doesn't abort
    144-        // (ie, non-fuzz non-debug builds), as otherwise tests which
    145-        // should fail due to a failing Assume may still pass. As such,
    146-        // we also statically disable fuzz determinism in that case.
    147-        return false;
    148-    } else {
    149-        return g_enable_dynamic_fuzz_determinism;
    150-    }
    151-}
    152+extern std::atomic<bool> g_fuzzing;
    153 
    154 std::string StrFormatInternalBug(std::string_view msg, std::string_view file, int line, std::string_view func);
    155 
    156@@ -75,7 +53,7 @@ void assertion_fail(std::string_view file, int line, std::string_view func, std:
    157 template <bool IS_ASSERT, typename T>
    158 constexpr T&& inline_assertion_check(LIFETIMEBOUND T&& val, [[maybe_unused]] const char* file, [[maybe_unused]] int line, [[maybe_unused]] const char* func, [[maybe_unused]] const char* assertion)
    159 {
    160-    if (IS_ASSERT || std::is_constant_evaluated() || G_FUZZING || G_ABORT_ON_FAILED_ASSUME) {
    161+    if (IS_ASSERT || std::is_constant_evaluated() || G_ABORT_ON_FAILED_ASSUME) {
    162         if (!val) {
    163             assertion_fail(file, line, func, assertion);
    164         }
    
  38. marcofleon commented at 4:28 pm on April 7, 2025: contributor

    tACK c94e02c1b2017ac054eb6a373ca5f31c589bc601 (assuming #32113 (comment) gets addressed)

    BUILD_FOR_FUZZING=ON: continues to work as expected, debug and non-debug

    non-debug BUILD_FUZZ_BINARY=ON: fuzzing doesn’t work and the proper message is displayed, same behavior as on master

    debug BUILD_FUZZ_BINARY=ON: I tested by running the p2p_headers_presync target, which stalls if actual proof of work is being checked. The target only stalls when FUZZ_NONDETERMINISM is set, which confirms that fuzzing is now possible in a debug build and that the environment variable works as expected. Running on a single input without libFuzzer is also possible.

    I like this solution. In terms of documentation, the only thing I’d like to clarify (maybe for those with less experience fuzzing) is that it is also possible to do BUILD_FOR_FUZZING=ON and Debug mode, if having a fuzz-only debug build is preferred.

  39. fanquake commented at 12:09 pm on April 17, 2025: member
  40. fuzz: enable running fuzz test cases in Debug mode
    When building with
    
     BUILD_FOR_FUZZING=OFF
     BUILD_FUZZ_BINARY=ON
     CMAKE_BUILD_TYPE=Debug
    
    allow the fuzz binary to execute given test cases (without actual
    fuzzing) to make it easier to reproduce fuzz test failures in a more
    normal debug build.
    
    In Debug builds, deterministic fuzz behaviour is controlled via a runtime
    variable, which is normally false, but set to true automatically in the
    fuzz binary, unless the FUZZ_NONDETERMINISM environment variable is set.
    c1d01f59ac
  41. doc: Document fuzz build options
    Co-Authored-By: Ryan Ofsky <ryan@ofsky.org>
    3669ecd4cc
  42. ajtowns force-pushed on Apr 22, 2025
  43. ajtowns commented at 8:02 am on April 22, 2025: contributor
    Renamed to G_FUZZING_BUILD. Added ryanofsky’s docs. Left the constexpr optimisations in.
  44. maflcko commented at 9:09 am on April 22, 2025: member

    re-ACK 3669ecd4ccd8e7a1e2b1a9dcbe708c51c78e4d6c 🏉

    Signature:

    0untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    1RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    2trusted comment: re-ACK 3669ecd4ccd8e7a1e2b1a9dcbe708c51c78e4d6c 🏉
    3ScshjrOwYsAHygc804zZswHEKt2qqexPuv1hlidW32TDUA7xq9fUsjMKciAkxBw47F+LLD1DP54wotukqyFsBA==
    
  45. DrahtBot requested review from marcofleon on Apr 22, 2025
  46. DrahtBot requested review from ryanofsky on Apr 22, 2025
  47. marcofleon commented at 10:04 am on April 22, 2025: contributor
    re ACK 3669ecd4ccd8e7a1e2b1a9dcbe708c51c78e4d6c
  48. ryanofsky approved
  49. ryanofsky commented at 0:34 am on April 23, 2025: contributor
    Code review ACK 3669ecd4ccd8e7a1e2b1a9dcbe708c51c78e4d6c with just variable renamed and documentation added since last review
  50. ryanofsky assigned ryanofsky on Apr 23, 2025
  51. ryanofsky merged this on Apr 23, 2025
  52. ryanofsky closed this on Apr 23, 2025


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-05-05 15:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me