dotnet / dotnet/runtime

Zip64 Data Descriptor Bug on Non-Seekable Streams

Open
#127,389 2 comments 0 reactions 0 assignees View on GitHub
area-System.IO.Compression
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

When writing a ZIP archive to a non-seekable stream, entries that exceed 4GB produce a usable, but malformed archive. The local file header contains no Zip64 signals, but the data descriptor uses 8-byte (Zip64) size fields.

Root Cause
In ZipArchiveEntry.WriteLocalFileHeaderInitialize, the non-seekable branch writes the local header before compression, when sizes are unknown:

https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs#L1088-L1096

This approach:
Sets sizes to 0 (not 0xFFFFFFFF)
Does not call VersionToExtractAtLeast(ZipVersionNeededValues.Zip64)
Does not write a Zip64 extra field
Later, after compression finishes, PrepareToWriteDataDescriptor checks actual sizes:

https://github.com/dotnet/runtime/blob/main/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchiveEntry.cs#L1448C12-L1454C14
If the entry exceeds 4GB, the data descriptor uses 8-byte fields, but the local header (already written to a non-seekable stream) gave no indication of this.

Impact
Any reader that relies on the local header to determine data descriptor format will misparse the archive, corrupting all subsequent entries. However, most commonly zip files rely on the Central Directory to read sizes, so the true impact is probably low, eben though the local header is not correctly created.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.