Skip to content

Comments

Fix reachable Debug.Assert in SubReadStream.Read when seeked past end#124813

Open
Copilot wants to merge 3 commits intomainfrom
copilot/fix-debug-assert-zipfile
Open

Fix reachable Debug.Assert in SubReadStream.Read when seeked past end#124813
Copilot wants to merge 3 commits intomainfrom
copilot/fix-debug-assert-zipfile

Conversation

Copy link
Contributor

Copilot AI commented Feb 24, 2026

SubReadStream (used for uncompressed ZIP entries) allows seeking past the end via Seek(offset, SeekOrigin.End) or Position = value > Length, but all three Read overloads assumed _positionInSuperStream <= _endInSuperStream, computing a negative count and hitting Debug.Assert(count >= 0).

ms.Position = 0;
using var readArchive = new ZipArchive(ms, ZipArchiveMode.Read);
using var readStream = readArchive.Entries[0].Open(); // stored/NoCompression entry
readStream.Seek(1, SeekOrigin.End);                   // position past end
readStream.Read(buffer, 0, buffer.Length);            // ← Debug.Assert fires

Changes

  • Read(byte[], int, int) and Read(Span<byte>) — clamp adjusted count to Math.Max(0L, _endInSuperStream - _positionInSuperStream), returning 0 bytes when position ≥ end
  • ReadAsync(Memory<byte>) — same clamp applied to the buffer slice length
  • TestReadAfterSeekingPastEnd_ReturnsZeroBytes in zip_ReadTests.cs covers both sync and async read paths after seeking past end, with assertions on Position before and after the read to verify it reflects the expected value throughout
Original prompt

This section details on the original issue you should resolve

<issue_title>Reachable Debug.Assert in SubReadStream used from ZipFile</issue_title>
<issue_description>The following unit test produces a Debug.Assert when using a Stream from opening a ZipFile and seeking past the end of the stream:

[Fact]
public void SeekPastEndAssert()
{
    MemoryStream ms = new();

    using (ZipArchive archive = new(ms, ZipArchiveMode.Create, leaveOpen: true)) {
        ZipArchiveEntry entry = archive.CreateEntry("test.txt", CompressionLevel.NoCompression);

        using (Stream stream = entry.Open()) {
            stream.Write("Hello, World!"u8);
        }
    }

    ms.Position = 0;
    using ZipArchive readArchive = new(ms, ZipArchiveMode.Read);
    using var readStream = readArchive.Entries[0].Open();
    readStream.Seek(1, SeekOrigin.End);

    byte[] buffer = new byte[1024];
    readStream.Read(buffer, 0, buffer.Length);
}

Fails with:

Error Message:
      Microsoft.VisualStudio.TestPlatform.TestHost.DebugAssertException : Method Debug.Fail failed with 'count >= 0',
      and was translated to Microsoft.VisualStudio.TestPlatform.TestHost.DebugAssertException to avoid terminating the process hosting the test.
     Stack Trace:
        at Microsoft.VisualStudio.TestPlatform.TestHost.TestHostTraceListener.GetException(String message)
      at Microsoft.VisualStudio.TestPlatform.TestHost.TestHostTraceListener.Fail(String message, String detailMessage)
      at System.Diagnostics.TraceInternal.Fail(String message, String detailMessage) in /source/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs:line 261
      at System.Diagnostics.TraceInternal.TraceProvider.Fail(String message, String detailMessage) in /source/src/libraries/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs:line 17
      at System.Diagnostics.Debug.Fail(String message, String detailMessage) in /source/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs:line 135
      at System.Diagnostics.Debug.Assert(Boolean condition, String message, String detailMessage) in /source/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs:line 99
      at System.Diagnostics.Debug.Assert(Boolean condition, String message) in /source/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Debug.cs:line 88
      at System.IO.Compression.SubReadStream.Read(Byte[] buffer, Int32 offset, Int32 count) in /source/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipCustomStreams.cs:line 339
      at System.IO.Compression.Tests.ZipFile_Create.SeekPastEndAssert() in /source/src/libraries/System.IO.Compression.ZipFile/tests/ZipFile.Create.cs:line 33
      at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
      at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
      at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor) in /source/src/coreclr/System.Private.CoreLib/src/System/RuntimeHandles.cs:line 1165
      at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args) in /source/src/coreclr/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.CoreCLR.cs:line 36
      at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) in /source/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBaseInvoker.cs:line 57
      at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) in /source/src/libraries/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs:line 134
      at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) in /source/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs:line 56

Multiple overloads of Read are likely impacted.</issue_description>

Comments on the Issue (you are @copilot in this section)


🔒 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.

Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix Debug.Assert in SubReadStream for ZipFile Fix reachable Debug.Assert in SubReadStream.Read when seeked past end Feb 24, 2026
@stephentoub stephentoub marked this pull request as ready for review February 24, 2026 20:09
Copilot AI review requested due to automatic review settings February 24, 2026 20:09
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @karelz, @dotnet/area-system-io-compression
See info in area-owners.md if you want to be subscribed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reachable Debug.Assert in SubReadStream used from ZipFile

3 participants