Fix reachable Debug.Assert in StreamPipeReader.AdvanceTo#123810
Fix reachable Debug.Assert in StreamPipeReader.AdvanceTo#123810
Conversation
Replace Debug.Assert with proper validation that throws InvalidOperationException when consumed position is invalid. This handles the case when a SequencePosition from a different PipeReader is passed to AdvanceTo. Add test for the scenario described in the issue. Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeReader.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
This pull request fixes a critical issue where Debug.Assert(_bufferedBytes >= 0) in StreamPipeReader.AdvanceTo was reachable from public API when passing a SequencePosition from a different PipeReader. Debug assertions should never be reachable from public APIs, even under misuse scenarios.
Changes:
- Replace
Debug.Assertwith runtime validation that throwsInvalidOperationExceptionwhenconsumedBytesis invalid - Add test coverage for the scenario where a position from one reader is passed to another reader's
AdvanceTomethod - Move the
_bufferedBytessubtraction to after validation to prevent state corruption
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeReader.cs | Replaces Debug.Assert with proper runtime validation that checks if consumedBytes is negative or exceeds _bufferedBytes, throwing InvalidOperationException for invalid values |
| src/libraries/System.IO.Pipelines/tests/StreamPipeReaderTests.cs | Adds test case verifying that passing a SequencePosition from a different PipeReader to AdvanceTo correctly throws InvalidOperationException |
🤖 Copilot Code Review — PR #123810Holistic AssessmentMotivation: The PR addresses a real issue where a Approach: The fix validates the computed Summary: ✅ LGTM. The runtime check is lightweight and effective. The test correctly reproduces the cross-reader pollution scenario. A minor unused variable in the test is noted below but is not blocking. Detailed Findings✅ Correctness — Runtime ValidationThe added check
✅ Testing — Regression CoverageThe new test ✅ Consistency — Exception PatternThe fix reuses the existing 💡 Suggestion — Unused Variable in TestThe ReadResult result1 = await reader1.ReadAsync(); // assigned but unused
ReadResult result2 = await reader2.ReadAsync();While the _ = await reader1.ReadAsync(); // ensures reader1 has data
ReadResult result2 = await reader2.ReadAsync();This is purely cosmetic and may generate a compiler warning (CS8600/IDE0059) depending on project settings. Models contributing to this review: Claude Sonnet 4, Gemini 3 Pro (Preview) |
Description
Debug.Assert(_bufferedBytes >= 0)inStreamPipeReader.AdvanceTois reachable from public API when passing aSequencePositionfrom a differentPipeReader. Debug assertions should never be reachable from public APIs, even under misuse.Pre-change behavior in release builds: The
Debug.Assertwas stripped out, allowing the code to continue with corrupted state._bufferedByteswould become negative after subtracting an invalidconsumedBytesvalue calculated from segments with unrelatedRunningIndexvalues. This led to the reader having an invalid_readHeadpointing to another reader's segment, negative_bufferedBytes, and potentially leaked/corrupted buffer segments.Changes:
Debug.Assertwith validation that throwsInvalidOperationExceptionwhenconsumedBytesis negative or exceeds_bufferedBytesAdvanceWithPositionFromDifferentReaderThrowscovering the reported scenarioNote: This fix validates the symptom (invalid
consumedBytesvalues) rather than explicitly checking segment ownership, which would add O(n) overhead. ThePipeclass has a similarDebug.Assert(_unconsumedBytes >= 0)pattern that may warrant similar treatment.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.