fuzz: Avoid time-based "non-determinism" in fuzzing harnesses by using mocked GetTime() #20437

pull practicalswift wants to merge 1 commits into bitcoin:master from practicalswift:fuzzers-remove-time-based-non-determinism changing 4 files +4 −1
  1. practicalswift commented at 9:06 PM on November 20, 2020: contributor

    Avoid time-based "non-determinism" in fuzzing harnesses by using mocked GetTime().

    Prior to this commit the fuzzing harnesses banman, connman, net and rbf had time-based "non-determinism". addrman is fixed in #20425. process_message and process_messages are left to fix: simply using mock time is not enough for them due to interaction with IsInitialBlockDownload().

    See doc/fuzzing.md for information on how to fuzz Bitcoin Core. Don't forget to contribute any coverage increasing inputs you find to the Bitcoin Core fuzzing corpus repo.

    Happy fuzzing :)

  2. fanquake added the label Tests on Nov 20, 2020
  3. MONIMAKER365 approved
  4. Crypt-iQ commented at 7:23 PM on November 26, 2020: contributor

    utACK 892e7ddbd6f85653ee775c34f07b146c6f3b628e

  5. MarcoFalke commented at 2:03 PM on November 27, 2020: member

    Why pick a time that won't ever be hit in practice? Why pick a time that makes it impossible to go back in time?

  6. practicalswift commented at 2:25 PM on November 27, 2020: contributor

    @MarcoFalke

    I choose SetMockTime(1) because SetMockTime(0) is a no-op, and it seemed to me that SetMockTime(1) is commonly use in src/test/:

    $ git grep SetMockTime "src/test/" | sed 's/^.*: *//g' | grep ^SetMockTime | sort | uniq -c | sort -rn
          5 SetMockTime(0);
          3 SetMockTime(2);
          3 SetMockTime(1);
          1 SetMockTime(nStartTime); // Overrides future calls to GetTime()
          1 SetMockTime(nStartTime+24*60);
          1 SetMockTime(nStartTime+21*60);
          1 SetMockTime(GetTime() + 3 * chainparams.GetConsensus().nPowTargetSpacing + 1);
          1 SetMockTime(ConsumeTime(fuzzed_data_provider));
          1 SetMockTime(42);
          1 SetMockTime(111);
    

    I see your point about using a constant that is more realistic (higher) and allows for going back in time. It seems like you had a number in mind: give me your suggested constant and I'll happily use it :)

  7. MarcoFalke commented at 9:38 AM on November 28, 2020: member

    SetMockTime(1) in existing tests in only used to check the difference between SetMockTime(2) and SetMockTime(1) is 1. I don't think one unit test case picking a random number should be a reason for all fuzz tests to pick the same number.

    Ideally the fuzz engine picks the number, so that additional coverage can be achieved (if there is any). If there is no additional coverage to be achieved by mocktime, at worst all seeds will increase in size by a few bytes.

  8. practicalswift commented at 10:28 PM on November 30, 2020: contributor

    @MarcoFalke Oh, I should have clarified that the current patch was written to avoid time-based "non-determinism" without invalidating any seed corpora. Hence the constant rather than a fuzzer provided value.

    I agree that if we are okay with invalidating seeds then SetMockTime(ConsumeTime(fuzzed_data_provider)); is obviously the superior choice. (FWIW I'm not opposed to invalidating seeds for these harnesses.)

    I think we still need a constant for the -mocktime parameter passed to BasicTestingSetup though (for InitializeFuzzingContext). Did you have any number in mind that is better than 1? :)

    I'll fix SetMockTime(ConsumeTime(fuzzed_data_provider)); and -mocktime=N when we've chosen the most appropriate default :)

  9. MarcoFalke commented at 6:35 AM on December 1, 2020: member

    I don't think we should be using -mocktime=N at all. This is just hiding the issue without actually improving anything.

  10. practicalswift force-pushed on Dec 1, 2020
  11. practicalswift commented at 12:34 PM on December 1, 2020: contributor

    @MarcoFalke Now using SetMockTime(ConsumeTime(fuzzed_data_provider)); and no -mocktime=N. Please re-review :)

  12. in src/test/fuzz/util.h:102 in 8651384c09 outdated
      97 | @@ -98,7 +98,8 @@ template <typename T>
      98 |  
      99 |  [[nodiscard]] inline int64_t ConsumeTime(FuzzedDataProvider& fuzzed_data_provider) noexcept
     100 |  {
     101 | -    static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:00Z");
     102 | +    // Avoid t=0 (1970-01-01T00:00:00Z) since SetMockTime(0) is a no-op.
     103 | +    static const int64_t time_min = ParseISO8601DateTime("1970-01-01T00:00:01Z");
    


    MarcoFalke commented at 1:07 PM on December 1, 2020:

    needs rebase


    practicalswift commented at 1:31 PM on December 1, 2020:

    Rebased! :)

  13. fuzz: Avoid time-based "non-determinism" in fuzzing harnesses by using mocked GetTime() 8c09c0c1d1
  14. practicalswift force-pushed on Dec 1, 2020
  15. DrahtBot commented at 5:27 PM on December 4, 2020: member

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #20649 (refactor: Remove nMyStartingHeight from CNode/Connman by MarcoFalke)
    • #20228 (addrman: Make addrman a top-level component by jnewbery)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  16. practicalswift commented at 12:58 PM on December 15, 2020: contributor

    Anything left to do here? :)

  17. MarcoFalke commented at 2:54 PM on December 15, 2020: member

    review ACK 8c09c0c1d18885ef94f79b3f2d073f43269bc95d

    Though, process_message* is still using non-mocked time. Also, is there a way to test? Locally the targets are non-deterministic before and after this change.

  18. practicalswift commented at 3:55 PM on December 15, 2020: contributor

    Though, process_message* is still using non-mocked time.

    Thanks for reviewing! :)

    process_message and process_messages are left to fix: simply using mock time is not enough for them due to interaction with IsInitialBlockDownload() (see OP description).

    Also, is there a way to test? Locally the targets are non-deterministic before and after this change.

    One way to test that time-based non-determinism is removed is probably to introduce if (g_fuzzing_started) { assert(false); } in the relevant part of GetTime, and set g_fuzzing_started = true; in test_one_input. Then compare the results before and after this patch :)

  19. MarcoFalke commented at 4:11 PM on December 15, 2020: member

    One way to test ...

    That is what I did. I was hoping there is better tooling out there to detect non-determinism in fuzz tests. I guess we'll have to roll our own...

  20. MarcoFalke commented at 4:13 PM on December 15, 2020: member

    Btw, if you quote ACKs, it will look like you did a review in the merge commit

  21. MarcoFalke merged this on Dec 15, 2020
  22. MarcoFalke closed this on Dec 15, 2020

  23. practicalswift commented at 4:15 PM on December 15, 2020: contributor

    One way to test ...

    That is what I did. I was hoping there is better tooling out there to detect non-determinism in fuzz tests. I guess we'll have to roll our own...

    I've thought about modifying contrib/devtools/test_deterministic_coverage.sh to add support for running the fuzz tests instead, but I haven't had time for that yet :)

  24. sidhujag referenced this in commit ecc8a3c3a4 on Dec 15, 2020
  25. practicalswift deleted the branch on Apr 10, 2021
  26. DrahtBot locked this on Aug 16, 2022

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: 2026-04-16 15:14 UTC

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