Fix reachable Debug.Assert in SubReadStream.Read when seeked past end#124813
Fix reachable Debug.Assert in SubReadStream.Read when seeked past end#124813
Conversation
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a reachable Debug.Assert failure in SubReadStream when reading after seeking past the end of the stream. SubReadStream is used internally for uncompressed ZIP archive entries. The issue occurred because the stream allows seeking past the end (via Seek(offset, SeekOrigin.End) or setting Position to a value greater than Length), but all three Read overloads assumed the position would never exceed the end position, leading to negative count calculations that triggered Debug.Assert(count >= 0).
Changes:
- Fixed all three Read methods in SubReadStream to clamp the count to zero when positioned past the end
- Added comprehensive test coverage for both synchronous and asynchronous read paths
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs | Fixed Read(byte[], int, int), Read(Span<byte>), and ReadAsync(Memory<byte>) to clamp count using Math.Max(0L, _endInSuperStream - _positionInSuperStream) when position is past the end |
| src/libraries/System.IO.Compression/tests/ZipArchive/zip_ReadTests.cs | Added ReadAfterSeekingPastEnd_ReturnsZeroBytes test covering both sync and async read operations after seeking past the end of a stored ZIP entry |
…End test Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
|
Tagging subscribers to this area: @karelz, @dotnet/area-system-io-compression |
SubReadStream(used for uncompressed ZIP entries) allows seeking past the end viaSeek(offset, SeekOrigin.End)orPosition = value > Length, but all threeReadoverloads assumed_positionInSuperStream <= _endInSuperStream, computing a negative count and hittingDebug.Assert(count >= 0).Changes
Read(byte[], int, int)andRead(Span<byte>)— clamp adjusted count toMath.Max(0L, _endInSuperStream - _positionInSuperStream), returning 0 bytes when position ≥ endReadAsync(Memory<byte>)— same clamp applied to the buffer slice lengthReadAfterSeekingPastEnd_ReturnsZeroBytesinzip_ReadTests.cscovers both sync and async read paths after seeking past end, with assertions onPositionbefore and after the read to verify it reflects the expected value throughoutOriginal prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.