upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/bitflashd.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-08-11 21:52:45 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-19 22:48:27 +0000
commiteb2e32e14b67d0eaad17cfa7086d8d7ebaf7f00f (patch)
tree453af5925d5722cb7b91aefc45feff8491bacbf7 /src/bitflashd.cpp
parente236d013e973fd00cbdbed4bea1e5f373c0d1236 (diff)
net: stop closing a descriptor number that now belongs to somebody else
THE WEDGE. CNode::Disconnect() closed hSocket and left the member holding the stale number. ~CNode's only guard is 'hSocket != INVALID_SOCKET' and nothing in the tree ever assigned that sentinel, so the destructor closed the same number a second time. The two closes are five minutes apart by design: ThreadSocketHandler2 disconnects a node, sets nReleaseTime to GetTime() + 5*60, and parks it in vNodesDisconnected; GetRefCount() reports it busy until that expires, so 'delete pnode' -- and the second close -- cannot run before then. Linux hands out the lowest free descriptor. Measured on the pool: 202 open descriptors with a high-water mark of 216, so a freed number is reissued within seconds -- to a Nostr relay TLS socket, a rendezvous socket, a tunnel end, or a miner connection. Five minutes later the destructor closes it. The victim gets no error and no log line. It simply stops. That is the whole signature, including the part that never made sense: .btf block delivery and Nostr descriptor lookup share no lock, no queue and no thread, so they should not fail together. They share the file descriptor table, and a stray close is drawn from all of it. It also explains why #78 did not help and may have hurt. #78 drops stuck nodes -- it fired 41 times in one window -- and every drop is another Disconnect(), which is another delayed close armed against an innocent socket. Second bug, same symptom, different subsystem: the pooled relay sockets. ResolveDescriptor returned the instant a descriptor verified, leaving the relay's EOSE unread on a socket that goes straight back into g_relayPool, so the next caller read our leftovers as its own answer -- permanently one message out of step, and no code path anywhere inspected the subscription id that would have caught it. Worse, BtfResolve set lease.fOk = true on every failure path including a dead RecvText, labelling it 'exchange completed, just no match'. fOk = false is the only thing that ever closes a pooled socket or clears fConnected, so a relay connection that died was pinned in the pool, reported healthy, and reused for the life of the process. That is 'no Nostr descriptor found' for a descriptor that nak can see, cleared only by a restart. Now: read to EOSE before returning, match the subscription id, and keep a pooled socket only when the relay actually finished the conversation. (cherry picked from commit ed4640c306cc46dbdbd92cb6dc55e7528651269c)
Diffstat (limited to 'src/bitflashd.cpp')
0 files changed, 0 insertions, 0 deletions