Skip to content

Guard server receive window against rollover and bound debug client copy#11

Open
sclaiborne wants to merge 1 commit into
fix/server-request-bodyfrom
fix/server-receive-robustness
Open

Guard server receive window against rollover and bound debug client copy#11
sclaiborne wants to merge 1 commit into
fix/server-request-bodyfrom
fix/server-receive-robustness

Conversation

@sclaiborne

Copy link
Copy Markdown
Member

Stacked on #10 (based on fix/server-request-body); will retarget to main once that merges.

Fixes two robustness bugs in src/Ar/LLHttp/HttpServer.c:

Receive window rollover (buffer overflow)

HttpShiftReceivePointer() decremented MaxReceiveLength with no rollover check (the old // TODO: Dont allow rollover). If an accumulated partial request exceeded the receive buffer, MaxReceiveLength wrapped (UDINT) and the TCP layer could overrun the buffer. The function now mirrors the client-side httpShiftReceivePointer() guard: it refuses the shift, and the call site raises LLHTTP_ERR_PACKET_SIZE_TOO_BIG and drops the connection — same policy as the client, which closes the socket on this error.

Heap overread in debug client copy

The end-of-cycle debug copy read sizeof(internal.clients) (5 elements) from pClients, which is TMP_alloc'd with only numClients elements — reading past the allocation every cycle whenever numClients < LLHTTP_MAX_NUM_CLIENTS. The copy is now bounded by min(numClients, LLHTTP_MAX_NUM_CLIENTS) elements.

Both changed functions are file-local, so no .fun/.typ changes.

Tests

Two new tests in test/tests.cpp (run off-PLC via the loupeRT harness, 32-bit MinGW):

  • Oversized request: streams a POST with a 10 MB content-length into a server with a 2000-byte buffer in segments (each capped at the advertised MaxReceiveLength, like the real TCP layer) until the buffer is exhausted; asserts the server errors with LLHTTP_ERR_PACKET_SIZE_TOO_BIG, drops the connection, and the receive window never extends past the allocated buffer.
  • Debug copy bounds: runs the server with numClients = 2, plants a sentinel in internal.clients[2..4], and asserts a cycle mirrors the first 2 elements while leaving the sentinel untouched.

Verified 9/9 tests pass with the fix; reverting only HttpServer.c to the parent commit makes exactly the two new tests fail.

🤖 Generated with Claude Code

The server-side HttpShiftReceivePointer decremented MaxReceiveLength
without a rollover check, so an accumulated request larger than the
receive buffer wrapped the UDINT and let the TCP layer overrun the
buffer. Mirror the client-side guard: refuse the shift, raise
LLHTTP_ERR_PACKET_SIZE_TOO_BIG, and drop the connection.

The end-of-cycle debug copy read sizeof(internal.clients) from
pClients, which is allocated with only numClients elements, reading
past the allocation whenever numClients < LLHTTP_MAX_NUM_CLIENTS.
Bound the copy by both sizes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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