Skip to content

Validate uncompressed size up front in ZipArchiveEntry update mode#128319

Open
Copilot wants to merge 6 commits into
mainfrom
copilot/analyze-and-fix-issue-127834
Open

Validate uncompressed size up front in ZipArchiveEntry update mode#128319
Copilot wants to merge 6 commits into
mainfrom
copilot/analyze-and-fix-issue-127834

Conversation

Copilot AI commented May 18, 2026

Copy link
Copy Markdown
Contributor

ZipArchiveEntry.Open throws ArgumentOutOfRangeException from the MemoryStream constructor whenever an entry's declared _uncompressedSize exceeds Array.MaxLength: the unchecked (int) cast in GetUncompressedData wraps negative, and MemoryStream(int capacity) validates capacity is in [0, Array.MaxLength]. The accompanying comment claimed this was safe ("MemoryStream will just grow") — true on .NET Framework, no longer true on .NET Core.

Description

  • ZipArchiveEntry.GetUncompressedData and GetUncompressedDataAsync: both methods now validate _uncompressedSize <= Array.MaxLength directly before constructing the MemoryStream. If the check fails, an InvalidDataException(SR.EntryUncompressedSizeTooLargeForUpdateMode) is thrown immediately — right at the point where the size limit is actually enforced — rather than letting an unchecked (int) cast wrap to negative and surface as a misleading ArgumentOutOfRangeException from the MemoryStream constructor.
  • IsOpenableFinalVerifications: the needToUncompress parameter has been removed. The uncompressed-size check is no longer placed here; only the compressed-size checks remain. This means archive construction (ThrowIfNotOpenable(needToUncompress: false, needToLoadIntoMemory: true)) is entirely unaffected — opening an update-mode archive never fails for entries whose large declared size may never be opened.
  • New resource string SR.EntryUncompressedSizeTooLargeForUpdateMode: "Entries with uncompressed data larger than 2GB are not supported in Update mode." — accurately reflects the Array.MaxLength (~2 GB) ceiling enforced by MemoryStream, replacing the previous reuse of SR.EntryTooLarge ("Entries larger than 4GB...") which was misleading for entries between ~2 GB and 4 GB.
  • Regression test in zip_InvalidParametersAndStrangeFiles.cs: reflectively forces _uncompressedSize > Array.MaxLength and asserts that both Open and OpenAsync throw InvalidDataException immediately in update mode (rather than the previous capacity '-2147483549' ArgumentOutOfRangeException from MemoryStream).
// In GetUncompressedData / GetUncompressedDataAsync — right before new MemoryStream(...)
// MemoryStream is backed by a single byte[] and cannot grow beyond Array.MaxLength.
// Validate up front before attempting the (int) cast.
if ((ulong)_uncompressedSize > (ulong)Array.MaxLength)
    throw new InvalidDataException(SR.EntryUncompressedSizeTooLargeForUpdateMode);

_storedUncompressedData = new MemoryStream((int)_uncompressedSize);

Scope / non-goals

Entries whose declared uncompressed size exceeds Array.MaxLength cannot be loaded in update mode — MemoryStream is backed by a single byte[] and cannot grow beyond Array.MaxLength. Such entries are now rejected when Open()/OpenAsync() is called with a descriptive InvalidDataException("Entries with uncompressed data larger than 2GB are not supported in Update mode.") instead of surfacing as a misleading ArgumentOutOfRangeException about a negative capacity argument. Opening the archive itself is unaffected — entries with large declared sizes that are never opened do not cause failures. Lifting the underlying ceiling requires replacing the in-memory update buffer with a segmented or file-backed stream and is out of scope.

Note

This pull request was authored by GitHub Copilot.

Copilot AI self-assigned this May 18, 2026
Copilot AI review requested due to automatic review settings May 18, 2026 11:18
Copilot AI removed the request for review from Copilot May 18, 2026 11:18
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@alinpahontu2912

Copy link
Copy Markdown
Member

Hey @copilot, I think the fix should be different. We shouldn’t rely on the MemoryStream hitting its max capacity to fail. Instead, we should validate the uncompressedSize up front in ZipArchiveEntry and ensure it falls within the valid range (0 to Array.MaxLength). If it doesn’t, we should throw immediately when opening the entry in update mode.

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/0fbb8331-f2e4-4bd0-acb1-a58139224285

Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot May 18, 2026 13:06

Copilot AI commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

