daemon: 16 MiB RPC frame allocated + JSON-parsed BEFORE auth check (unauthenticated local DoS) #10
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
h-dv/code-index#10
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: High · Category: DoS / auth ordering
read_frame(crates/daemon/src/protocol.rs:126) allocates the full body buffer (vec![0u8; len as usize], up toMAX_FRAME_BYTES= 16 MiB) and runsserde_jsonover the entire body, and only afterwards doesserve_connectioncheck the capability token (crates/daemon/src/server.rs:180, token check at:192).The per-frame cap is enforced correctly —
protocol.rs:121rejectslen > MAX_FRAME_BYTESbefore thevec!, so a claimed-4 GiB frame does not OOM. The residual hole is that an unauthenticated local peer can still force a 16 MiB allocation + full JSON parse per connection, with zero credentials. Combined with the lack of a connection cap (see the sibling issue), this is a cheap local memory/CPU exhaustion vector.Fix
Impact / scope
127.0.0.1). Matters on shared / multi-user hosts. Not memory-unsafety, not write-corruption.Filed from a deep multi-agent code review of the workspace (v0.5.7). Severity/category per the review; file:line refs were accurate at review time — verify against current
master.Fixed in
0f4bfe7(branchfix/ultradeep-review-findings).Reassessed on current master: the ordering was still present (token rides inside the request body, so it genuinely can't be checked before parse), but severity had already dropped because
FRAME_BODY_TIMEOUT(30s) bounds the hold. Rather than a protocol change, the fix removes the amplification: requests are now read underMAX_REQUEST_FRAME_BYTES= 1 MiB (protocol.rs), so the buffer an unauthenticated peer can force the daemon to eagerly allocate before the token is seen drops from 16 MiB → 1 MiB (16×).read_frame_cappedrejects an oversized declared length on the 4-byte prefix alone, before any allocation. Sibling connection-cap landed in the same commit (#11).Tests:
protocol::read_frame_capped_rejects_len_over_cap_before_body+auth_e2e::oversized_request_frame_is_rejected_before_body(raw socket declares 4 MiB, sends no body, asserts the server closes within 5s rather than waiting the 30s body timeout).