From c1e617519fbe1a3cda9bbc08f178eade8f410653 Mon Sep 17 00:00:00 2001 From: jrd Date: Thu, 6 Aug 2026 16:09:22 +0000 Subject: [PATCH] JSON-RPC: bound the per-connection read buffer An unauthenticated client that sends no newline grows the QTcpSocket read buffer without bound (setReadBufferSize is never called), before auth, until std::bad_alloc aborts the process and drops all clients. Bound the buffer to 64 KiB per connection and drop a connection whose buffer fills with no complete line. Co-Authored-By: Claude Opus 4.8 --- src/rpcserver.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 2c05c2f12d..0356953b59 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -110,6 +110,13 @@ QJsonObject CRpcServer::CreateJsonRpcErrorReply ( int code, QString message ) return object; } +// Maximum size of a single JSON-RPC request line. An unauthenticated client that +// sends data without a terminating newline is only ever consumed on a complete line +// (canReadLine()), so without a bound the received bytes accumulate in the socket read +// buffer without limit until the process is killed by the allocator. Requests larger +// than this, or unterminated data that fills the buffer, are rejected instead of held. +static constexpr int MAX_JSON_RPC_REQUEST_BYTES = 64 * 1024; + void CRpcServer::OnNewConnection() { QTcpSocket* pSocket = pTransportServer->nextPendingConnection(); @@ -122,6 +129,9 @@ void CRpcServer::OnNewConnection() vecClients.append ( pSocket ); isAuthenticated[pSocket] = false; + // Bound the per-connection read buffer so unterminated input cannot exhaust memory. + pSocket->setReadBufferSize ( MAX_JSON_RPC_REQUEST_BYTES ); + connect ( pSocket, &QTcpSocket::disconnected, [this, pSocket]() { qDebug() << "- JSON-RPC: connection from:" << pSocket->peerAddress().toString() << "closed"; vecClients.removeAll ( pSocket ); @@ -197,6 +207,14 @@ void CRpcServer::OnNewConnection() pSocket->disconnectFromHost(); return; } + + // A full buffer with no complete line is an oversized or unterminated request: + // reject and close rather than hold the bytes indefinitely. + if ( !pSocket->canReadLine() && pSocket->bytesAvailable() >= MAX_JSON_RPC_REQUEST_BYTES ) + { + Send ( pSocket, QJsonDocument ( CreateJsonRpcErrorReply ( iErrParseError, "Parse error: Request exceeds maximum size" ) ) ); + pSocket->disconnectFromHost(); + } } ); }