Skip to content

Parser view-lifetime UAF fix, quiet idle close, framing/soak gates - #11

Merged
AgentDudu merged 1 commit into
developfrom
feature/hardening
Oct 20, 2026
Merged

AgentDudu merged 1 commit into
developfrom
feature/hardening

Conversation

@AgentDudu

Copy link
Copy Markdown
Owner

Product bugs fixed

  • heap-use-after-free (ASan-proven): Request fields were string_views into the parser's working buffer; a later feed() that reallocated it left handler/keepAlive() reading freed memory. Repro path: byte-at-a-time feeds (slow clients). Request now owns its bytes (copied at completion).
  • Idle keep-alive got 408: after ≥1 served request, an idle stream is now closed quietly per RFC 9112; 408 remains for stalled requests (fresh-connection 408 test still passes).

Hardening coverage added

  • tests/test_smuggling.cpp — server-level framing corpus (CL+TE, TE variants, dup/signed/garbage CL, bare-LF, NUL-in-value, obs-fold, poison-behind-valid): exact status trace + the stream is dead after a rejection + rejected requests never reach the handler. Slow-body: 408 past budget, served inside budget.
  • tests/test_threads.cpp — restored multi-reactor suite (keep-alive×4 reactors, stop-drain from every reactor, cross-reactor pipelined storms, idle-poll stop). Now genuinely executed by the TSan job.
  • tests/test_soak.cpp — HTTP_SERVER_SOAK_SECONDS-gated 60 s churn soak (short/keep-alive/slow-read/abort/pipelined clients, 24 workers), RSS sampled via /proc. Measured: 109 110 iterations, 0 errors, 15.6→17.9 MB (2.3 MB drift). CI: new linux-soak job.

Honest audit note

A commit --amend chain earlier in this project silently dropped test_connection.cpp, test_threads.cpp, test_util.cpp, test_range.cpp and an http::Range extraction from their commits — they were never on GitHub, so every CI run since executed a smaller suite than reported (functionality itself was committed and is green). The threading suite is restored here; range/date coverage was verified to live inside the committed test_static.cpp/test_date.cpp. Details recorded in PLAN.md.

Verified locally: MinGW ck.ps1; Docker ASan (4 full runs clean incl. SIGINT smoke); Docker TSan (with the restored threading suite); 60 s soak in-container.

Request::method/target/version/body were string_views into the parser's
working buffer: any feed() that reallocated it after the request line was
parsed left the handler and keepAlive() reading freed memory (caught by
ASan in the new suites). The parser now copies those fields when a request
completes, so a Request owns its bytes.

Idle keep-alive connections that finished their last request are now closed
quietly (RFC 9112); 408 stays reserved for stalled requests.

Adds tests/test_smuggling.cpp (server-level framing corpus incl. slow-body
budgets), tests/test_threads.cpp (restored multi-reactor coverage, TSan),
tests/test_soak.cpp (env-gated 60 s churn soak, RSS drift < 16 MiB: 109k
iterations, 0 errors) plus a linux-soak CI job. .gitignore now ignores all
build* trees.
@AgentDudu
AgentDudu merged commit 6fbdefb into develop Oct 20, 2026
4 checks passed
@AgentDudu
AgentDudu deleted the feature/hardening branch October 20, 2026 18:34
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