Repository navigation
Conversation
|
Please rebase onto the oldest commit this applies to rather than the |
83af73a to
1a2066e
Compare
|
Rebased onto |
There was a problem hiding this comment.
Tested ACK 1a2066e merged into 29.x-knots. With a 2s delay, base aborts in BlockRequested; with this, the RPC returns "Peer not fully connected" and the block still fetches from another peer.
|
Please keep the original Core author on the backported commit, with Github-Pull and Rebased-From trailers. |
FetchBlock (used by the getblockfrompeer RPC) acquired a PeerRef before locking cs_main. If the peer was being finalized on a net thread in between, FinalizeNode could erase the peer's CNodeState, and BlockRequested would then hit assert(state != nullptr) and crash the node. Take cs_main before GetPeerRef so the CNodeState cannot be removed while FetchBlock runs. The lock order (cs_main, then m_peer_mutex) matches FinalizeNode. Github-Pull: bitcoin#35498 Rebased-From: 359680b
1a2066e to
63badae
Compare
|
Done: the backported commits now carry the original upstream author, with |
Summary
Backport of bitcoin#35498.
FetchBlock(used by thegetblockfrompeerRPC) takes aPeerRefbefore lockingcs_main. If thepeer is finalized on a net thread in between,
BlockRequestedhitsassert(state != nullptr)and the node aborts. This PR takescs_mainbefore
GetPeerRef.Based on
rpc_getblockfrompeer_wo_header(ce3641a), which alreadyincludes
fix_getblockfrompeer_rereq_err; both are needed for this to apply.Problem
All line numbers are at ce3641a.
FetchBlockcallsGetPeerRef(peer_id)(src/net_processing.cpp:2055)and only then
LOCK(cs_main)(:2061).FinalizeNodeholdscs_main(:1776), removes the peer from the peermap (
:1783) and erases itsCNodeState(:1817).FinalizeNoderuns between those two points inFetchBlock, theCNodeStateis gone whenBlockRequestedruns, andassert(state != nullptr)fires (:1365).BlockRequestedis called only if
block_index != nullptr(:2066).getblockfrompeer(src/rpc/blockchain.cpp:473),which runs on an HTTP worker thread. Peer disconnects run on net threads.
Reproduction
The window is very small, so the repro injects a delay. This is a test-only
change and is not part of the diff.
LOCK(cs_main)inFetchBlock:if (block_index != nullptr) UninterruptibleSleep(2s);getblockfrompeer <hash> <peer_id>on node 0 for a P2P peer.disconnectnodeon that peer.Functional test used
Before
node 0 aborts (
returncode=-6):Timeline from node 0's debug.log:
Change
The upstream patch does not apply as-is:
FetchBlockhere returnsstd::optional<std::string>and takes(peer_id, hash, block_index). Thechange is the same two hunks adapted to that signature. Lock order
(
cs_main, thenm_peer_mutex) matchesFinalizeNode.After
Same repro with the lock moved up: node 0 keeps running
(
returncode=None) and the RPC returns a clean error:FinalizeNodewaits for the lock instead of racing: the RPC entered at20:03:00.114 and
FinalizeNodefinished at 20:03:02.164.Test results
Tested at ce3641a, with and without this change. Autotools build:
--without-bdb --with-gui=qt5 --enable-wallet --disable-bench.make check: 126test_bitcoinsuites,test_bitcoin-qt, univalue,minisketch, secp256k1 and util tests all passed, before and after.
--extended(314 tests): 247 passed, 63 skipped,4 failed, before and after.
feature_dbcrash.py,feature_pruning.py,feature_index_prune.pyandrpc_getblockfrompeer.pypassed.wallet_multiwallet.py --usecli,wallet_multiwallet.py --descriptors,wallet_listtransactions.py --descriptorsandp2p_dns_seeds.py. Theirerrors are RPC connection failures ("Unable to connect to bitcoind",
"Could not connect to the server ... EOF reached"). Run on their own,
all 4 pass, with and without this change.
Skips:
interface_usdt_*: no permissions to use BPF.feature_bind_port_*: need routable addresses 1.1.1.1 and 2.2.2.2.Caveats
there is no deterministic regression test.