JSON-RPC: bound the per-connection read buffer so unterminated input cannot exhaust memory - #3861
JSON-RPC: bound the per-connection read buffer so unterminated input cannot exhaust memory#3861mcfnord wants to merge 1 commit into
Conversation
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 <noreply@anthropic.com>
|
@mcfnord, pass your LLM this PR and get it to work out why it failed in the build: and work out what to put in the appropriate guidance notes to stop it happening again. |
NO need to prompt the LLM. Very likely just an issue on GitHubs side |
|
MY LLM WROTE: The blocking check was the "Verify C-like coding style" job, conclusion cancelled. The job record shows zero steps executed, no stored log, and inverted timestamps (started_at 2026-08-07 06:38 UTC, completed_at 2026-08-06 16:24 UTC) — it never ran. Re-running it directly needs repo write access, so the PR was closed and reopened to fire a fresh |
MY LLM WROTE:
The JSON-RPC server consumes input only on a complete line —
while ( pSocket->canReadLine() )inCRpcServer::OnNewConnection(src/rpcserver.cpp). Until a newline arrives the received bytes stay in theQTcpSocketread buffer, which is unbounded becausesetReadBufferSize()is never called. A client that connects and sends bytes with no\ngrows that buffer 1:1 with bytes sent, before authentication, until the process is killed by the allocator (uncaughtstd::bad_alloc→ SIGABRT, dropping every connected client).Measured on a non-ASan release build of
main, one connection sending 200 MiB with no newline:mainWell-formed traffic is unaffected on the patched build:
jamulus/apiAuthreturns"result":"ok"andjamulus/getVersionreturns the version, connection stays open.Scope: reachable only when the RPC server is enabled (
--jsonrpcport) and bound off-loopback (--jsonrpcbindip); the default bind is127.0.0.1. This is the denial-of-service classSECURITY.mddocuments as a non-guarantee, so the change hardens a documented limitation rather than closing a promised guarantee — the buffer bounds in a few lines.The change, all in
OnNewConnection:setReadBufferSize ( MAX_JSON_RPC_REQUEST_BYTES )on each new connection, so buffering stops at the bound.MAX_JSON_RPC_REQUEST_BYTES= 64 KiB — far above any real request (secret, method name, or a large batch), and adjustable.A regression test fits the fork's JSON-RPC test surface directly: send an unterminated payload and assert the server's RSS stays flat and the connection is dropped, versus a terminated payload parsed normally.
@dtinth — flagging you as the JSON-RPC author and since this sits in the surface the fork is bringing under test.