fuzz: connman: strengthen assertions and extend coverage #35220

pull brunoerg wants to merge 5 commits into bitcoin:master from brunoerg:2026-04-fuzz-connman-asserts changing 1 files +58 −13
  1. brunoerg commented at 8:39 AM on May 6, 2026: contributor

    This PR improves the connman fuzz target by replacing some "(void)" calls with actual invariant checks, adding coverage for previously uncovered methods, and exercising more initialization states.

    • Set m_local_services, m_use_addrman_outgoing, and m_max_automatic_connections via fuzzed values before Init() to explore more startup configurations.

    • Add network activity and outbound-bytes invariants.

    • Add AddNode/RemoveAddedNode invariants: e.g. a successful AddNode increases GetAddedNodeInfo() by one; adding the same node again must fail; a subsequent RemoveAddedNode must succeed and restore the original count.

    • Add coverage for AddLocalServices/RemoveLocalServices.

  2. DrahtBot added the label Fuzzing on May 6, 2026
  3. DrahtBot commented at 8:39 AM on May 6, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    Code Coverage & Benchmarks

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

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK nervana21, frankomosh

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    No conflicts as of last run.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  4. frankomosh commented at 2:38 PM on May 11, 2026: contributor

    Concept ACK. I think it is a good thing to have these assertions. Will review each one of them separately.

  5. in src/test/fuzz/connman.cpp:271 in dfd3744b7b
     271 | -    (void)connman.GetMaxOutboundTimeframe();
     272 | -    (void)connman.GetMaxOutboundTimeLeftInCycle();
     273 | -    (void)connman.GetNetworkActive();
     274 | +    const auto max_outbound_timeframe{connman.GetMaxOutboundTimeframe()};
     275 | +    const auto time_left_in_cycle{connman.GetMaxOutboundTimeLeftInCycle()};
     276 | +    assert(time_left_in_cycle <= max_outbound_timeframe);
    


    marcofleon commented at 5:39 PM on May 13, 2026:

    If we're using FuzzedThreadInterrupt::sleep_for for fuzzing here, then time could go backwards so not sure if this assertion ends up making much sense.

    <details> <summary>stack trace</summary>

    New crash id='c1a9eafd29f404091ffb4ea9ff64f01cc82e49b9' (project='bitcoin' harness='connman')
    Base64: XJB5XFxcXABbXFxcJ1xcKgB5//////9381l5AHkkeP//d/NZeQBbXNBcXCdcXCr9/f39/f39/f39/f39/f39/f39eVwxMTFCMTExMYoAef//d/Jc+////yoAef//////d/NZeQBbXFxcMV0xojExKTExAwAAAAAAAAA//SQAeYKGXFxcAQAACFx5Knl5eVt5igB5//938ll5AFtcXFxcJ1xcKnl5eVs/eSQA/YKGXG8AAAAAAAD///8IAAAAZ2V0YmxvY2t0AAAAAAAAAJCQkJCQkJCQXCg=
    ===== stack trace ===== 
    INFO: Running with entropic power schedule (0xFF, 100).
    INFO: Seed: 551831089
    INFO: Loaded 1 modules   (523390 inline 8-bit counters): 523390 [0x559145f62880, 0x559145fe24fe), 
    INFO: Loaded 1 PC tables (523390 PCs): 523390 [0x559145fe2500,0x5591467dece0), 
    /workdir/out/libfuzzer_ubsan/fuzz: Running 1 inputs 1 time(s) each.
    Running: /workdir/workspace/solutions/crash-f0a3fbcd8af1f458dd289f8154f89e3d8e4279c0
    fuzz: test/fuzz/connman.cpp:271: void connman_fuzz_target(FuzzBufferType): Assertion `time_left_in_cycle <= max_outbound_timeframe' failed.
    ==2604== ERROR: libFuzzer: deadly signal
        [#0](/bitcoin-bitcoin/0/) 0x55914445aaf4 in __sanitizer_print_stack_trace /llvm-project/compiler-rt/lib/ubsan/ubsan_diag_standalone.cpp:31:3
        [#1](/bitcoin-bitcoin/1/) 0x5591443cc178 in fuzzer::PrintStackTrace() /llvm-project/compiler-rt/lib/fuzzer/FuzzerUtil.cpp:210:5
        [#2](/bitcoin-bitcoin/2/) 0x5591443aeb93 in fuzzer::Fuzzer::CrashCallback() /llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:231:3
        [#3](/bitcoin-bitcoin/3/) 0x7ff09eb77def  (/lib/x86_64-linux-gnu/libc.so.6+0x3fdef) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)
        [#4](/bitcoin-bitcoin/4/) 0x7ff09ebcc95b  (/lib/x86_64-linux-gnu/libc.so.6+0x9495b) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)
        [#5](/bitcoin-bitcoin/5/) 0x7ff09eb77cc1 in raise (/lib/x86_64-linux-gnu/libc.so.6+0x3fcc1) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)
        [#6](/bitcoin-bitcoin/6/) 0x7ff09eb604ab in abort (/lib/x86_64-linux-gnu/libc.so.6+0x284ab) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)
        [#7](/bitcoin-bitcoin/7/) 0x7ff09eb6041f  (/lib/x86_64-linux-gnu/libc.so.6+0x2841f) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)
        [#8](/bitcoin-bitcoin/8/) 0x5591445aa080 in connman_fuzz_target(std::span<unsigned char const, 18446744073709551615ul>) /workdir/bitcoin/src/test/fuzz/connman.cpp:271:5
        [#9](/bitcoin-bitcoin/9/) 0x559144915aed in std::function<void (std::span<unsigned char const, 18446744073709551615ul>)>::operator()(std::span<unsigned char const, 18446744073709551615ul>) const /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/bits/std_function.h:591:9
        [#10](/bitcoin-bitcoin/10/) 0x559144915aed in test_one_input(std::span<unsigned char const, 18446744073709551615ul>) /workdir/bitcoin/src/test/fuzz/fuzz.cpp:86:5
        [#11](/bitcoin-bitcoin/11/) 0x559144915aed in LLVMFuzzerTestOneInput /workdir/bitcoin/src/test/fuzz/fuzz.cpp:214:5
        [#12](/bitcoin-bitcoin/12/) 0x5591443b02eb in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) /llvm-project/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:619:13
        [#13](/bitcoin-bitcoin/13/) 0x559144399f92 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) /llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:329:6
        [#14](/bitcoin-bitcoin/14/) 0x55914439fe30 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) /llvm-project/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:864:9
        [#15](/bitcoin-bitcoin/15/) 0x5591443ccb02 in main /llvm-project/compiler-rt/lib/fuzzer/FuzzerMain.cpp:20:10
        [#16](/bitcoin-bitcoin/16/) 0x7ff09eb61ca7  (/lib/x86_64-linux-gnu/libc.so.6+0x29ca7) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)
        [#17](/bitcoin-bitcoin/17/) 0x7ff09eb61d64 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29d64) (BuildId: 58749c528985eab03e6700ebc1469fa50aa41219)
        [#18](/bitcoin-bitcoin/18/) 0x559144392f80 in _start (/workdir/out/libfuzzer_ubsan/fuzz+0x1745f80)
    
    NOTE: libFuzzer has rudimentary signal handlers.
          Combine libFuzzer with AddressSanitizer or similar for better crash reports.
    SUMMARY: libFuzzer: deadly signal
    

    </details>


    brunoerg commented at 12:07 PM on May 19, 2026:

    Yes, better to remove it. Will do.


    brunoerg commented at 2:00 PM on May 19, 2026:

    Done.

  6. in src/test/fuzz/connman.cpp:231 in dfd3744b7b outdated
     262 | @@ -228,20 +263,26 @@ FUZZ_TARGET(connman, .init = initialize_connman)
     263 |                  connman.SocketHandlerPublic();
     264 |              });
     265 |      }
     266 | -    (void)connman.GetAddedNodeInfo(fuzzed_data_provider.ConsumeBool());
    


    frankomosh commented at 10:04 AM on May 14, 2026:

    Noticed that include_connected=false path in GetAddedNodeInfo was uncovered was uncovered before this PR and remains so after. Checked coverage against existing corpus, and lines 2976 - 2977 and 2987 - 2988 in net.cpp have not been reached.

    Tracing through the code, the gap seems structural, nodes added via AddTestNode get fuzzed CAddress values while AddNode takes arbitrary strings, so mapConnectedByName is never populated relative to m_added_node_params. If it's worth addressing, I think collecting m_addr_name from test nodes during setup and reusing those names in the AddNode lambda could reach these paths?

    <details> <summary>Suggested fix</summary>

    @@ -109,12 +109,14 @@
    CSubNet random_subnet;
    std::string random_string;
    + std::vector<std::string> node_addr_names;
    
    LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) {
    CNode& p2p_node{*ConsumeNodeAsUniquePtr(fuzzed_data_provider).release()};
    p2p_node.fSuccessfullyConnected = true;
    connman.AddTestNode(p2p_node);
    + node_addr_names.push_back(p2p_node.m_addr_name);
    }
    
    @@ -133,14 +135,17 @@
    [&] {
    + const std::string& node_str = (!node_addr_names.empty() && fuzzed_data_provider.ConsumeBool())
    + ? PickValue(fuzzed_data_provider, node_addr_names)
    + : random_string;
    const auto added_node_info{connman.GetAddedNodeInfo(true)};
    - const auto add_node{connman.AddNode({random_string, fuzzed_data_provider.ConsumeBool()})};
    + const auto add_node{connman.AddNode({node_str, fuzzed_data_provider.ConsumeBool()})};
    if (add_node) {
    - assert(!connman.AddNode({random_string, fuzzed_data_provider.ConsumeBool()}));
    + assert(!connman.AddNode({node_str, fuzzed_data_provider.ConsumeBool()}));
    ...
    - assert(connman.RemoveAddedNode(random_string));
    + assert(connman.RemoveAddedNode(node_str));
    ...
    }
    
    @@ -263,6 +268,7 @@
    }
    + (void)connman.GetAddedNodeInfo(false);
    (void)connman.GetExtraFullOutboundCount();
    

    </details>

    Definitely not a blocker, but might be a solution to a structural coverage gap for this harness?


    brunoerg commented at 2:07 PM on May 19, 2026:

    It makes sense. By using the m_addr_name from the nodes we can exercise the path with include_connected=false.

  7. nervana21 commented at 1:50 PM on May 19, 2026: contributor

    Concept ACK

  8. brunoerg force-pushed on May 19, 2026
  9. brunoerg commented at 2:07 PM on May 19, 2026: contributor

    Force-pushed addressing #35220 (review) and #35220 (review).

  10. DrahtBot added the label Needs rebase on May 19, 2026
  11. brunoerg force-pushed on May 19, 2026
  12. brunoerg commented at 4:30 PM on May 19, 2026: contributor

    Rebased

  13. DrahtBot removed the label Needs rebase on May 19, 2026
  14. in src/test/fuzz/connman.cpp:144 in 9d3d16e01b
     136 | @@ -125,7 +137,20 @@ FUZZ_TARGET(connman, .init = initialize_connman)
     137 |                  random_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
     138 |              },
     139 |              [&] {
     140 | -                connman.AddNode({random_string, fuzzed_data_provider.ConsumeBool()});
     141 | +                const std::string& node_str = (!node_addr_names.empty() && fuzzed_data_provider.ConsumeBool())
     142 | +                    ? PickValue(fuzzed_data_provider, node_addr_names)
     143 | +                    : random_string;
     144 | +                const auto added_node_info{connman.GetAddedNodeInfo(/*include_connected=*/true)};
     145 | +                const auto add_node{connman.AddNode({node_str, fuzzed_data_provider.ConsumeBool()})};
    


    nervana21 commented at 12:52 PM on May 26, 2026:

    nit:

                    const auto add_node{connman.AddNode({node_str, /*use_v2transport=*/fuzzed_data_provider.ConsumeBool()})};
    

    brunoerg commented at 9:01 PM on May 28, 2026:

    Done, thanks.

  15. in src/test/fuzz/connman.cpp:146 in 9d3d16e01b
     142 | +                    ? PickValue(fuzzed_data_provider, node_addr_names)
     143 | +                    : random_string;
     144 | +                const auto added_node_info{connman.GetAddedNodeInfo(/*include_connected=*/true)};
     145 | +                const auto add_node{connman.AddNode({node_str, fuzzed_data_provider.ConsumeBool()})};
     146 | +                if (add_node) {
     147 | +                    assert(!connman.AddNode({node_str, fuzzed_data_provider.ConsumeBool()}));
    


    nervana21 commented at 12:54 PM on May 26, 2026:

    nit:

                        assert(!connman.AddNode({node_str, /*use_v2transport=*/fuzzed_data_provider.ConsumeBool()}));
    

    brunoerg commented at 9:01 PM on May 28, 2026:

    Done, thanks.

  16. in src/test/fuzz/connman.cpp:142 in 9d3d16e01b outdated
     136 | @@ -125,7 +137,20 @@ FUZZ_TARGET(connman, .init = initialize_connman)
     137 |                  random_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
     138 |              },
     139 |              [&] {
     140 | -                connman.AddNode({random_string, fuzzed_data_provider.ConsumeBool()});
     141 | +                const std::string& node_str = (!node_addr_names.empty() && fuzzed_data_provider.ConsumeBool())
     142 | +                    ? PickValue(fuzzed_data_provider, node_addr_names)
     143 | +                    : random_string;
    


    nervana21 commented at 1:17 PM on May 26, 2026:

    Would coverage be improved if instead of using the random_string here, we consumed fuzzed bytes?

                    const std::string node_str = (!node_addr_names.empty() && fuzzed_data_provider.ConsumeBool())
                        ? PickValue(fuzzed_data_provider, node_addr_names)
                        : fuzzed_data_provider.ConsumeRandomLengthString(64);
    

    frankomosh commented at 11:22 AM on May 28, 2026:

    I think random_string already holds fuzz-derived input. Look at the dedicated CallOneOf holding it:

    [&] {
        random_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
    },
    [&] {
        const std::string& node_str = (!node_addr_names.empty() && fuzzed_data_provider.ConsumeBool())
            ? PickValue(fuzzed_data_provider, node_addr_names)
            : random_string;
    },
    

    So maybe this can be left as is?


    nervana21 commented at 4:44 PM on May 28, 2026:

    Ahh, I understand now. TYSM for fielding all my silly questions.

  17. in src/test/fuzz/connman.cpp:89 in 9d3d16e01b
      80 | @@ -81,6 +81,13 @@ FUZZ_TARGET(connman, .init = initialize_connman)
      81 |      options.m_msgproc = &net_events;
      82 |      options.nMaxOutboundLimit = max_outbound_limit;
      83 |  
      84 | +    const auto local_services{ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS)};
      85 | +    options.m_local_services = local_services;
      86 | +
      87 | +    const auto use_addrman_outgoing{fuzzed_data_provider.ConsumeBool()};
      88 | +    options.m_use_addrman_outgoing = use_addrman_outgoing;
      89 | +    options.m_max_automatic_connections = DEFAULT_MAX_PEER_CONNECTIONS;
    


    nervana21 commented at 1:25 PM on May 26, 2026:

    The PR description states that m_max_automatic_connections is fuzzed, but that doesn't seem to be the case. If fuzzing this value is not intended or desired, then I think this line should be hoisted above to go in between these two lines like:

        CConnman::Options options;
        options.m_max_automatic_connections = DEFAULT_MAX_PEER_CONNECTIONS;
        options.m_msgproc = &net_events;
    

    brunoerg commented at 9:02 PM on May 28, 2026:

    Thanks, this is a leftover from a test I was doing, just changed it to be fuzzed.

  18. in src/test/fuzz/connman.cpp:247 in 9d3d16e01b


    nervana21 commented at 1:46 PM on May 26, 2026:

    Since we now have a dedicated SetNetworkActive arm, would it make sense to remove this line?

    connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool());

    IIUC, this logic is now tested elsewhere and it's unrelated to the rest of the logic in this arm of the fuzz harness.


    frankomosh commented at 11:33 AM on May 28, 2026:

    Since we now have a dedicated SetNetworkActive arm, would it make sense to remove this line?

    I think the two calls serve different purposes, no?

    IIUC, this logic is now tested elsewhere

    Do you mean outside this harness when you say 'elsewhere'?


    nervana21 commented at 4:50 PM on May 28, 2026:

    Oh I get it now. I meant within this harness, but before I didn't see in CreateNodeFromAcceptedSocket how we used fNetworkActive. Now that I found it, it makes sense why we'd want to toggle it here.

  19. brunoerg force-pushed on May 28, 2026
  20. brunoerg commented at 9:03 PM on May 28, 2026: contributor

    Force-pushed addressing #35220 (review) and some nits.

  21. DrahtBot added the label CI failed on May 28, 2026
  22. fuzz: connman: add network activity invariants 4b84c9125a
  23. fuzz: connman: set m_local_services/m_use_addrman_outgoing/m_max_automatic_connections a5859edef4
  24. fuzz: connman: add AddNode/RemoveAddedNode invariants
    Co-authored-by: frankomosh <frankomosh197@gmail.com>
    bd6124d7b6
  25. fuzz: connman: add outbound-bytes invariants 138f5ce745
  26. fuzz: connman: cover AddLocalServices/RemoveLocalServices 0180f91ad9
  27. brunoerg force-pushed on May 29, 2026
  28. DrahtBot removed the label CI failed on May 29, 2026
  29. nervana21 commented at 5:03 PM on May 29, 2026: contributor

    tACK 0180f91ad99539b985a0ac98da71ccd6f025a427

    I've tested the master branch vs commit 0180f91ad99539b985a0ac98da71ccd6f025a427 on a corpus of 3391 inputs and got the below diffs.

    <details>

    SetNetworkActive — net.cpp
    - 3397|  3.99k|{
    - 3398|  3.99k|    LogInfo("%s: %s\n", __func__, active);
    + 3397|  10.2k|{
    + 3398|  10.2k|    LogInfo("%s: %s\n", __func__, active);
    
    OutboundTargetReached — net.cpp
    - 3963|  15.3k|    if (nMaxOutboundLimit == 0)
    - 3964|    697|        return false;
    + 3963|  17.4k|    if (nMaxOutboundLimit == 0)
    + 3964|  2.57k|        return false;
    
    AddNode — net.cpp
    - 3783|     20|        if (add.m_added_node == it.m_added_node || (resolved_is_valid && resolved == LookupNumeric(it.m_added_node, GetDefaultPort(it.m_added_node)))) return false;
    - 3786|     66|    m_added_node_params.push_back(add);
    - 3787|     66|    return true;
    + 3783|     58|        if (add.m_added_node == it.m_added_node || (resolved_is_valid && resolved == LookupNumeric(it.m_added_node, GetDefaultPort(it.m_added_node)))) return false;
    + 3786|     49|    m_added_node_params.push_back(add);
    + 3787|     49|    return true;
    
    GetAddedNodeInfo — net.cpp
    - 2987|      0|                if (!include_connected) {
    - 2988|      0|                    continue;
    - 2989|      0|                }
    - 2990|      0|                addedNode.resolvedAddress = it->second.second;
    - 2991|      0|                addedNode.fConnected = true;
    - 2992|      0|                addedNode.fInbound = it->second.first;
    + 2987|     28|                if (!include_connected) {
    + 2988|      8|                    continue;
    + 2989|      8|                }
    + 2990|     20|                addedNode.resolvedAddress = it->second.second;
    + 2991|     20|                addedNode.fConnected = true;
    + 2992|     20|                addedNode.fInbound = it->second.first;
    
    RemoveAddedNode — net.cpp
    - 3794|      4|        if (node == it->m_added_node) {
    - 3795|      4|            m_added_node_params.erase(it);
    - 3796|      4|            return true;
    + 3794|     34|        if (node == it->m_added_node) {
    + 3795|     32|            m_added_node_params.erase(it);
    + 3796|     32|            return true;
    
    AddLocalServices — net.h
    - 1376|      0|    void AddLocalServices(ServiceFlags services) { m_local_services = ServiceFlags(m_local_services | services); };
    + 1376|  90.1k|    void AddLocalServices(ServiceFlags services) { m_local_services = ServiceFlags(m_local_services | services); };
    
    RemoveLocalServices — net.h
    - 1377|      0|    void RemoveLocalServices(ServiceFlags services) { m_local_services = ServiceFlags(m_local_services & ~services); }
    + 1377|  90.1k|    void RemoveLocalServices(ServiceFlags services) { m_local_services = ServiceFlags(m_local_services & ~services); }
    

    </details>

  30. DrahtBot requested review from frankomosh on May 29, 2026
  31. frankomosh commented at 6:57 AM on June 8, 2026: contributor

    Code Review ACK 0180f91ad99539b985a0ac98da71ccd6f025a427. Reviewed assertions added in this PR against the CConnman implementation in net.cpp/net.h and they lgtm. Also did mutation(statement deletions) on SetNetworkActive, AddLocalServices, and RemoveLocalServices, and some operator inversions ( == to != , <= to >= ) and all are killed

  32. sedited requested review from marcofleon on Jun 8, 2026

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-06-11 10:51 UTC

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