diff options
| author | Bitflash <btf01@proton.me> | 2026-08-01 21:31:58 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-01 21:31:58 -0300 |
| commit | fc071066c9de259795a210e449b364ac126416ad (patch) | |
| tree | e9f9b98f12f9eae51ed7f91b82036bf53207429b /src/net.cpp | |
| parent | 5358e2396ca6cb371400f685181ef420dcf12382 (diff) | |
net: stop closing a peer's socket twice (#103)
CNode::Disconnect() closed the socket and left the handle number in the object; ~CNode() then closed it again with a raw closesocket() the accounting never saw. There is no assignment of INVALID_SOCKET to CNode::hSocket anywhere in this tree, so the destructor's guard never fired. Between the two closes the OS can reissue that number, and the second close then takes down somebody else's connection. The same omission left a closed handle in the select() set, which fails every iteration and drives the recovery path that prints 'dropping node: its socket is no longer valid'. Measured A/B on the real network at matched socket volume: unpatched 552 sockets, 4 closes failing with 10038, 741 invalid-socket drops (all one peer), 11 peers held; patched 547 sockets, 0, 0, and 21 peers held. No protocol change.
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/src/net.cpp b/src/net.cpp index 3af0189..7091981 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -1030,7 +1030,12 @@ void CNode::Disconnect() | |||
| 1030 | { | 1030 | { |
| 1031 | LogPrint("net", "disconnecting node %s\n", addr.ToString().c_str()); | 1031 | LogPrint("net", "disconnecting node %s\n", addr.ToString().c_str()); |
| 1032 | 1032 | ||
| 1033 | // Invalidate before anything else can look at it. This used to close the | ||
| 1034 | // socket and leave the handle number sitting in the object, so ~CNode | ||
| 1035 | // closed it a second time and the select loop kept arming it in the | ||
| 1036 | // meantime. See the note on ~CNode in net.h. | ||
| 1033 | BtfCloseSocket(hSocket); | 1037 | BtfCloseSocket(hSocket); |
| 1038 | hSocket = INVALID_SOCKET; | ||
| 1034 | 1039 | ||
| 1035 | // All of a nodes broadcasts and subscriptions are automatically torn down | 1040 | // All of a nodes broadcasts and subscriptions are automatically torn down |
| 1036 | // when it goes down, so a node has to stay up to keep its broadcast going. | 1041 | // when it goes down, so a node has to stay up to keep its broadcast going. |
| @@ -1210,6 +1215,11 @@ void ThreadSocketHandler2(void* parg) | |||
| 1210 | { | 1215 | { |
| 1211 | if (nWatched >= FD_SETSIZE) | 1216 | if (nWatched >= FD_SETSIZE) |
| 1212 | break; | 1217 | break; |
| 1218 | // A disconnected node stays in vNodes until its references go. | ||
| 1219 | // Arming its closed handle asks select() to watch a number the | ||
| 1220 | // operating system may already have given to somebody else. | ||
| 1221 | if (pnode->hSocket == INVALID_SOCKET) | ||
| 1222 | continue; | ||
| 1213 | FD_SET(pnode->hSocket, &fdsetRecv); | 1223 | FD_SET(pnode->hSocket, &fdsetRecv); |
| 1214 | nWatched++; | 1224 | nWatched++; |
| 1215 | hSocketMax = max(hSocketMax, pnode->hSocket); | 1225 | hSocketMax = max(hSocketMax, pnode->hSocket); |
| @@ -1272,6 +1282,11 @@ void ThreadSocketHandler2(void* parg) | |||
| 1272 | { | 1282 | { |
| 1273 | foreach(CNode* pnode, vNodes) | 1283 | foreach(CNode* pnode, vNodes) |
| 1274 | { | 1284 | { |
| 1285 | // Already disconnected: its handle is gone on purpose, and | ||
| 1286 | // reporting it here would name the wrong node for a select() | ||
| 1287 | // failure it did not cause. | ||
| 1288 | if (pnode->hSocket == INVALID_SOCKET) | ||
| 1289 | continue; | ||
| 1275 | int nType = 0; | 1290 | int nType = 0; |
| 1276 | #ifdef _WIN32 | 1291 | #ifdef _WIN32 |
| 1277 | int nTypeLen = sizeof(nType); | 1292 | int nTypeLen = sizeof(nType); |