fix: evaluate wolfSSL_pending() under cs_hSocket in the recv-drain loop
The bulk-streaming recv-drain loop called wolfSSL_pending(pnode->ssl) after releasing cs_hSocket, racing with SocketSendData (wolfSSL_write) and CloseSocketDisconnect (wolfSSL_free) on the same TLS session -- a data race and potential use-after-free on any TLS peer. Capture the pending-byte count inside the cs_hSocket-locked block and use the captured value for the drain decision. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -593,6 +593,7 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
|
|||||||
int64_t nPassBytes = 0;
|
int64_t nPassBytes = 0;
|
||||||
bool fKeepReading = true;
|
bool fKeepReading = true;
|
||||||
while (fKeepReading) {
|
while (fKeepReading) {
|
||||||
|
int nSSLPending = 0;
|
||||||
if (nRecvBase + nPassBytes > (int64_t)ReceiveFloodSize())
|
if (nRecvBase + nPassBytes > (int64_t)ReceiveFloodSize())
|
||||||
break;
|
break;
|
||||||
{
|
{
|
||||||
@@ -609,6 +610,10 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
|
|||||||
wolfSSL_ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread
|
wolfSSL_ERR_clear_error(); // clear the error queue, otherwise we may be reading an old error that occurred previously in the current thread
|
||||||
nBytes = wolfSSL_read(pnode->ssl, pchBuf, sizeof(pchBuf));
|
nBytes = wolfSSL_read(pnode->ssl, pchBuf, sizeof(pchBuf));
|
||||||
nRet = wolfSSL_get_error(pnode->ssl, nBytes);
|
nRet = wolfSSL_get_error(pnode->ssl, nBytes);
|
||||||
|
// Capture TLS buffered-byte count while still under cs_hSocket; the drain-continuation
|
||||||
|
// check below runs unlocked, so touching pnode->ssl there would race with
|
||||||
|
// SocketSendData / CloseSocketDisconnect (which free ssl) -> data race / use-after-free.
|
||||||
|
nSSLPending = wolfSSL_pending(pnode->ssl);
|
||||||
} else {
|
} else {
|
||||||
nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
|
nBytes = recv(pnode->hSocket, pchBuf, sizeof(pchBuf), MSG_DONTWAIT);
|
||||||
nRet = WSAGetLastError();
|
nRet = WSAGetLastError();
|
||||||
@@ -628,7 +633,7 @@ int TLSManager::threadSocketHandler(CNode* pnode, fd_set& fdsetRecv, fd_set& fds
|
|||||||
// buffer, or TLS has buffered decrypted bytes) and within the per-pass cap.
|
// buffer, or TLS has buffered decrypted bytes) and within the per-pass cap.
|
||||||
// The flood ceiling is enforced pre-read at the top of the loop.
|
// The flood ceiling is enforced pre-read at the top of the loop.
|
||||||
if (fKeepReading) {
|
if (fKeepReading) {
|
||||||
bool fMore = (nBytes == (int)sizeof(pchBuf)) || (bIsSSL && wolfSSL_pending(pnode->ssl) > 0);
|
bool fMore = (nBytes == (int)sizeof(pchBuf)) || (bIsSSL && nSSLPending > 0);
|
||||||
if (!fMore || ++nDrainReads >= MAX_DRAIN_READS)
|
if (!fMore || ++nDrainReads >= MAX_DRAIN_READS)
|
||||||
fKeepReading = false;
|
fKeepReading = false;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user