Hey @copilot, I think the fix should be different. We shouldn’t rely on the MemoryStream hitting its max capacity to fail. Instead, we should validate the uncompressedSize up front in ZipArchiveEntr...

Switched to up-front validation in ff62bbb. IsOpenableFinalVerifications now rejects _uncompressedSize > Array.MaxLength with InvalidDataException (using SR.EntryTooLarge) when opening an entry in update mode — covering both sync (Open) and async (OpenAsync) paths via the shared verification helper. The Math.Min clamp in GetUncompressedData[Async] is gone; the (int) cast is now safe because the value is validated upstream. Updated the regression test to assert the throwing behavior on both paths; full System.IO.Compression.Tests suite still passes (1970/1970).

Copilot AI changed the title Clamp MemoryStream capacity hint in ZipArchiveEntry.GetUncompressedData Validate uncompressed size up front in ZipArchiveEntry update mode May 18, 2026
Copilot AI requested a review from alinpahontu2912 May 18, 2026 13:07
@alinpahontu2912 alinpahontu2912 marked this pull request as ready for review June 1, 2026 08:12
Copilot AI review requested due to automatic review settings June 1, 2026 08:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 tightens validation for ZipArchiveEntry when opening entries in Update mode by rejecting entries whose declared uncompressed size can’t be represented by the in-memory MemoryStream buffer used for Update-mode editing, and adds a regression test to cover the exception behavior for both sync and async open paths.

Changes:

  • Add an Update-mode openability check that fails early when _uncompressedSize exceeds Array.MaxLength, returning InvalidDataException instead of allowing an (int) cast to wrap and trigger MemoryStream(int) ArgumentOutOfRangeException.
  • Update comments in sync/async GetUncompressedData* to reflect the new upstream validation.
  • Add a regression test that forces _uncompressedSize > Array.MaxLength and asserts Open() / OpenAsync() throw InvalidDataException.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs Adds upfront Update-mode validation for _uncompressedSize and updates the GetUncompressedData comment accordingly.
src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs Updates the async GetUncompressedDataAsync comment to match the new validation behavior.
src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs Adds a regression test ensuring Update-mode Open/OpenAsync throws InvalidDataException for oversized declared uncompressed sizes.

Comment thread src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs Outdated
Comment thread src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs Outdated
@alinpahontu2912

Copy link
Copy Markdown
Member

@copilot fix these comments

…ss gating

Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>

Copilot AI commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot fix these comments

Fixed in the latest commit. Two changes made:

  1. Accurate error message — added a new SR.EntryUncompressedSizeTooLargeForUpdateMode resource string ("Entries with uncompressed data larger than 2GB are not supported in Update mode.") instead of reusing SR.EntryTooLarge ("4GB"), so the message matches the actual Array.MaxLength limit being enforced.

  2. Narrowed the check scopeIsOpenableFinalVerifications now takes a needToUncompress parameter and the uncompressed-size check is gated on needToUncompress && needToLoadIntoMemory. This means it only fires when Open()/OpenAsync() is called on the entry (where needToUncompress: true), not during archive construction where ThrowIfNotOpenable(needToUncompress: false, needToLoadIntoMemory: true) is called for every entry.

@alinpahontu2912

Copy link
Copy Markdown
Member

@copilot I liked this option better: move the size check to GetUncompressedData/GetUncompressedDataAsync right before constructing the MemoryStream)

…emoryStream construction

Co-authored-by: alinpahontu2912 <56953855+alinpahontu2912@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot June 10, 2026 08:45

Copilot AI commented Jun 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot I liked this option better: move the size check to GetUncompressedData/GetUncompressedDataAsync right before constructing the MemoryStream)

Done in the latest commit. The size check is now in GetUncompressedData and GetUncompressedDataAsync directly before constructing the MemoryStream, and the needToUncompress parameter has been removed from IsOpenableFinalVerifications entirely. All 146 tests in zip_InvalidParametersAndStrangeFiles pass, including the sync and async regression cases.

<data name="EntryTooLarge" xml:space="preserve">
<value>Entries larger than 4GB are not supported in Update mode.</value>
</data>
<data name="EntryUncompressedSizeTooLargeForUpdateMode" xml:space="preserve">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@rzikm since a new error message was added, does it count as breaking change?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we consider change of exception message a breaking change. We only consider it breaking if we change the exception type to something the method never has thrown before (and wasn't documented as a possibility)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 12, 2026 08:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

@alinpahontu2912 alinpahontu2912 enabled auto-merge (squash) June 12, 2026 11:54
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.

6 participants