Fuzz: extend CConnman tests #28584

pull vasild wants to merge 7 commits into bitcoin:master from vasild:fuzz_connman changing 15 files +303 −65
  1. vasild commented at 11:27 am on October 4, 2023: contributor

    Extend CConnman fuzz tests to also exercise the methods OpenNetworkConnection(), CreateNodeFromAcceptedSocket(), InitBinds() and SocketHandler().

    Previously fuzzing those methods would have resulted in real socket functions being called in the operating system which is undesirable during fuzzing. Now that #21878 is complete all those are mocked to a fuzzed socket and a fuzzed DNS resolver (see how CreateSock and g_dns_lookup are replaced in the first commit).

  2. DrahtBot commented at 11:27 am on October 4, 2023: 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/28584.

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK jonatack, dergoegge
    Stale ACK brunoerg

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

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #32065 (i2p: make a time gap between creating transient sessions and using them by vasild)
    • #32015 (net: replace manual reference counting of CNode with shared_ptr by vasild)
    • #29641 (scripted-diff: Use LogInfo over LogPrintf [WIP, NOMERGE, DRAFT] by maflcko)
    • #29415 (Broadcast own transactions only via short-lived Tor or I2P connections by vasild)

    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.

  3. DrahtBot added the label Tests on Oct 4, 2023
  4. dergoegge commented at 11:48 am on October 4, 2023: member
    Concept ACK
  5. dergoegge commented at 2:07 pm on October 4, 2023: member

    The cpu utilization of this target doesn’t look great, i suspect this is due to timeouts/sleeps e.g.: https://github.com/bitcoin/bitcoin/blob/db7b5dfcc502a8a81c51f56fe753990ae8b3a202/src/net.cpp#L2099

    Would be nice to address that (maybe in a follow up).

    Here is a preliminary coverage report: https://dergoegge.github.io/bitcoin-coverage/pr28584/fuzz.coverage/index.html (I’ll update this after fuzzing for longer). Looks good to me as the target alone now has more coverage (by line count) in net.cpp than our coverage on master with all targets.

  6. DrahtBot added the label CI failed on Oct 4, 2023
  7. brunoerg commented at 10:24 pm on October 4, 2023: contributor
    Concept ACK
  8. DrahtBot removed the label CI failed on Oct 5, 2023
  9. vasild commented at 10:33 am on October 5, 2023: contributor

    @dergoegge, thanks for looking into this!

    The cpu utilization of this target doesn’t look great

    How did you measure? I guess it should be possible to mock CConnman::interruptNet so that its sleep_for() method returns immediately.

  10. dergoegge commented at 10:48 am on October 5, 2023: member

    How did you measure?

    Eyeballing htop this target only achieves about 10% - 30% per core on my machine.

    I guess it should be possible to mock CConnman::interruptNet so that its sleep_for() method returns immediately.

    Sounds good to me.

  11. vasild commented at 11:50 am on October 5, 2023: contributor
    When I run FUZZ=connman ./src/test/fuzz/fuzz it is single CPU/single threaded and it stays at 98%-100% CPU all the time. It is the same in master. Does it get executed on >1 cores for you?
  12. dergoegge commented at 10:34 am on October 6, 2023: member

    When I run FUZZ=connman ./src/test/fuzz/fuzz it is single CPU/single threaded and it stays at 98%-100% CPU all the time.

    For how long did you run this? The fuzzer needs to first find inputs that trigger sleep_for for you to be able to observe the problem. I have tested this on two machines now and the result is always the same.

    Does it get executed on >1 cores for you?

    You can let libfuzzer run on multiple cores with either -jobs=<num cpus> or -fork=<num cpus>. I prefer fork since it also includes a minimization step.

  13. vasild commented at 9:40 am on October 11, 2023: contributor

    I guess it should be possible to mock CConnman::interruptNet so that its sleep_for() method returns immediately.

    Another use-case for that mock: #28635:

    If the test suite could mock the delay in ThreadOpenAddedConnections

  14. DrahtBot added the label Needs rebase on Jan 9, 2024
  15. vasild force-pushed on Jan 17, 2024
  16. DrahtBot removed the label Needs rebase on Jan 17, 2024
  17. vasild commented at 8:40 am on January 18, 2024: contributor
    20a9fc83bd...cd5bbb12e0: rebase due to conflicts
  18. in src/test/fuzz/connman.cpp:165 in b1b74a13ad outdated
    159@@ -160,6 +160,15 @@ FUZZ_TARGET(connman, .init = initialize_connman)
    160                     /*strDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
    161                     /*conn_type=*/conn_type,
    162                     /*use_v2transport=*/fuzzed_data_provider.ConsumeBool());
    163+            },
    164+            [&] {
    165+                connman.SetNetworkActive(true);
    


    brunoerg commented at 1:01 pm on February 2, 2024:
    In b1b74a13adb8e943d90fbe56c4a706b0ae91335a: nit: I don’t think to set network active here is a must, we could fuzz it with both network active and inactive.

    vasild commented at 1:38 pm on February 4, 2024:
    Right, done!
  19. in src/test/fuzz/util/net.h:105 in d923b86264 outdated
    100+{
    101+    std::vector<CService> ret;
    102+    const size_t size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size);
    103+    ret.reserve(size);
    104+    for (size_t i = 0; i < size; ++i) {
    105+        ret.emplace_back(ConsumeNetAddr(fuzzed_data_provider),
    


    brunoerg commented at 1:19 pm on February 2, 2024:

    In d923b86264df93ad86c7bd557b9970e156585dc7: You could use ConsumeService into ConsumeServiceVector.

     0diff --git a/src/test/fuzz/util/net.h b/src/test/fuzz/util/net.h
     1index a97017555d..78e61b51d9 100644
     2--- a/src/test/fuzz/util/net.h
     3+++ b/src/test/fuzz/util/net.h
     4@@ -102,8 +102,7 @@ inline std::vector<CService> ConsumeServiceVector(FuzzedDataProvider& fuzzed_dat
     5     const size_t size = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, max_vector_size);
     6     ret.reserve(size);
     7     for (size_t i = 0; i < size; ++i) {
     8-        ret.emplace_back(ConsumeNetAddr(fuzzed_data_provider),
     9-                         fuzzed_data_provider.ConsumeIntegral<uint16_t>());
    10+        ret.emplace_back(ConsumeService(fuzzed_data_provider));
    11     }
    12     return ret;
    13 }
    

    vasild commented at 1:38 pm on February 4, 2024:
    Done, thanks!
  20. vasild force-pushed on Feb 4, 2024
  21. vasild commented at 1:38 pm on February 4, 2024: contributor
    cd5bbb12e0...5e3c80da14: address suggestions
  22. brunoerg approved
  23. brunoerg commented at 5:03 pm on February 5, 2024: contributor
    utACK 5e3c80da1473f90406b84c1ba14dc563ce4d2853
  24. DrahtBot requested review from dergoegge on Feb 5, 2024
  25. DrahtBot added the label CI failed on Mar 13, 2024
  26. DrahtBot commented at 11:59 pm on March 13, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin/bitcoin/runs/21199329709

  27. vasild force-pushed on Mar 27, 2024
  28. vasild commented at 6:01 pm on March 27, 2024: contributor
    5e3c80da14...6d9083b249: rebase due to silent merge conflict
  29. DrahtBot removed the label CI failed on Mar 28, 2024
  30. brunoerg approved
  31. brunoerg commented at 11:53 am on April 30, 2024: contributor
    reACK 6d9083b249376d503621da7980ef7ae02e690e0b
  32. DrahtBot added the label CI failed on Jun 22, 2024
  33. DrahtBot commented at 11:23 am on June 22, 2024: contributor

    🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the documentation.

    Possibly this is due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    Leave a comment here, if you need help tracking down a confusing failure.

    Debug: https://github.com/bitcoin/bitcoin/runs/23166298024

  34. vasild force-pushed on Jun 25, 2024
  35. vasild commented at 3:39 pm on June 25, 2024: contributor
    6d9083b249...45f4dbe484: rebase due to conflicts
  36. DrahtBot removed the label CI failed on Jun 25, 2024
  37. vasild force-pushed on Jun 26, 2024
  38. vasild commented at 6:46 am on June 26, 2024: contributor
    45f4dbe484...655a2cf666: the previous push resolved the merge conflict in a too late commit, causing the “test each commit” CI job to fail
  39. vasild force-pushed on Sep 2, 2024
  40. vasild commented at 2:48 pm on September 2, 2024: contributor
    655a2cf666...99b1f88fe8: rebase to pick CMake
  41. DrahtBot added the label CI failed on Sep 12, 2024
  42. DrahtBot removed the label CI failed on Sep 15, 2024
  43. achow101 requested review from brunoerg on Oct 15, 2024
  44. DrahtBot added the label CI failed on Oct 25, 2024
  45. DrahtBot removed the label CI failed on Oct 25, 2024
  46. DrahtBot added the label Needs rebase on Oct 25, 2024
  47. vasild force-pushed on Nov 6, 2024
  48. vasild commented at 10:53 am on November 6, 2024: contributor

    99b1f88fe8...cf83f0c14c: rebase due to conflicts and mock the sleeps

    I guess it should be possible to mock CConnman::interruptNet so that its sleep_for() method returns immediately.

    Sounds good to me.

    Done. CThreadInterrupt can now be mocked in other tests as well. Thanks for the suggestion, @dergoegge!

  49. in src/test/fuzz/util/threadinterrupt.cpp:21 in cf83f0c14c outdated
    14+    return m_fuzzed_data_provider.ConsumeBool();
    15+}
    16+
    17+bool FuzzedThreadInterrupt::sleep_for(Clock::duration)
    18+{
    19+    return m_fuzzed_data_provider.ConsumeBool();
    


    dergoegge commented at 10:59 am on November 6, 2024:
    Perhaps in a follow up we can make mocktime accurate to the millisecond and then advance it here to simulate an actual sleep.

    vasild commented at 12:59 pm on November 6, 2024:

    I like simulating some time passage here.

    Changing static std::atomic<std::chrono::seconds> g_mock_time{}; to a finer precision is indeed out of the scope here.

    Following a discussion on IRC I checked that indeed the time is frozen already for fuzz tests so calling SetMockTime() here is not going to freeze it (since it is already frozen). I added:

    0SetMockTime(ConsumeTime(m_fuzzed_data_provider)); // Time could go backwards.
    

    It could end up increasing the time more than the argument to sleep_for() but this might happen during normal operation, so is a good exercise.

    It could end up with the time going backwards. If this is undesirable then this may be changed to something like:

    0SetMockTime(NodeClock::now() + ConsumeIntegralInRange(0, the_argument_to_sleep_for * 2));
    
  50. DrahtBot removed the label Needs rebase on Nov 6, 2024
  51. DrahtBot added the label CI failed on Nov 6, 2024
  52. DrahtBot commented at 12:43 pm on November 6, 2024: contributor

    🚧 At least one of the CI tasks failed. Debug: https://github.com/bitcoin/bitcoin/runs/32589451018

    Try to run the tests locally, according to the documentation. However, a CI failure may still happen due to a number of reasons, for example:

    • Possibly due to a silent merge conflict (the changes in this pull request being incompatible with the current code in the target branch). If so, make sure to rebase on the latest commit of the target branch.

    • A sanitizer issue, which can only be found by compiling with the sanitizer and running the affected test.

    • An intermittent issue.

    Leave a comment here, if you need help tracking down a confusing failure.

  53. vasild force-pushed on Nov 6, 2024
  54. vasild commented at 1:00 pm on November 6, 2024: contributor
    cf83f0c...c97d496: simulate time passage from FuzzedThreadInterrupt::sleep_for() and (hopefully) fix CI
  55. DrahtBot removed the label CI failed on Nov 6, 2024
  56. jonatack commented at 10:20 pm on November 19, 2024: member
    utACK c97d49628a78aac9a65f2bd1ddc733b0b425090b
  57. DrahtBot requested review from dergoegge on Nov 19, 2024
  58. vasild force-pushed on Nov 28, 2024
  59. vasild commented at 2:29 pm on November 28, 2024: contributor
    c97d49628a...687a9af2a8: include #31316 as first commit here. It fixes a problem in master with FuzzedSock::Accept() which might be triggered by the tests added in this PR.
  60. dergoegge commented at 10:15 am on December 9, 2024: member
    0$ echo "XGQtSi1YAIIkp/8D/yQtJCRYq/9YSv///95cXFz//gABQQAAAABcXCVcXP9cZVxcXFxcXGP//2FkZHL/bWVya2xlYjpISEhISFgjSlgAAQAADABJMCEgIf//5wD+AAsA/wAA/////wAAAQDdAAAAAAAAAAAQAAAJAAAARQD4LgAAABD//gAA/PoAAAAA9v8EAP2n/wP/XGVcXFxcXFxj//8A5wD+AAsA/wD8ZPwjAA==" | base64 --decode > connman.crash
    1$ FUZZ=connman fuzz connman.crash
    2==9344==ERROR: MemorySanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f5b532b2c62 bp 0x7ffe2ed01cb0 sp 0x7ffe2ed01468 T9344)
    
  61. vasild force-pushed on Dec 10, 2024
  62. vasild commented at 10:17 am on December 10, 2024: contributor

    Oops, ConsumeBytes() could return less bytes than requested :facepalm: :face_with_head_bandage:

    Fixed:

    687a9af2a8...33ffe74302: #31316 (comment)

    Thanks!

  63. dergoegge commented at 11:21 am on December 10, 2024: member

    Please rebase on #31235, fuzzing otherwise leads to:

    0$ echo "VlZWVlZWVlZWVlZWVhZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWACWcAFwEAAAAZVz9f61hY2v/XHP8BIAI/1z9IiIiIyIiIiIiIiIAIgAACwAAPQ8AAAAAAAAnD29ja3R4AAUAAAAAAAQAAAAAAAAAAAEAAAAAAAAAACAAAAAAOAAAAAAAAAAAAAAAAAAAKm90aAD//wAA//8BAwAAAAAAAAAAAAAAAAAAAND///8AIQAAAAAAACEAAAQAIQAAAAAAXAAAACEAAAARAP8/bH7//wAAAHkAAAAAAAD/ACf/AIN5eVwMAAAAOgHe+6Oj24ejdHgIfmNjY2NjhYWFhY9yYwAAAN6JB/t5eXl5eXl5eXkDAwMAAAAAAAAAAAD+Mf/77zoBAAEAACf/AIN5eVwMAAAAOgEAAUB139773977o6PbeXl5eXl5ef4x///vOgEAAQAAkBEBAQAACH5+fiFcZXYlYWNrAP8hdmX/BXRgY2sHN2hlYWRlcnMAAAAAAAAAUAABAQAkmwAhAEAEAAAAAAAAAND/////////////AAA=" | base64 --decode > connman2.crash
    1$ FUZZ=connman fuzz connman2.crash
    2==27516==ERROR: MemorySanitizer: requested allocation size 0x23d7999a4970a3a8 exceeds maximum supported size of 0x10000000000
    

    Additionally:

     0$ echo "XGRYSlgAgiQkLSQkXSQkJCRYq/9YSv///95cXFwBAAABAAAAAVxcXFz/fGVcXFxcXGNoMWVjRUVFRUX+//9F/0VFdGtw//8HCgAAAAAA5gAAADAhICH//+cA/gALAP8AAP////8AAAEA3QAAAAAAAAAAAAAAAAAA////+gAAAAAQ//4AAPz6AAAAAPb/BAD9p/8D/wcAAAAAADchICH//+cA/gALAP8A/Pz8AxA=" | base64 --decode > connman3.crash
     1$ FUZZ=connman fuzz connman3.crash
     2==247==WARNING: MemorySanitizer: use-of-uninitialized-value
     3    [#0](/bitcoin-bitcoin/0/) 0x55adc01e655c in CService::SetSockAddr(sockaddr const*) /workdir/bitcoin/build_fuzz/src/./netaddress.cpp:812:5
     4    [#1](/bitcoin-bitcoin/1/) 0x55adc12ba012 in GetBindAddress(Sock const&) /workdir/bitcoin/build_fuzz/src/./net.cpp:382:19
     5    [#2](/bitcoin-bitcoin/2/) 0x55adc12ddb12 in CConnman::AcceptConnection(CConnman::ListenSocket const&) /workdir/bitcoin/build_fuzz/src/./net.cpp:1722:51
     6    [#3](/bitcoin-bitcoin/3/) 0x55adc12ed808 in CConnman::SocketHandlerListening(std::__1::unordered_map<std::__1::shared_ptr<Sock const>, Sock::Events, Sock::HashSharedPtrSock, Sock::EqualSharedPtrSock, std::__1::allocator<std::__1::pair<std::__1::shared_ptr<Sock const> const, Sock::Events>>> const&) /workdir/bitcoin/build_fuzz/src/./net.cpp:2160:13
     7    [#4](/bitcoin-bitcoin/4/) 0x55adc12e9d48 in CConnman::SocketHandler() /workdir/bitcoin/build_fuzz/src/./net.cpp:2053:5
     8    [#5](/bitcoin-bitcoin/5/) 0x55adbf82d129 in ConnmanTestMsg::SocketHandlerPublic() /workdir/bitcoin/build_fuzz/src/test/fuzz/./test/util/net.h:85:9
     9    [#6](/bitcoin-bitcoin/6/) 0x55adbf82d129 in connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_26::operator()() const /workdir/bitcoin/build_fuzz/src/test/fuzz/./test/fuzz/connman.cpp:211:25
    10    [#7](/bitcoin-bitcoin/7/) 0x55adbf82d129 in unsigned long CallOneOf<connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_4, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_5, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_6, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_7, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_8, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_9, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_10, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_11, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_12, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_13, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_0, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_1, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_14, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_15, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_16, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_17, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_18, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_19, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_20, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_21, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_22, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_23, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_24, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_25, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_26>(FuzzedDataProvider&, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_4, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_5, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_6, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_7, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_8, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_9, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_10, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_11, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_12, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_13, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_0, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_1, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_14, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_15, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_16, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_17, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_18, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_19, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_20, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_21, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_22, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_23, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_24, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_25, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_26) /workdir/bitcoin/build_fuzz/src/test/fuzz/./test/fuzz/util.h:42:27
    11    [#8](/bitcoin-bitcoin/8/) 0x55adbf82d129 in connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>) /workdir/bitcoin/build_fuzz/src/test/fuzz/./test/fuzz/connman.cpp:94:9
    12    [#9](/bitcoin-bitcoin/9/) 0x55adbf5200a5 in decltype(std::declval<void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>)>()(std::declval<std::__1::span<unsigned char const, 18446744073709551615ul>>())) std::__1::__invoke[abi:de190104]<void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::span<unsigned char const, 18446744073709551615ul>>(void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::span<unsigned char const, 18446744073709551615ul>&&) /libcxx_msan/include/c++/v1/__type_traits/invoke.h:149:25
    13    [#10](/bitcoin-bitcoin/10/) 0x55adbf5200a5 in void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:de190104]<void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::span<unsigned char const, 18446744073709551615ul>>(void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::span<unsigned char const, 18446744073709551615ul>&&) /libcxx_msan/include/c++/v1/__type_traits/invoke.h:224:5
    14    [#11](/bitcoin-bitcoin/11/) 0x55adbf5200a5 in std::__1::__function::__alloc_func<void (*)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::allocator<void (*)(std::__1::span<unsigned char const, 18446744073709551615ul>)>, void (std::__1::span<unsigned char const, 18446744073709551615ul>)>::operator()[abi:de190104](std::__1::span<unsigned char const, 18446744073709551615ul>&&) /libcxx_msan/include/c++/v1/__functional/function.h:171:12
    15    [#12](/bitcoin-bitcoin/12/) 0x55adbf5200a5 in std::__1::__function::__func<void (*)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::allocator<void (*)(std::__1::span<unsigned char const, 18446744073709551615ul>)>, void (std::__1::span<unsigned char const, 18446744073709551615ul>)>::operator()(std::__1::span<unsigned char const, 18446744073709551615ul>&&) /libcxx_msan/include/c++/v1/__functional/function.h:313:10
    16    [#13](/bitcoin-bitcoin/13/) 0x55adc0034ce2 in std::__1::__function::__value_func<void (std::__1::span<unsigned char const, 18446744073709551615ul>)>::operator()[abi:de190104](std::__1::span<unsigned char const, 18446744073709551615ul>&&) const /libcxx_msan/include/c++/v1/__functional/function.h:430:12
    17    [#14](/bitcoin-bitcoin/14/) 0x55adc0034ce2 in std::__1::function<void (std::__1::span<unsigned char const, 18446744073709551615ul>)>::operator()(std::__1::span<unsigned char const, 18446744073709551615ul>) const /libcxx_msan/include/c++/v1/__functional/function.h:989:10
    18    [#15](/bitcoin-bitcoin/15/) 0x55adc0034ce2 in LLVMFuzzerTestOneInput /workdir/bitcoin/build_fuzz/src/test/fuzz/util/./test/fuzz/fuzz.cpp:213:5
    19    [#16](/bitcoin-bitcoin/16/) 0x55adbf409dc6 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:614:13
    20    [#17](/bitcoin-bitcoin/17/) 0x55adbf3f3662 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:328:6
    21    [#18](/bitcoin-bitcoin/18/) 0x55adbf3f957f in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:863:9
    22    [#19](/bitcoin-bitcoin/19/) 0x55adbf4259a2 in main /llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    23    [#20](/bitcoin-bitcoin/20/) 0x7f6a6dd60d67  (/lib/x86_64-linux-gnu/libc.so.6+0x29d67) (BuildId: 3bc74dbb72522bb47e0d899e5615140b044a5b40)
    24    [#21](/bitcoin-bitcoin/21/) 0x7f6a6dd60e24 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e24) (BuildId: 3bc74dbb72522bb47e0d899e5615140b044a5b40)
    25    [#22](/bitcoin-bitcoin/22/) 0x55adbf3eb6b0 in _start (/workdir/out/libfuzzer_msan/fuzz+0xf226b0)
    26
    27  Uninitialized value was stored to memory at
    28    [#0](/bitcoin-bitcoin/0/) 0x55adc01e6555 in CService::SetSockAddr(sockaddr const*) /workdir/bitcoin/build_fuzz/src/./netaddress.cpp:812:20
    29    [#1](/bitcoin-bitcoin/1/) 0x55adc12ba012 in GetBindAddress(Sock const&) /workdir/bitcoin/build_fuzz/src/./net.cpp:382:19
    30    [#2](/bitcoin-bitcoin/2/) 0x55adc12ddb12 in CConnman::AcceptConnection(CConnman::ListenSocket const&) /workdir/bitcoin/build_fuzz/src/./net.cpp:1722:51
    31    [#3](/bitcoin-bitcoin/3/) 0x55adc12ed808 in CConnman::SocketHandlerListening(std::__1::unordered_map<std::__1::shared_ptr<Sock const>, Sock::Events, Sock::HashSharedPtrSock, Sock::EqualSharedPtrSock, std::__1::allocator<std::__1::pair<std::__1::shared_ptr<Sock const> const, Sock::Events>>> const&) /workdir/bitcoin/build_fuzz/src/./net.cpp:2160:13
    32    [#4](/bitcoin-bitcoin/4/) 0x55adc12e9d48 in CConnman::SocketHandler() /workdir/bitcoin/build_fuzz/src/./net.cpp:2053:5
    33    [#5](/bitcoin-bitcoin/5/) 0x55adbf82d129 in ConnmanTestMsg::SocketHandlerPublic() /workdir/bitcoin/build_fuzz/src/test/fuzz/./test/util/net.h:85:9
    34    [#6](/bitcoin-bitcoin/6/) 0x55adbf82d129 in connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_26::operator()() const /workdir/bitcoin/build_fuzz/src/test/fuzz/./test/fuzz/connman.cpp:211:25
    35    [#7](/bitcoin-bitcoin/7/) 0x55adbf82d129 in unsigned long CallOneOf<connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_4, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_5, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_6, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_7, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_8, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_9, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_10, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_11, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_12, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_13, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_0, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_1, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_14, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_15, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_16, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_17, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_18, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_19, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_20, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_21, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_22, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_23, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_24, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_25, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_26>(FuzzedDataProvider&, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_4, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_5, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_6, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_7, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_8, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_9, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_10, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_11, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_12, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_13, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_0, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_1, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_14, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_15, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_16, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_17, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_18, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_19, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_20, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_21, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_22, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_23, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_24, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_25, connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>)::$_26) /workdir/bitcoin/build_fuzz/src/test/fuzz/./test/fuzz/util.h:42:27
    36    [#8](/bitcoin-bitcoin/8/) 0x55adbf82d129 in connman_fuzz_target(std::__1::span<unsigned char const, 18446744073709551615ul>) /workdir/bitcoin/build_fuzz/src/test/fuzz/./test/fuzz/connman.cpp:94:9
    37    [#9](/bitcoin-bitcoin/9/) 0x55adbf5200a5 in decltype(std::declval<void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>)>()(std::declval<std::__1::span<unsigned char const, 18446744073709551615ul>>())) std::__1::__invoke[abi:de190104]<void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::span<unsigned char const, 18446744073709551615ul>>(void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::span<unsigned char const, 18446744073709551615ul>&&) /libcxx_msan/include/c++/v1/__type_traits/invoke.h:149:25
    38    [#10](/bitcoin-bitcoin/10/) 0x55adbf5200a5 in void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:de190104]<void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::span<unsigned char const, 18446744073709551615ul>>(void (*&)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::span<unsigned char const, 18446744073709551615ul>&&) /libcxx_msan/include/c++/v1/__type_traits/invoke.h:224:5
    39    [#11](/bitcoin-bitcoin/11/) 0x55adbf5200a5 in std::__1::__function::__alloc_func<void (*)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::allocator<void (*)(std::__1::span<unsigned char const, 18446744073709551615ul>)>, void (std::__1::span<unsigned char const, 18446744073709551615ul>)>::operator()[abi:de190104](std::__1::span<unsigned char const, 18446744073709551615ul>&&) /libcxx_msan/include/c++/v1/__functional/function.h:171:12
    40    [#12](/bitcoin-bitcoin/12/) 0x55adbf5200a5 in std::__1::__function::__func<void (*)(std::__1::span<unsigned char const, 18446744073709551615ul>), std::__1::allocator<void (*)(std::__1::span<unsigned char const, 18446744073709551615ul>)>, void (std::__1::span<unsigned char const, 18446744073709551615ul>)>::operator()(std::__1::span<unsigned char const, 18446744073709551615ul>&&) /libcxx_msan/include/c++/v1/__functional/function.h:313:10
    41    [#13](/bitcoin-bitcoin/13/) 0x55adc0034ce2 in std::__1::__function::__value_func<void (std::__1::span<unsigned char const, 18446744073709551615ul>)>::operator()[abi:de190104](std::__1::span<unsigned char const, 18446744073709551615ul>&&) const /libcxx_msan/include/c++/v1/__functional/function.h:430:12
    42    [#14](/bitcoin-bitcoin/14/) 0x55adc0034ce2 in std::__1::function<void (std::__1::span<unsigned char const, 18446744073709551615ul>)>::operator()(std::__1::span<unsigned char const, 18446744073709551615ul>) const /libcxx_msan/include/c++/v1/__functional/function.h:989:10
    43    [#15](/bitcoin-bitcoin/15/) 0x55adc0034ce2 in LLVMFuzzerTestOneInput /workdir/bitcoin/build_fuzz/src/test/fuzz/util/./test/fuzz/fuzz.cpp:213:5
    44    [#16](/bitcoin-bitcoin/16/) 0x55adbf409dc6 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:614:13
    45    [#17](/bitcoin-bitcoin/17/) 0x55adbf3f3662 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:328:6
    46    [#18](/bitcoin-bitcoin/18/) 0x55adbf3f957f in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:863:9
    47    [#19](/bitcoin-bitcoin/19/) 0x55adbf4259a2 in main /llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
    48    [#20](/bitcoin-bitcoin/20/) 0x7f6a6dd60d67  (/lib/x86_64-linux-gnu/libc.so.6+0x29d67) (BuildId: 3bc74dbb72522bb47e0d899e5615140b044a5b40)
    49
    50  Uninitialized value was created by an allocation of 'sockaddr_bind' in the stack frame
    51    [#0](/bitcoin-bitcoin/0/) 0x55adc12b9e9e in GetBindAddress(Sock const&) /workdir/bitcoin/build_fuzz/src/./net.cpp:379:5
    52
    53SUMMARY: MemorySanitizer: use-of-uninitialized-value /workdir/bitcoin/build_fuzz/src/./netaddress.cpp:812:5 in CService::SetSockAddr(sockaddr const*)
    

    Probably caused by FuzzedSock::GetSockName using ConsumeData on an empty data provider (i.e. sockaddr* name won’t be initialized):

    https://github.com/bitcoin/bitcoin/blob/37e49c2c7ca5969124830d79b2cb31041c570755/src/test/fuzz/util/net.cpp#L342-L354

  64. vasild force-pushed on Dec 10, 2024
  65. vasild commented at 12:03 pm on December 10, 2024: contributor

    33ffe74302...72ff6d2b50:

    Please rebase on #31235

    Done. Rebasing fixed connman2.crash for me.

    I can’t reproduce connman3.crash (!?) but anyway I can see the problem with FuzzedSock::GetSockName() initializing too little of the output, so I changed it to fully set the entire output, always.

  66. dergoegge commented at 12:09 pm on December 10, 2024: member

    I can’t reproduce connman3.crash

    You’ll need memory sanitizer instrumentation for that one, which unfortunately requires all dependencies (including libc++) to be instrumented as well, so it’s a pain to setup. I’ll re-test the fix for you!

  67. maflcko commented at 12:28 pm on December 10, 2024: member
    Shouldn’t valgrind be able to detect UB memory issues, unless they were optimized out?
  68. DrahtBot added the label Needs rebase on Feb 21, 2025
  69. vasild force-pushed on Mar 20, 2025
  70. vasild commented at 5:57 pm on March 20, 2025: contributor
    72ff6d2b50...696b6671da: rebase due to conflicts
  71. DrahtBot removed the label Needs rebase on Mar 20, 2025
  72. DrahtBot added the label Needs rebase on Apr 10, 2025
  73. vasild force-pushed on Apr 11, 2025
  74. vasild commented at 11:44 am on April 11, 2025: contributor
    696b6671da...7e42c92d2b: rebase due to conflicts
  75. DrahtBot removed the label Needs rebase on Apr 11, 2025
  76. DrahtBot added the label Needs rebase on May 20, 2025
  77. vasild force-pushed on May 30, 2025
  78. vasild commented at 12:44 pm on May 30, 2025: contributor
    7e42c92d2b...582d9e3dbd: rebase due to conflicts
  79. vasild commented at 12:48 pm on May 30, 2025: contributor
    I believe all issues here have been addressed and this is ready for review. Has a stale ACK from @brunoerg and @jonatack and Concept ACK from @dergoegge.
  80. DrahtBot removed the label Needs rebase on May 30, 2025
  81. in src/test/fuzz/util/net.h:252 in d790b83996 outdated
    224@@ -225,6 +225,18 @@ inline CService ConsumeService(FuzzedDataProvider& fuzzed_data_provider) noexcep
    225     return {ConsumeNetAddr(fuzzed_data_provider), fuzzed_data_provider.ConsumeIntegral<uint16_t>()};
    226 }
    227 
    228+inline std::vector<CService> ConsumeServiceVector(FuzzedDataProvider& fuzzed_data_provider,
    


    brunoerg commented at 2:31 pm on June 2, 2025:
    d790b83996ae0d0bc4fe9723606c8c923cec1bcc: nit: It seems the ConsumeServiceVector is always called with the max_vector_size being 5? Maybe it could be the default value since it’s only used here.

    vasild commented at 12:19 pm on June 9, 2025:
    Done, thanks!
  82. brunoerg approved
  83. brunoerg commented at 2:31 pm on June 2, 2025: contributor

    reACK 582d9e3dbdf5b64272b65844d679942c5aca643f

    Just generated this coverage report with some hours of running: https://brunoerg.xyz/bitcoin-core-coverage/28584/coverage_report/

  84. DrahtBot requested review from jonatack on Jun 2, 2025
  85. jonatack commented at 4:57 pm on June 2, 2025: member

    re-ACK 582d9e3dbdf5b64272b65844d679942c5aca643f per git range-diff 1c66023 c97d496 582d9e3

    changes since my last review #28584#pullrequestreview-2446770519 have been rebases and adding commit cf0a49723bbbbb22d47ccb8f0e030a51e6757609

  86. in src/test/fuzz/connman.cpp:79 in 582d9e3dbd outdated
    75+                     fuzzed_data_provider.ConsumeBool(),
    76+                     ConsumeThreadInterrupt(fuzzed_data_provider)};
    77 
    78     const uint64_t max_outbound_limit{fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
    79     CConnman::Options options;
    80+    options.m_msgproc = g_setup->m_node.peerman.get();
    


    dergoegge commented at 3:35 pm on June 4, 2025:

    It’d be better to create a fake NetEventsInterface for this test, as we are testing connman not peerman. Peerman is also a global here which might have unwanted side effects.

    I haven’t tried running the most recent version of this test but it looks like it’ll run out of memory eventually as the Peers allocated in peerman are never deleted?


    vasild commented at 12:22 pm on June 9, 2025:
    Added a fake/fuzzed NetEventsInterface that is created and destroyed per every run, like the test connman itself. That addresses the “out of memory eventually” concern as well. Thanks!
  87. DrahtBot requested review from dergoegge on Jun 4, 2025
  88. fuzz: set the output argument of FuzzedSock::Accept()
    `FuzzedSock::Accept()` properly returns a new socket, but it forgot to
    set the output argument `addr`, like `accept(2)` is expected to.
    
    This could lead to reading uninitialized data during testing when we
    read it, e.g. from `CService::SetSockAddr()` which reads the `sa_family`
    member.
    
    Set `addr` to a fuzzed IPv4 or IPv6 address.
    e883b37768
  89. fuzz: add Fuzzed NetEventsInterface and use it in connman tests e6a917c8f8
  90. fuzz: add CConnman::OpenNetworkConnection() to the tests
    Now that all network calls done by `CConnman::OpenNetworkConnection()`
    are done via `Sock` they can be redirected (mocked) to `FuzzedSocket`
    for testing.
    50da7432ec
  91. fuzz: add CConnman::CreateNodeFromAcceptedSocket() to the tests 91cbf4dbd8
  92. fuzz: add CConnman::InitBinds() to the tests 3265df63a4
  93. fuzz: add CConnman::SocketHandler() to the tests 6d9e5d130d
  94. fuzz: make it possible to mock (fuzz) CThreadInterrupt
    * Make the methods of `CThreadInterrupt` virtual and store a pointer to
      it in `CConnman`, thus making it possible to override with a mocked
      instance.
    * Initialize `CConnman::m_interrupt_net` from the constructor, making it
      possible for callers to supply mocked version.
    * Introduce `FuzzedThreadInterrupt` and `ConsumeThreadInterrupt()` and
      use them in `src/test/fuzz/connman.cpp` and `src/test/fuzz/i2p.cpp`.
    
    This improves the CPU utilization of the `connman` fuzz test.
    
    As a nice side effect, the `std::shared_ptr` used for
    `CConnman::m_interrupt_net` resolves the possible lifetime issues with
    it (see the removed comment for that variable).
    0802398e74
  95. vasild force-pushed on Jun 9, 2025
  96. vasild commented at 12:19 pm on June 9, 2025: contributor
    582d9e3dbd...0802398e74: rebase and address suggestions
  97. jonatack commented at 0:23 am on June 19, 2025: member
    Review re-ACK 0802398e749c5e16fa7085cd87c91a31bbe043bd
  98. DrahtBot requested review from brunoerg on Jun 19, 2025
  99. in src/util/threadinterrupt.h:45 in 0802398e74
    44+
    45+    /// Interrupt any sleeps. After this `interrupted()` will return `true`.
    46+    virtual void operator()() EXCLUSIVE_LOCKS_REQUIRED(!mut);
    47+
    48+    /// Reset to an non-interrupted state.
    49+    virtual void reset();
    


    dergoegge commented at 10:24 am on July 11, 2025:
    nit: it might make sense to name this differently, just to avoid someone confusing this function with a smart ptr’s reset().

    vasild commented at 11:49 am on September 2, 2025:

    Good point! I do not know why I chose lowercase since it also violates the coding style. Maybe it was like that before and I left it as it is even though I touched those lines and the callers.

    Append the following as a new commit or amend it into the last one fuzz: make it possible to mock (fuzz) CThreadInterrupt?

      0commit 486db14b9ce13a90b69f69dafb59d3d8932498f7 (HEAD -> fuzz_connman)
      1Parent: 0802398e749c5e16fa7085cd87c91a31bbe043bd
      2Author:     Vasil Dimov <vd@FreeBSD.org>
      3AuthorDate: Tue Sep 2 13:40:35 2025 +0200
      4Commit:     Vasil Dimov <vd@FreeBSD.org>
      5CommitDate: Tue Sep 2 13:40:35 2025 +0200
      6gpg: Signature made Tue Sep  2 13:42:45 2025 CEST
      7gpg:                using RSA key E64D8D45614DB07545D9CCC154DF06F64B55CBBF
      8gpg: Good signature from "Vasil Dimov <vd@myforest.net>" [ultimate]
      9gpg:                 aka "Vasil Dimov <vd@FreeBSD.org>" [ultimate]
     10gpg:                 aka "Vasil Dimov <vasild@gmail.com>" [ultimate]
     11
     12
     13    util: Uppercase methods of CThreadInterrupt
     14    
     15    Rename
     16    
     17    `CThreadInterrupt::interrupted()` -> `CThreadInterrupt::Interrupted()`
     18    `CThreadInterrupt::reset()` -> `CThreadInterrupt::Reset()`
     19    `CThreadInterrupt::sleep_for()` -> `CThreadInterrupt::SleepFor()`
     20    
     21    for consistency with the coding style and to avoid confusing the
     22    `reset()` method of `CThreadInterrupt` with the `reset()` methods of
     23    smart pointers.
     24
     25diff --git a/src/i2p.cpp b/src/i2p.cpp
     26index 80f3bde4cf..2ebd0b8c45 100644
     27--- a/src/i2p.cpp
     28+++ b/src/i2p.cpp
     29@@ -159,13 +159,13 @@ bool Session::Accept(Connection& conn)
     30 {
     31     AssertLockNotHeld(m_mutex);
     32 
     33     std::string errmsg;
     34     bool disconnect{false};
     35 
     36-    while (!m_interrupt->interrupted()) {
     37+    while (!m_interrupt->Interrupted()) {
     38         Sock::Event occurred;
     39         if (!conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred)) {
     40             errmsg = "wait on socket failed";
     41             break;
     42         }
     43 
     44@@ -202,13 +202,13 @@ bool Session::Accept(Connection& conn)
     45 
     46         conn.peer = CService(peer_addr, I2P_SAM31_PORT);
     47 
     48         return true;
     49     }
     50 
     51-    if (m_interrupt->interrupted()) {
     52+    if (m_interrupt->Interrupted()) {
     53         LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Accept was interrupted\n");
     54     } else {
     55         LogPrintLevel(BCLog::I2P, BCLog::Level::Debug, "Error accepting%s: %s\n", disconnect ? " (will close the session)" : "", errmsg);
     56     }
     57     if (disconnect) {
     58         LOCK(m_mutex);
     59diff --git a/src/index/base.cpp b/src/index/base.cpp
     60index 5767116ce5..cd36967ac6 100644
     61--- a/src/index/base.cpp
     62+++ b/src/index/base.cpp
     63@@ -83,13 +83,13 @@ BaseIndex::~BaseIndex()
     64 
     65 bool BaseIndex::Init()
     66 {
     67     AssertLockNotHeld(cs_main);
     68 
     69     // May need reset if index is being restarted.
     70-    m_interrupt.reset();
     71+    m_interrupt.Reset();
     72 
     73     // m_chainstate member gives indexing code access to node internals. It is
     74     // removed in followup [#24230](/bitcoin-bitcoin/24230/)
     75     m_chainstate = WITH_LOCK(::cs_main,
     76         return &m_chain->context()->chainman->GetChainstateForIndexing());
     77     // Register to validation interface before setting the 'm_synced' flag, so that
     78diff --git a/src/mapport.cpp b/src/mapport.cpp
     79index 83105f51fd..4d2f1f1957 100644
     80--- a/src/mapport.cpp
     81+++ b/src/mapport.cpp
     82@@ -110,23 +110,23 @@ static void ProcessPCP()
     83         }
     84         // RFC6887 11.2.1 recommends that clients send their first renewal packet at a time chosen with uniform random
     85         // distribution in the range 1/2 to 5/8 of expiration time.
     86         std::chrono::seconds sleep_time_min(actual_lifetime / 2);
     87         std::chrono::seconds sleep_time_max(actual_lifetime * 5 / 8);
     88         sleep_time = sleep_time_min + FastRandomContext().randrange<std::chrono::milliseconds>(sleep_time_max - sleep_time_min);
     89-    } while (ret && g_mapport_interrupt.sleep_for(sleep_time));
     90+    } while (ret && g_mapport_interrupt.SleepFor(sleep_time));
     91 
     92     // We don't delete the mappings when the thread is interrupted because this would add additional complexity, so
     93     // we rather just choose a fairly short expiry time.
     94 }
     95 
     96 static void ThreadMapPort()
     97 {
     98     do {
     99         ProcessPCP();
    100-    } while (g_mapport_interrupt.sleep_for(PORT_MAPPING_RETRY_PERIOD));
    101+    } while (g_mapport_interrupt.SleepFor(PORT_MAPPING_RETRY_PERIOD));
    102 }
    103 
    104 void StartThreadMapPort()
    105 {
    106     if (!g_mapport_thread.joinable()) {
    107         assert(!g_mapport_interrupt);
    108@@ -152,9 +152,9 @@ void InterruptMapPort()
    109 }
    110 
    111 void StopMapPort()
    112 {
    113     if (g_mapport_thread.joinable()) {
    114         g_mapport_thread.join();
    115-        g_mapport_interrupt.reset();
    116+        g_mapport_interrupt.Reset();
    117     }
    118 }
    119diff --git a/src/net.cpp b/src/net.cpp
    120index 85c25543d3..ee34b09ebd 100644
    121--- a/src/net.cpp
    122+++ b/src/net.cpp
    123@@ -2091,13 +2091,13 @@ void CConnman::SocketHandler()
    124         // Check for the readiness of the already connected sockets and the
    125         // listening sockets in one call ("readiness" as in poll(2) or
    126         // select(2)). If none are ready, wait for a short while and return
    127         // empty sets.
    128         events_per_sock = GenerateWaitSockets(snap.Nodes());
    129         if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
    130-            m_interrupt_net->sleep_for(timeout);
    131+            m_interrupt_net->SleepFor(timeout);
    132         }
    133 
    134         // Service (send/receive) each of the already connected nodes.
    135         SocketHandlerConnected(snap.Nodes(), events_per_sock);
    136     }
    137 
    138@@ -2108,13 +2108,13 @@ void CConnman::SocketHandler()
    139 void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
    140                                       const Sock::EventsPerSock& events_per_sock)
    141 {
    142     AssertLockNotHeld(m_total_bytes_sent_mutex);
    143 
    144     for (CNode* pnode : nodes) {
    145-        if (m_interrupt_net->interrupted()) {
    146+        if (m_interrupt_net->Interrupted()) {
    147             return;
    148         }
    149 
    150         //
    151         // Receive
    152         //
    153@@ -2205,13 +2205,13 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
    154     }
    155 }
    156 
    157 void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
    158 {
    159     for (const ListenSocket& listen_socket : vhListenSocket) {
    160-        if (m_interrupt_net->interrupted()) {
    161+        if (m_interrupt_net->Interrupted()) {
    162             return;
    163         }
    164         const auto it = events_per_sock.find(listen_socket.sock);
    165         if (it != events_per_sock.end() && it->second.occurred & Sock::RECV) {
    166             AcceptConnection(listen_socket);
    167         }
    168@@ -2219,13 +2219,13 @@ void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock
    169 }
    170 
    171 void CConnman::ThreadSocketHandler()
    172 {
    173     AssertLockNotHeld(m_total_bytes_sent_mutex);
    174 
    175-    while (!m_interrupt_net->interrupted()) {
    176+    while (!m_interrupt_net->Interrupted()) {
    177         DisconnectNodes();
    178         NotifyNumConnectionsChanged();
    179         SocketHandler();
    180     }
    181 }
    182 
    183@@ -2243,14 +2243,14 @@ void CConnman::ThreadDNSAddressSeed()
    184     int outbound_connection_count = 0;
    185 
    186     if (!gArgs.GetArgs("-seednode").empty()) {
    187         auto start = NodeClock::now();
    188         constexpr std::chrono::seconds SEEDNODE_TIMEOUT = 30s;
    189         LogPrintf("-seednode enabled. Trying the provided seeds for %d seconds before defaulting to the dnsseeds.\n", SEEDNODE_TIMEOUT.count());
    190-        while (!m_interrupt_net->interrupted()) {
    191-            if (!m_interrupt_net->sleep_for(500ms)) {
    192+        while (!m_interrupt_net->Interrupted()) {
    193+            if (!m_interrupt_net->SleepFor(500ms)) {
    194                 return;
    195             }
    196 
    197             // Abort if we have spent enough time without reaching our target.
    198             // Giving seed nodes 30 seconds so this does not become a race against fixedseeds (which triggers after 1 min)
    199             if (NodeClock::now() > start + SEEDNODE_TIMEOUT) {
    200@@ -2307,13 +2307,13 @@ void CConnman::ThreadDNSAddressSeed()
    201                     std::chrono::seconds to_wait = seeds_wait_time;
    202                     while (to_wait.count() > 0) {
    203                         // if sleeping for the MANY_PEERS interval, wake up
    204                         // early to see if we have enough peers and can stop
    205                         // this thread entirely freeing up its resources
    206                         std::chrono::seconds w = std::min(DNSSEEDS_DELAY_FEW_PEERS, to_wait);
    207-                        if (!m_interrupt_net->sleep_for(w)) return;
    208+                        if (!m_interrupt_net->SleepFor(w)) return;
    209                         to_wait -= w;
    210 
    211                         if (GetFullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) {
    212                             if (found > 0) {
    213                                 LogPrintf("%d addresses found from DNS seeds\n", found);
    214                                 LogPrintf("P2P peers available. Finished DNS seeding.\n");
    215@@ -2323,19 +2323,19 @@ void CConnman::ThreadDNSAddressSeed()
    216                             return;
    217                         }
    218                     }
    219                 }
    220             }
    221 
    222-            if (m_interrupt_net->interrupted()) return;
    223+            if (m_interrupt_net->Interrupted()) return;
    224 
    225             // hold off on querying seeds if P2P network deactivated
    226             if (!fNetworkActive) {
    227                 LogPrintf("Waiting for network to be reactivated before querying DNS seeds.\n");
    228                 do {
    229-                    if (!m_interrupt_net->sleep_for(1s)) return;
    230+                    if (!m_interrupt_net->SleepFor(1s)) return;
    231                 } while (!fNetworkActive);
    232             }
    233 
    234             LogPrintf("Loading addresses from DNS seed %s\n", seed);
    235             // If -proxy is in use, we make an ADDR_FETCH connection to the DNS resolved peer address
    236             // for the base dns seed domain in chainparams
    237@@ -2524,18 +2524,18 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
    238             for (const std::string& strAddr : connect)
    239             {
    240                 CAddress addr(CService(), NODE_NONE);
    241                 OpenNetworkConnection(addr, false, {}, strAddr.c_str(), ConnectionType::MANUAL, /*use_v2transport=*/use_v2transport);
    242                 for (int i = 0; i < 10 && i < nLoop; i++)
    243                 {
    244-                    if (!m_interrupt_net->sleep_for(500ms)) {
    245+                    if (!m_interrupt_net->SleepFor(500ms)) {
    246                         return;
    247                     }
    248                 }
    249             }
    250-            if (!m_interrupt_net->sleep_for(500ms)) {
    251+            if (!m_interrupt_net->SleepFor(500ms)) {
    252                 return;
    253             }
    254             PerformReconnections();
    255         }
    256     }
    257 
    258@@ -2555,13 +2555,13 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
    259     constexpr std::chrono::seconds ADD_NEXT_SEEDNODE = 10s;
    260 
    261     if (!add_fixed_seeds) {
    262         LogPrintf("Fixed seeds are disabled\n");
    263     }
    264 
    265-    while (!m_interrupt_net->interrupted()) {
    266+    while (!m_interrupt_net->Interrupted()) {
    267         if (add_addr_fetch) {
    268             add_addr_fetch = false;
    269             const auto& seed{SpanPopBack(seed_nodes)};
    270             AddAddrFetch(seed);
    271 
    272             if (addrman.Size() == 0) {
    273@@ -2570,20 +2570,20 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
    274                 LogInfo("Couldn't connect to peers from addrman after %d seconds. Adding seednode (%s) to addrfetch\n", ADD_NEXT_SEEDNODE.count(), seed);
    275             }
    276         }
    277 
    278         ProcessAddrFetch();
    279 
    280-        if (!m_interrupt_net->sleep_for(500ms)) {
    281+        if (!m_interrupt_net->SleepFor(500ms)) {
    282             return;
    283         }
    284 
    285         PerformReconnections();
    286 
    287         CountingSemaphoreGrant<> grant(*semOutbound);
    288-        if (m_interrupt_net->interrupted()) {
    289+        if (m_interrupt_net->Interrupted()) {
    290             return;
    291         }
    292 
    293         const std::unordered_set<Network> fixed_seed_networks{GetReachableEmptyNetworks()};
    294         if (add_fixed_seeds && !fixed_seed_networks.empty()) {
    295             // When the node starts with an empty peers.dat, there are a few other sources of peers before
    296@@ -2753,13 +2753,13 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
    297         addrman.ResolveCollisions();
    298 
    299         const auto current_time{NodeClock::now()};
    300         int nTries = 0;
    301         const auto reachable_nets{g_reachable_nets.All()};
    302 
    303-        while (!m_interrupt_net->interrupted()) {
    304+        while (!m_interrupt_net->Interrupted()) {
    305             if (anchor && !m_anchors.empty()) {
    306                 const CAddress addr = m_anchors.back();
    307                 m_anchors.pop_back();
    308                 if (!addr.IsValid() || IsLocal(addr) || !g_reachable_nets.Contains(addr) ||
    309                     !m_msgproc->HasAllDesirableServiceFlags(addr.nServices) ||
    310                     outbound_ipv46_peer_netgroups.count(m_netgroupman.GetGroup(addr))) continue;
    311@@ -2855,13 +2855,13 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
    312             break;
    313         }
    314 
    315         if (addrConnect.IsValid()) {
    316             if (fFeeler) {
    317                 // Add small amount of random noise before connection to avoid synchronization.
    318-                if (!m_interrupt_net->sleep_for(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
    319+                if (!m_interrupt_net->SleepFor(rng.rand_uniform_duration<CThreadInterrupt::Clock>(FEELER_SLEEP_WINDOW))) {
    320                     return;
    321                 }
    322                 LogDebug(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToStringAddrPort());
    323             }
    324 
    325             if (preferred_net != std::nullopt) LogDebug(BCLog::NET, "Making network specific connection to %s on %s.\n", addrConnect.ToStringAddrPort(), GetNetworkName(preferred_net.value()));
    326@@ -2966,19 +2966,19 @@ void CConnman::ThreadOpenAddedConnections()
    327                 // the addednodeinfo state might change.
    328                 break;
    329             }
    330             tried = true;
    331             CAddress addr(CService(), NODE_NONE);
    332             OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
    333-            if (!m_interrupt_net->sleep_for(500ms)) return;
    334+            if (!m_interrupt_net->SleepFor(500ms)) return;
    335             grant = CountingSemaphoreGrant<>(*semAddnode, /*fTry=*/true);
    336         }
    337         // See if any reconnections are desired.
    338         PerformReconnections();
    339         // Retry every 60 seconds if a connection was attempted, otherwise two seconds
    340-        if (!m_interrupt_net->sleep_for(tried ? 60s : 2s)) {
    341+        if (!m_interrupt_net->SleepFor(tried ? 60s : 2s)) {
    342             return;
    343         }
    344     }
    345 }
    346 
    347 // if successful, this moves the passed grant to the constructed node
    348@@ -2987,13 +2987,13 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
    349     AssertLockNotHeld(m_unused_i2p_sessions_mutex);
    350     assert(conn_type != ConnectionType::INBOUND);
    351 
    352     //
    353     // Initiate outbound network connection
    354     //
    355-    if (m_interrupt_net->interrupted()) {
    356+    if (m_interrupt_net->Interrupted()) {
    357         return;
    358     }
    359     if (!fNetworkActive) {
    360         return;
    361     }
    362     if (!pszDest) {
    363@@ -3075,19 +3075,19 @@ void CConnman::ThreadI2PAcceptIncoming()
    364     auto err_wait = err_wait_begin;
    365 
    366     bool advertising_listen_addr = false;
    367     i2p::Connection conn;
    368 
    369     auto SleepOnFailure = [&]() {
    370-        m_interrupt_net->sleep_for(err_wait);
    371+        m_interrupt_net->SleepFor(err_wait);
    372         if (err_wait < err_wait_cap) {
    373             err_wait += 1s;
    374         }
    375     };
    376 
    377-    while (!m_interrupt_net->interrupted()) {
    378+    while (!m_interrupt_net->Interrupted()) {
    379 
    380         if (!m_i2p_sam_session->Listen(conn)) {
    381             if (advertising_listen_addr && conn.me.IsValid()) {
    382                 RemoveLocal(conn.me);
    383                 advertising_listen_addr = false;
    384             }
    385@@ -3347,13 +3347,13 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
    386     }
    387 
    388     //
    389     // Start threads
    390     //
    391     assert(m_msgproc);
    392-    m_interrupt_net->reset();
    393+    m_interrupt_net->Reset();
    394     flagInterruptMsgProc = false;
    395 
    396     {
    397         LOCK(mutexMsgProc);
    398         fMsgProcWake = false;
    399     }
    400diff --git a/src/test/fuzz/util/threadinterrupt.cpp b/src/test/fuzz/util/threadinterrupt.cpp
    401index 5dd87e0588..5e23402447 100644
    402--- a/src/test/fuzz/util/threadinterrupt.cpp
    403+++ b/src/test/fuzz/util/threadinterrupt.cpp
    404@@ -7,16 +7,16 @@
    405 
    406 FuzzedThreadInterrupt::FuzzedThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider)
    407     : m_fuzzed_data_provider{fuzzed_data_provider}
    408 {
    409 }
    410 
    411-bool FuzzedThreadInterrupt::interrupted() const
    412+bool FuzzedThreadInterrupt::Interrupted() const
    413 {
    414     return m_fuzzed_data_provider.ConsumeBool();
    415 }
    416 
    417-bool FuzzedThreadInterrupt::sleep_for(Clock::duration)
    418+bool FuzzedThreadInterrupt::SleepFor(Clock::duration)
    419 {
    420     SetMockTime(ConsumeTime(m_fuzzed_data_provider)); // Time could go backwards.
    421     return m_fuzzed_data_provider.ConsumeBool();
    422 }
    423diff --git a/src/test/fuzz/util/threadinterrupt.h b/src/test/fuzz/util/threadinterrupt.h
    424index d56aefd919..71ce2ddd3f 100644
    425--- a/src/test/fuzz/util/threadinterrupt.h
    426+++ b/src/test/fuzz/util/threadinterrupt.h
    427@@ -15,14 +15,14 @@
    428  */
    429 class FuzzedThreadInterrupt : public CThreadInterrupt
    430 {
    431 public:
    432     explicit FuzzedThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider);
    433 
    434-    virtual bool interrupted() const override;
    435-    virtual bool sleep_for(Clock::duration) override;
    436+    virtual bool Interrupted() const override;
    437+    virtual bool SleepFor(Clock::duration) override;
    438 
    439 private:
    440     FuzzedDataProvider& m_fuzzed_data_provider;
    441 };
    442 
    443 [[nodiscard]] inline std::shared_ptr<CThreadInterrupt> ConsumeThreadInterrupt(FuzzedDataProvider& fuzzed_data_provider)
    444diff --git a/src/util/threadinterrupt.cpp b/src/util/threadinterrupt.cpp
    445index aaa9e831a9..8402181cf0 100644
    446--- a/src/util/threadinterrupt.cpp
    447+++ b/src/util/threadinterrupt.cpp
    448@@ -6,23 +6,23 @@
    449 #include <util/threadinterrupt.h>
    450 
    451 #include <sync.h>
    452 
    453 CThreadInterrupt::CThreadInterrupt() : flag(false) {}
    454 
    455-bool CThreadInterrupt::interrupted() const
    456+bool CThreadInterrupt::Interrupted() const
    457 {
    458     return flag.load(std::memory_order_acquire);
    459 }
    460 
    461 CThreadInterrupt::operator bool() const
    462 {
    463-    return interrupted();
    464+    return Interrupted();
    465 }
    466 
    467-void CThreadInterrupt::reset()
    468+void CThreadInterrupt::Reset()
    469 {
    470     flag.store(false, std::memory_order_release);
    471 }
    472 
    473 void CThreadInterrupt::operator()()
    474 {
    475@@ -30,11 +30,11 @@ void CThreadInterrupt::operator()()
    476         LOCK(mut);
    477         flag.store(true, std::memory_order_release);
    478     }
    479     cond.notify_all();
    480 }
    481 
    482-bool CThreadInterrupt::sleep_for(Clock::duration rel_time)
    483+bool CThreadInterrupt::SleepFor(Clock::duration rel_time)
    484 {
    485     WAIT_LOCK(mut, lock);
    486     return !cond.wait_for(lock, rel_time, [this]() { return flag.load(std::memory_order_acquire); });
    487 }
    488diff --git a/src/util/threadinterrupt.h b/src/util/threadinterrupt.h
    489index 8b393c26df..e11ff1881b 100644
    490--- a/src/util/threadinterrupt.h
    491+++ b/src/util/threadinterrupt.h
    492@@ -30,27 +30,27 @@ public:
    493 
    494     CThreadInterrupt();
    495 
    496     virtual ~CThreadInterrupt() = default;
    497 
    498     /// Return true if `operator()()` has been called.
    499-    virtual bool interrupted() const;
    500+    virtual bool Interrupted() const;
    501 
    502     /// An alias for `interrupted()`.
    503     virtual explicit operator bool() const;
    504 
    505     /// Interrupt any sleeps. After this `interrupted()` will return `true`.
    506     virtual void operator()() EXCLUSIVE_LOCKS_REQUIRED(!mut);
    507 
    508     /// Reset to an non-interrupted state.
    509-    virtual void reset();
    510+    virtual void Reset();
    511 
    512     /// Sleep for the given duration.
    513     /// [@retval](/bitcoin-bitcoin/contributor/retval/) true The time passed.
    514     /// [@retval](/bitcoin-bitcoin/contributor/retval/) false The sleep was interrupted.
    515-    virtual bool sleep_for(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
    516+    virtual bool SleepFor(Clock::duration rel_time) EXCLUSIVE_LOCKS_REQUIRED(!mut);
    517 
    518 private:
    519     std::condition_variable cond;
    520     Mutex mut;
    521     std::atomic<bool> flag;
    522 };
    
  100. dergoegge approved
  101. dergoegge commented at 10:28 am on July 11, 2025: member
    Code review ACK 0802398e749c5e16fa7085cd87c91a31bbe043bd
  102. vasild commented at 11:50 am on September 2, 2025: contributor
    @brunoerg, this has 2 ACKs and a stale ACK from you, maybe you would like to take a look and re-ACK it?

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-09-17 09:13 UTC

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