Skip to content

[Audit-Medium] detached streaming PipeReader leaks when the returned… #4466

@pepone

Description

@pepone

AI-generated audit finding — this issue was opened from an automated security/correctness audit. It has not been triaged by a human yet; verify the reasoning, reproducibility, and severity before acting on it.

Medium: detached streaming PipeReader leaks when the returned IAsyncEnumerable<T> is ignored — CONFIRMED

Affected code:

Verification:

Confirmed. The leak is real and the ownership transfer is explicit.

DetachPayload removes the payload from the owning frame by replacing incoming.Payload with InvalidPipeReader.Instance. After that point, disposing the IncomingRequest, OutgoingRequest, or IncomingResponse no longer reaches the detached reader. Cleanup is delegated entirely to the compiler-generated async-enumerable state machine in PipeReaderExtensions.ToAsyncEnumerable, and that state machine only enters its try/finally after GetAsyncEnumerator() is called.

As a result, a stream payload is cleaned up in these already-tested cases:

  • normal full enumeration,
  • partial enumeration with break,
  • iteration canceled via WithCancellation, and
  • decode failures after enumeration has started.

But if a caller simply receives the IAsyncEnumerable<T> and drops it without ever starting enumeration, reader.Complete() is never reached. This can happen on both sides of the stack:

  • a server handler validates the request and returns early without reading the streamed argument, or
  • a client receives a streamed return value and abandons it before the first await foreach.

Impact:

  • Per-connection resource leak under abandoned streaming payloads.
  • Peer-reachable amplification on the server side when handlers legitimately return early without consuming a streamed argument.

Recommendation:

  • Replace the compiler-generated async IAsyncEnumerable<T> wrapper with a custom enumerable/enumerator type that owns the PipeReader and can complete it deterministically even when iteration never starts.
  • Or keep ownership on the frame side and expose a higher-level abstraction that does not require detaching the raw PipeReader.
  • Add regression tests for both overloads where the returned enumerable is never enumerated and the underlying reader must still be completed.

Status: Valid, Medium severity.


Source report: src-IceRpc.Slice-audit-2026-04-14.md (finding detached streaming PipeReaderleaks when the returnedIAsyncEnumerable is ignored — **CONFIRMED**)

Severity (auditor-assigned): Medium

Metadata

Metadata

Labels

ai-auditAI-generated audit finding — needs human triage

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions