diff --git a/src/libraries/System.IO.Compression/src/Resources/Strings.resx b/src/libraries/System.IO.Compression/src/Resources/Strings.resx
index bbb10afbcf342a..9c121844d89619 100644
--- a/src/libraries/System.IO.Compression/src/Resources/Strings.resx
+++ b/src/libraries/System.IO.Compression/src/Resources/Strings.resx
@@ -212,6 +212,9 @@
Entries larger than 4GB are not supported in Update mode.
+
+ Entries with uncompressed data larger than 2GB are not supported in Update mode.
+
End of Central Directory record could not be found.
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
index a393ea76491e0f..353b1a71e7631d 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.Async.cs
@@ -115,8 +115,14 @@ private async Task GetUncompressedDataAsync(CancellationToken canc
{
// this means we have never opened it before
- // if _uncompressedSize > int.MaxValue, it's still okay, because MemoryStream will just
- // grow as data is copied into it
+ // 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)
+ {
+ _currentlyOpenForWrite = false;
+ throw new InvalidDataException(SR.EntryUncompressedSizeTooLargeForUpdateMode);
+ }
+
_storedUncompressedData = new MemoryStream((int)_uncompressedSize);
if (_originallyInArchive)
diff --git a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
index e84666144c9379..755e88abf67550 100644
--- a/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
+++ b/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs
@@ -481,8 +481,14 @@ private MemoryStream GetUncompressedData()
{
// this means we have never opened it before
- // if _uncompressedSize > int.MaxValue, it's still okay, because MemoryStream will just
- // grow as data is copied into it
+ // 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)
+ {
+ _currentlyOpenForWrite = false;
+ throw new InvalidDataException(SR.EntryUncompressedSizeTooLargeForUpdateMode);
+ }
+
_storedUncompressedData = new MemoryStream((int)_uncompressedSize);
if (_originallyInArchive)
diff --git a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs
index 46d899426459e8..25aa227a0dd63f 100644
--- a/src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs
+++ b/src/libraries/System.IO.Compression/tests/ZipArchive/zip_InvalidParametersAndStrangeFiles.cs
@@ -408,6 +408,47 @@ await Assert.ThrowsAsync(async () =>
await DisposeZipArchive(async, archive);
}
+ [Theory]
+ [MemberData(nameof(Get_Booleans_Data))]
+ public static async Task ZipArchiveEntry_OpenInUpdateMode_UncompressedSizeGreaterThanArrayMaxLength_ThrowsInvalidData(bool async)
+ {
+ // When _uncompressedSize > Array.MaxLength, the entry's uncompressed payload
+ // cannot be loaded into a MemoryStream (which is backed by a single byte[] and
+ // therefore bounded by Array.MaxLength). The entry must be rejected up front
+ // with a descriptive InvalidDataException when opened in Update mode, rather
+ // than failing later from the MemoryStream constructor with a misleading
+ // argument-out-of-range exception caused by the (int) cast wrapping negative
+ // when _uncompressedSize exceeds int.MaxValue.
+ byte[] payload = [0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF];
+ MemoryStream stream = new MemoryStream();
+
+ ZipArchive archive = await CreateZipArchive(async, stream, ZipArchiveMode.Create, leaveOpen: true);
+ ZipArchiveEntry entry = archive.CreateEntry("entry.bin", CompressionLevel.NoCompression);
+ Stream entryStream = await OpenEntryStream(async, entry);
+ await entryStream.WriteAsync(payload);
+ await DisposeStream(async, entryStream);
+ await DisposeZipArchive(async, archive);
+
+ stream.Position = 0;
+ archive = await CreateZipArchive(async, stream, ZipArchiveMode.Update, leaveOpen: true);
+ entry = archive.GetEntry("entry.bin");
+
+ FieldInfo uncompressedSizeField = typeof(ZipArchiveEntry).GetField("_uncompressedSize", BindingFlags.NonPublic | BindingFlags.Instance);
+ Assert.NotNull(uncompressedSizeField);
+ uncompressedSizeField.SetValue(entry, (long)int.MaxValue + 1L);
+
+ if (async)
+ {
+ await Assert.ThrowsAsync(() => entry.OpenAsync());
+ }
+ else
+ {
+ Assert.Throws(() => entry.Open());
+ }
+
+ await DisposeZipArchive(async, archive);
+ }
+
[Theory]
[MemberData(nameof(Get_Booleans_Data))]
public static async Task UnseekableVeryLargeArchive_DataDescriptor_Read_Zip64(bool async)