Repository navigation
Conversation
BIP152 requires the first integer of sendcmpct to be 0 or 1. Reading it as a bool accepted any byte and treated every non-zero value as true. Read it as uint8_t and call Misbehaving() for values above 1, returning before any peer state is changed. Add a functional test. Backport of bitcoin#35550.
kwsantiago
reviewed
Oct 16, 2026
kwsantiago
left a comment
There was a problem hiding this comment.
Tested ACK c080c0e merged into 29.x-knots. New test fails on base. Of 156 mainnet peers that sent sendcmpct to me, including 63 signaling BLAKE2b, none sent above 1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Backport of Bitcoin Core bitcoin#35550 ("net_processing: fix BIP152 first integer interpretation"). BIP152 says the first integer of
sendcmpct"is interpreted as a boolean and MUST have a value of either 1 or 0". Knots reads it as abool, so any byte from 2 to 255 is silently accepted and treated astrue. This change reads the byte as auint8_tand, if it is greater than 1, callsMisbehaving()and returns before any peer state is touched. It is a protocol-conformance fix; a peer sending 2 currently gets the same behaviour as a peer sending 1, so there is no known exploit.Problem
src/net_processing.cpp:3750-3763(base58398baf33):Unserialize(Stream&, bool&)(src/serialize.h:286) isuint8_t f = ser_readdata8(s); a = f;, so every non-zero byte becomestruewith no error. Asendcmpctwith announce=2 (or 255) and version 2 setsm_provides_cmpctblocks,m_requested_hb_cmpctblocksandm_bip152_highbandwidth_fromexactly as announce=1 does, and the peer is not penalised.Reproduction
Base
58398baf33(upstream/29.x-knots), full build (Release,BUILD_TESTS=ON,BUILD_GUI=ON,BUILD_GUI_TESTS=ON,ENABLE_WALLET=ON, Qt5,WITH_BDB=OFF).Add the new test to
test/functional/p2p_compactblocks.py(included in this PR's diff) and run it without thenet_processing.cppchange:Optional state check (not part of the diff), which sends announce = 0, 1, 2, 255 and reads
bip152_hb_fromfromgetpeerinfo:repro_sendcmpct_state.py (unmodified-base version)
No injected delay or other test-only hack is needed.
Before
New test in
p2p_compactblocks.pyon the unmodified base (the peer is never disconnected, and the node logsreceived: sendcmpct (9 bytes)with noMisbehavingline after it):State script on the unmodified base:
Change
Plus the 13-line test
test_invalid_sendcmpct_announceintest/functional/p2p_compactblocks.py, taken unchanged from Core. The only adaptation from Core isMisbehaving(*peer, ...)instead ofMisbehaving(peer, ...), becausepeeris a pointer in this version ofProcessMessage. The laterboolassignments (m_requested_hb_cmpctblocks,m_bip152_highbandwidth_from) still compile, and after the check the value is only 0 or 1.After
New test in
p2p_compactblocks.py:State script, rewritten to expect rejection for values above 1:
Test results
Tested: base
58398baf33plus this change; the tested working tree was then committed unchanged asc080c0e25f. Full build as above, Release.ctest--extended(359 tests,ALLrow excluded)The two functional runs skipped the same 65 tests. Reasons, taken from the logs:
BDB has not been compiled(the--legacy-walletvariants, includingwallet_pruning)bitcoind has not been built with USDT tracepoints enabledprevious releases not available or disabledpython3-zmq module not availablefeature_bind_port_externalipneeds routable address 1.1.1.1feature_bind_port_discoverneeds routable addresses 1.1.1.1 and 2.2.2.2The baseline run used the unmodified
p2p_compactblocks.py; the run with the change used the version containing the new test.Caveats
MaybeDiscourageAndDisconnect). Core accepted the same trade-off; I did not test against real-world peers.p2p_compactblocks.pytest is.