Skip to content

fix: cap inbound message size — one oversized line could exhaust memory - #3

Merged
jkyberneees merged 1 commit into
mainfrom
fix/bounded-request-decode
Aug 21, 2026
Merged

fix: cap inbound message size — one oversized line could exhaust memory#3
jkyberneees merged 1 commit into
mainfrom
fix/bounded-request-decode

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Summary

readMessage accumulated a line without bound (bufio.ErrBufferFull → keep appending), so any client could OOM the server process with a single multi-gigabyte line. In odek this is reachable through odek mcp from any connected MCP client (open finding from odek's 2026-08 security audit).

Fix: Server.MaxRequestBytes caps one inbound message.

  • Default DefaultMaxRequestBytes = 10 MiB (matches what a typical MCP client accepts for a response); 0 selects the default; negative disables (documented as not recommended).
  • An oversized line is answered in-band with -32600 (id null, "exceeds maximum size") per the project rule that bad input never kills the loop.
  • The remainder of the oversized line is discarded in constant memory so the stream stays framed and the next well-formed message is served normally.

Testing

  • TestRunWithIO_RequestSizeCap — oversized line → -32600 with null id, then the following initialize still succeeds (loop survives, framing intact).
  • TestRunWithIO_UnderCapStillServed — default cap admits normal 4 KiB traffic.
  • Full go test ./... green (incl. pipe-based integration + subprocess E2E), go vet clean, gofmt clean.

readMessage accumulated a line without bound (bufio.ErrBufferFull → keep
appending), so a client could OOM the server process with a single
multi-gigabyte line — reachable in odek via `odek mcp` from any connected
MCP client (2026-08 odek audit, open upstream finding).

Server.MaxRequestBytes now caps one inbound message: default
DefaultMaxRequestBytes (10 MiB, matching what a typical client accepts for
a response), zero selects the default, negative disables (not recommended).
An oversized line is answered in-band with -32600 (id null, "exceeds
maximum size"), its remainder discarded in constant memory so the stream
stays framed, and the dispatch loop keeps serving — per the project rule
that bad input never kills the loop.

Regression tests: TestRunWithIO_RequestSizeCap (oversized line rejected +
next message still served), TestRunWithIO_UnderCapStillServed (default cap
admits normal traffic).
@jkyberneees
jkyberneees merged commit c0955f0 into main Aug 21, 2026
6 checks passed
@jkyberneees
jkyberneees deleted the fix/bounded-request-decode branch August 21, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant