diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-02 07:09:45 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-02 07:09:45 +0000 |
| commit | 0b566b4869b9e86c90162befd87f9882b6dd143c (patch) | |
| tree | 978c6a3d0ecf729fa72ab38468280d195843996a /src/net.cpp | |
| parent | a44c9402c20493f0964a1296dd38d313216fbd4d (diff) | |
net: a refused thread spawn drops one peer, not the node
std::thread throws std::system_error when pthread_create is refused
(RLIMIT_NPROC, a container's pids cgroup, memory pressure), and nothing
caught it, so the node died by std::terminate. Threads are spawned per
peer connection, so the pressure that triggers this scales with peer
count, which is attacker-influenced. Observed killing a healthy
participant worker mid-mining.
Every spawn site in the node build, and what a failure now does:
- btftunnel.cpp: both tunnel pumps are guarded. The inbound pump starts
first because it owns cleanup of the pump end and rendezvous socket;
if the second spawn is refused, shutdown() wakes the first out of its
read and it cleans up itself -- closing those descriptors from the
failure path instead would race it onto reused descriptors. Both
paths return INVALID_SOCKET, which every caller already treats as
"this connection failed".
- btfrv.cpp: the relay's accept loop drops the connection it cannot
serve and stops accepting for a moment -- longer after consecutive
failures, capped at 5s -- instead of dying at an arbitrary one. The
per-pair second pump now runs on the handling thread itself, leaving
exactly one spawn to guard and nothing running yet to race.
- main.cpp: a participant that cannot spawn its hash workers stops the
ones that did start, drops the pool connection, and returns to the
reconnect loop, the same way an ordinary disconnect does.
- randomx_pow.cpp: dataset init joins the workers it got, releases the
dataset, and stays in light mode -- the same trade the allocation
failure path above it already makes.
- rpc.cpp, net.cpp: the remaining unchecked _beginthread calls (which
report failure by value, they cannot throw) now say precisely what
did not start. The pool's accept and payout threads failing silently
left the pool running deaf -- nobody accepts, nobody gets paid, and
nothing in the log says why.
Left alone deliberately: the per-relay accept spawns (rpc.cpp) already
check, log and retry on the next poll; startup-critical spawns in
net.cpp keep failing hard; the selftest-only std::thread uses may abort
a selftest run, which is acceptable where dying over one peer is not.
Verified: full node rebuilds clean and links; pool-stratum selftest
passes; a scratch harness under RLIMIT_NPROC=1 confirms _beginthread
reports -1 and std::thread throws system_error, the premises of every
path above.
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 5 |
1 files changed, 3 insertions, 2 deletions
diff --git a/src/net.cpp b/src/net.cpp index fed6123..16038c8 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -2090,7 +2090,7 @@ bool StartNode(string& strError) | |||
| 2090 | if (addrIncoming.ip) | 2090 | if (addrIncoming.ip) |
| 2091 | addrLocalHost.ip = addrIncoming.ip; | 2091 | addrLocalHost.ip = addrIncoming.ip; |
| 2092 | 2092 | ||
| 2093 | _beginthread([](void*) { | 2093 | if (_beginthread([](void*) { |
| 2094 | unsigned int ip = addrLocalHost.ip; | 2094 | unsigned int ip = addrLocalHost.ip; |
| 2095 | if (GetMyExternalIP(ip)) { | 2095 | if (GetMyExternalIP(ip)) { |
| 2096 | addrLocalHost.ip = ip; | 2096 | addrLocalHost.ip = ip; |
| @@ -2098,7 +2098,8 @@ bool StartNode(string& strError) | |||
| 2098 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); | 2098 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); |
| 2099 | printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); | 2099 | printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); |
| 2100 | } | 2100 | } |
| 2101 | }, 0, NULL); | 2101 | }, 0, NULL) == (uintptr_t)-1) |
| 2102 | printf("Error: _beginthread(GetMyExternalIP) failed\n"); | ||
| 2102 | 2103 | ||
| 2103 | // Peers that answered last time, dialled straight away. The relays are the | 2104 | // Peers that answered last time, dialled straight away. The relays are the |
| 2104 | // only way to find anyone otherwise, and reaching them plus walking their | 2105 | // only way to find anyone otherwise, and reaching them plus walking their |