p2p: Prevent integer overflow in LocalServiceInfo::nScore #34028

pull codeabysss wants to merge 1 commits into bitcoin:master from codeabysss:p2p-fix-nscore-overflow changing 3 files +37 −2
  1. codeabysss commented at 8:08 pm on December 7, 2025: none

    The nScore field in LocalServiceInfo could overflow during version handshakes that wrap from INT_MAX to INT_MIN triggering undefined behavior in address selection

    SeenLocal() now saturates nScore at std::numeric_limits <int>::max(), so further increments stay capped

    The test LocalAddress_nScore_Overflow covers the saturation behavior

    This should fix the issue #24049

  2. DrahtBot commented at 8:08 pm on December 7, 2025: contributor

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

    Code Coverage & Benchmarks

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

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

    LLM Linter (✨ experimental)

    Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • UtilBuildAddress(0x002, 0x001, 0x001, 0x001) in src/test/net_tests.cpp

    2026-02-12 11:39:44

  3. fanquake renamed this:
    [p2p] Saturate LocalServiceInfo::nScore to prevent overflow
    p2p: saturate LocalServiceInfo::nScore to prevent overflow
    on Dec 8, 2025
  4. DrahtBot added the label P2P on Dec 8, 2025
  5. in src/net.cpp:323 in f11b165f35 outdated
    319@@ -320,7 +320,10 @@ bool SeenLocal(const CService& addr)
    320     LOCK(g_maplocalhost_mutex);
    321     const auto it = mapLocalHost.find(addr);
    322     if (it == mapLocalHost.end()) return false;
    323-    ++it->second.nScore;
    324+    
    325+    if (it->second.nScore < std::numeric_limits<int>::max()) {
    


    brunoerg commented at 5:45 pm on December 10, 2025:

    I just ran a mutation test for this PR, here’s a mutant that wasn’t killed and might be worth addressing (writing tests that can detect this change):

     0diff --git a/src/net.cpp b/src/net.cpp
     1index bbe24c8d3e..ab159e0123 100644
     2--- a/src/net.cpp
     3+++ b/src/net.cpp
     4@@ -321,7 +321,7 @@ bool SeenLocal(const CService& addr)
     5     const auto it = mapLocalHost.find(addr);
     6     if (it == mapLocalHost.end()) return false;
     7
     8-    if (it->second.nScore < std::numeric_limits<int>::max()) {
     9+    if (it->second.nScore >  std::numeric_limits<int>::max()) {
    10         ++it->second.nScore;
    11     }
    12     return true;
    
  6. DrahtBot added the label CI failed on Dec 15, 2025
  7. DrahtBot commented at 5:31 pm on December 15, 2025: contributor

    🚧 At least one of the CI tasks failed. Task lint: https://github.com/bitcoin/bitcoin/actions/runs/20009656888/job/58081216641 LLM reason (✨ experimental): Lint check failed due to trailing whitespace detected (trailing_whitespace) in the source, causing the docker lint run to exit with code 1.

    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.

  8. DrahtBot commented at 9:34 am on December 22, 2025: contributor
    Could turn into draft while CI is red? @codeabysss Are you still working on this?
  9. codeabysss commented at 10:24 am on December 22, 2025: none

    Could turn into draft while CI is red?

    @codeabysss Are you still working on this?

    yes still working on this

  10. DrahtBot marked this as a draft on Dec 22, 2025
  11. codeabysss force-pushed on Dec 22, 2025
  12. codeabysss requested review from brunoerg on Dec 22, 2025
  13. codeabysss marked this as ready for review on Dec 22, 2025
  14. DrahtBot removed the label CI failed on Dec 23, 2025
  15. luke-jr referenced this in commit d8fc27f1a3 on Jan 13, 2026
  16. in src/test/net_tests.cpp:829 in a364f6303c
    824+        BOOST_CHECK(SeenLocal(addr));
    825+        BOOST_CHECK_EQUAL(GetnScore(addr), std::numeric_limits<int>::max());
    826+    }
    827+
    828+    RemoveLocal(addr);
    829+    g_reachable_nets.Remove(NET_IPV4);
    


    luke-jr commented at 10:36 pm on January 26, 2026:

    This line needs to go, or other tests might fail because they assume it’s there.

    (Failure depends on the order tests run in, which is semi-random)

  17. codeabysss force-pushed on Feb 5, 2026
  18. codeabysss requested review from luke-jr on Feb 5, 2026
  19. luke-jr referenced this in commit 8caf0836a8 on Feb 7, 2026
  20. DrahtBot added the label CI failed on Feb 7, 2026
  21. DrahtBot closed this on Feb 9, 2026

  22. DrahtBot reopened this on Feb 9, 2026

  23. DrahtBot removed the label CI failed on Feb 9, 2026
  24. in src/net.h:106 in 10100f86f5 outdated
     97@@ -102,6 +98,9 @@ static constexpr bool DEFAULT_V2_TRANSPORT{true};
     98 
     99 typedef int64_t NodeId;
    100 
    101+/** Get the score of a local address. */
    102+int GetnScore(const CService& addr);
    


    Crypt-iQ commented at 2:46 pm on February 11, 2026:
    I want to hear what others think, but I’m not a fan of changing the function signature for it to only be used in a test. I think the test and this change can go, and you can provide a small diff (that doesn’t get merged) to apply on top of this PR so reviewers can see that the issue is fixed.
  25. Crypt-iQ commented at 2:46 pm on February 11, 2026: contributor

    A couple comments:

    It seems like some of the wording in the OP was copied from the other PR #33072 and I think it would be better if you put things in your own words (my personal preference). Discussion in the other PR also mentioned that this has little to do with memory efficiency since it’s just 4 bytes at the end of the day. I think the commit message could also be cleaned up a bit with a short title and then a descriptive paragraph underneath.

  26. p2p: Prevent integer overflow in LocalServiceInfo::nScore
    The nScore field would wrap from INT_MAX to INT_MIN when incremented
    repeatedly, causing undefined behavior.
    
    Saturate nScore at INT_MAX in SeenLocal() to prevent overflow.
    
    Add test for saturation behavior.
    
    Signed-off-by: codeabysss <codeabysss@proton.me>
    3fc5948e1f
  27. codeabysss force-pushed on Feb 12, 2026
  28. codeabysss renamed this:
    p2p: saturate LocalServiceInfo::nScore to prevent overflow
    p2p: Prevent integer overflow in LocalServiceInfo::nScore
    on Feb 12, 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-02-17 12:13 UTC

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