CVE-2026-115907: Denial of Service via Excessive Memory Pre-allocation in Rust zip Crate Legacy Decoders

Vulnerability ID: CVE-2026-115907
CVSS Score: 7.5
Published: 2026-10-29

CVE-2026-115907 is an uncontrolled resource consumption vulnerability in the Rust zip crate (versions 5.1.0 through 8.6.0, and 9.0.0 pre-releases) when compiled with the optional legacy-zip feature flag. The vulnerability resides in the legacy decompression decoders—Shrink, Reduce, and Implode—which directly pass an archive-declared 64-bit uncompressed size from a ZIP64 extended information extra field to Vec::reserve() before validating the compressed data stream. By supplying a malformed ZIP archive with a declared uncompressed size exceeding isize::MAX (such as u64::MAX), an unauthenticated attacker can trigger an unhandled capacity overflow panic in the Rust standard library allocator, terminating the calling thread or aborting the entire process.

TL;DR

Applications using the Rust zip crate (>= 5.1.0, < 9.0.0) with the legacy-zip feature enabled are vulnerable to a denial-of-service condition. Passing an attacker-controlled ZIP64 uncompressed size directly to Vec::reserve() in legacy Shrink, Reduce, and Implode decoders triggers a capacity overflow panic. Upgrading to version 9.0.0 resolves the issue by clamping speculative pre-allocations to 1 MiB.

⚠️ Exploit Status: POC

Technical Details

  • CVE / Advisory ID: CVE-2026-115907 / GHSA-9hj8-73fh-9963
  • Weakness Class: CWE-789 (Excessive Memory Allocation) / CWE-400 (Resource Consumption)
  • Attack Vector: Network (Crafted 149-byte ZIP64 Archive)
  • CVSS v3.1 Score: 7.5 HIGH (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
  • EPSS Score: 0.35% (28.63rd Percentile)
  • Primary Impact: Denial of Service via Unhandled Capacity Overflow Panic
  • Exploit Status: Proof-of-Concept Available (Regression Test)
  • CISA KEV Status: Not Listed

Affected Systems

  • Rust zip crate (zip-rs/zip2) with the legacy-zip feature enabled
  • zip (Rust crate): >= 5.1.0, < 9.0.0 (with legacy-zip feature enabled) (Fixed in: 9.0.0)

Code Analysis

Commit: 8b49912

fix: don't pre-allocate the declared size in the legacy decoders (#991)

--- a/src/legacy/mod.rs +++ b/src/legacy/mod.rs @@ -1,4 +1,12 @@ mod huffman; + +/// Upper bound on the speculative pre-allocation made from an entry's declared +/// uncompressed size. That size comes from the archive, so it is not a fact: +/// reserving it outright panics with "capacity overflow" past `isize::MAX` and +/// aborts on allocation failure below it. Entries larger than this still decode +/// in full; the buffer just grows geometrically past the cap instead of in one step. +pub(crate) const MAX_PREALLOC: usize = 1 << 20; +  pub(crate) mod implode; pub(crate) mod reduce; pub(crate) mod shrink;  --- a/src/legacy/implode.rs +++ b/src/legacy/implode.rs @@ -94,7 +94,7 @@ fn hwexplode( dst: &mut Vec<u8>, ) -> std::io::Result<()> { // Pre-allocate capacity - dst.reserve(uncomp_len); + dst.reserve(uncomp_len.min(crate::legacy::MAX_PREALLOC));   let bit_length = src.len() as u64 * 8; let mut is = BitReader::endian(Cursor::new(&src), LittleEndian); @@ -211,7 +211,8 @@ impl<R: Read> Read for ImplodeDecoder<R> { self.compressed_reader.read_to_end(&mut compressed_bytes)?;   // Pre-allocate stream buffer - self.stream.reserve(self.uncompressed_size as usize); + self.stream + .reserve((self.uncompressed_size as usize).min(crate::legacy::MAX_PREALLOC));   hwexplode( &compressed_bytes,  --- a/src/legacy/reduce.rs +++ b/src/legacy/reduce.rs @@ -104,7 +104,7 @@ fn hwexpand(src: &[u8], uncomp_len: usize, comp_factor: u8, dst: &mut Vec<u8>) - debug_assert!((1..=4).contains(&comp_factor));   // Pre-allocate to avoid reallocations - dst.reserve(uncomp_len); + dst.reserve(uncomp_len.min(crate::legacy::MAX_PREALLOC));   let mut is = BitReader::endian(src, LittleEndian); let mut fsets = read_follower_sets(&mut is)?;  --- a/src/legacy/shrink.rs +++ b/src/legacy/shrink.rs @@ -231,7 +231,7 @@ fn output_code( }   fn hwunshrink(src: &[u8], uncompressed_size: usize, dst: &mut Vec<u8>) -> io::Result<()> { - dst.reserve(uncompressed_size); + dst.reserve(uncompressed_size.min(crate::legacy::MAX_PREALLOC));  let mut codetab = Codetab::create_new(); let mut queue = CodeQueue::new(); let mut is = BitReader::endian(src, LittleEndian); @@ -344,7 +344,8 @@ impl<R: Read> Read for ShrinkDecoder<R> { self.stream_read = true; let mut compressed_bytes = Vec::new(); self.compressed_reader.read_to_end(&mut compressed_bytes)?; - self.stream.reserve(self.uncompressed_size as usize); + self.stream + .reserve((self.uncompressed_size as usize).min(crate::legacy::MAX_PREALLOC));  hwunshrink( &compressed_bytes, self.uncompressed_size as usize, 

Exploit Details

  • GitHub: Official regression test (tests/legacy_oversized_declared_size.rs) included in the fix commit demonstrating a minimal PoC generator for Shrink, Reduce, and Implode methods.

Mitigation Strategies

  • Upgrade the Rust zip crate to version 9.0.0 or later to enforce the 1 MiB cap on speculative vector reservations in legacy decoders.
  • Disable the optional legacy-zip Cargo feature if support for PKWARE Shrink, Reduce, and Implode algorithms is not required.
  • Implement pre-extraction metadata validation using ZipArchive::by_index_raw() to reject entries declaring excessive uncompressed sizes or utilizing legacy compression methods.
  • Enforce bounded reads using std::io::Read::take() when decompressing streams from untrusted archives to defend against both header manipulation and actual decompression bombs.

Remediation Steps:

  1. Audit project dependencies using 'cargo tree -i zip -e features' or 'cargo audit' to verify if zip (>= 5.1.0, < 9.0.0) is compiled with the 'legacy-zip' feature flag.
  2. Modify Cargo.toml to specify zip = ">=9.0.0" (or remove the 'legacy-zip' feature flag from existing version specifications).
  3. Run 'cargo update -p zip' and rebuild the application binary.
  4. Execute regression tests verifying that archives with ZIP64 uncompressed sizes set to u64::MAX return an io::Error rather than triggering a thread panic.

References

Read the full report for CVE-2026-115907 on our website for more details including interactive diagrams and full exploit analysis.