CVE-2026-115909
zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.5
zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
- Record
CVE-2026-115909- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
EUVDEUVD-2026-109987
7.5
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
- Record
EUVD-2026-109987- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-q7v9-x93c-xqv3
7.5
zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction
Summary
The symbolic-link branch of ZipArchive::extract_internal() allocates a buffer using an attacker-declared size before reading any entry data. A 112-byte archive terminates the extracting process with SIGABRT through this infallible allocation.
Details
#[cfg(any(unix, windows))]
if file.is_symlink() {
let mut target = Vec::with_capacity(file.size() as usize); // @audit unbounded memory allocation
file.read_to_end(&mut target)?;
drop(file);
make_symlink(&outpath, &target, &self.shared.files)?;
continue;
}
src/read/mod.rs#L399-L405 (reviewed commit 6f09b306); the same code is at src/read.rs#L417-L418 in released v8.6.0.
Both inputs are attacker-controlled:
ZipFile::size()returnsuncompressed_sizeverbatim from the central directory record. A ZIP64 extended-information extra field widens it to a fullu64(src/read/mod.rs#L619-L625). It is never cross-checked against the compressed size, the remaining archive length, or any platform-appropriate bound for a symlink target.ZipFile::is_symlink()is decided entirely by the central directory's external-attributes field, via theS_IFLNKbit.
Memory allocation failure → uncatchable process abort. The reservation is sized from the declared uncompressed_size before a single byte of the entry is read. Vec::with_capacity() is infallible: on allocator refusal it calls handle_alloc_error(), which aborts the process. This is not a Rust panic — it is not caught by catch_unwind(), is unaffected by panic = "unwind", and is not contained by isolating the call in a worker thread. This was verified by wrapping the call in all three at once; the process still died with exit 134.
The declared value needed is a property of the host's address space, not of reachability. On a 64 GB host with heuristic overcommit, declared sizes from roughly 2^38 upward abort reliably. Sizes at or above 2^63 instead trip Vec's internal capacity check and produce a catchable capacity overflow panic, so an attacker simply avoids that band. The reproducer declares 2^62, which exceeds the 47-bit user address space of x86-64 Linux outright and therefore aborts deterministically regardless of installed RAM or overcommit policy. Under a ulimit -v cap or a memory-constrained container the threshold drops far enough that a plain 32-bit uncompressed_size with no ZIP64 field suffices — a virtual-memory limit lowers the bar rather than mitigating the defect.
This is process termination, not memory exhaustion: Vec::with_capacity() only maps address space and never touches pages, so resident memory stays flat and the abort happens on the refused reservation itself, before any byte is read.
PoC
The reproducer consists of the following zip harness and payload. It requires only the zip crate; use a standard Base64 decoder to materialise the archive before running it.
[package]
name = "zip-poc"
version = "0.1.0"
edition = "2021"
[dependencies]
zip = "8.6.0"
use std::{env, fs::File, io::BufReader};
fn main() {
let archive_path = env::args().nth(1).expect("usage: poc <archive.zip>");
let outdir = std::env::temp_dir().join("zip-poc");
std::fs::create_dir_all(&outdir).unwrap();
let file = File::open(archive_path).unwrap();
let mut archive = zip::ZipArchive::new(BufReader::new(file)).unwrap();
let r = archive.extract(&outdir); // <-- process dies here
…
- Record
GHSA-q7v9-x93c-xqv3- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
NVD
7.5
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
- Record
CVE-2026-115909- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
OSV
7.5
zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
- Record
CVE-2026-115909- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
OSVDEBIAN-CVE-2026-115909
7.5
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
- Record
DEBIAN-CVE-2026-115909- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
Show 7 moreShow less
OSVGHSA-q7v9-x93c-xqv3
7.5
zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction
Summary
The symbolic-link branch of ZipArchive::extract_internal() allocates a buffer using an attacker-declared size before reading any entry data. A 112-byte archive terminates the extracting process with SIGABRT through this infallible allocation.
Details
#[cfg(any(unix, windows))]
if file.is_symlink() {
let mut target = Vec::with_capacity(file.size() as usize); // @audit unbounded memory allocation
file.read_to_end(&mut target)?;
drop(file);
make_symlink(&outpath, &target, &self.shared.files)?;
continue;
}
src/read/mod.rs#L399-L405 (reviewed commit 6f09b306); the same code is at src/read.rs#L417-L418 in released v8.6.0.
Both inputs are attacker-controlled:
ZipFile::size()returnsuncompressed_sizeverbatim from the central directory record. A ZIP64 extended-information extra field widens it to a fullu64(src/read/mod.rs#L619-L625). It is never cross-checked against the compressed size, the remaining archive length, or any platform-appropriate bound for a symlink target.ZipFile::is_symlink()is decided entirely by the central directory's external-attributes field, via theS_IFLNKbit.
Memory allocation failure → uncatchable process abort. The reservation is sized from the declared uncompressed_size before a single byte of the entry is read. Vec::with_capacity() is infallible: on allocator refusal it calls handle_alloc_error(), which aborts the process. This is not a Rust panic — it is not caught by catch_unwind(), is unaffected by panic = "unwind", and is not contained by isolating the call in a worker thread. This was verified by wrapping the call in all three at once; the process still died with exit 134.
The declared value needed is a property of the host's address space, not of reachability. On a 64 GB host with heuristic overcommit, declared sizes from roughly 2^38 upward abort reliably. Sizes at or above 2^63 instead trip Vec's internal capacity check and produce a catchable capacity overflow panic, so an attacker simply avoids that band. The reproducer declares 2^62, which exceeds the 47-bit user address space of x86-64 Linux outright and therefore aborts deterministically regardless of installed RAM or overcommit policy. Under a ulimit -v cap or a memory-constrained container the threshold drops far enough that a plain 32-bit uncompressed_size with no ZIP64 field suffices — a virtual-memory limit lowers the bar rather than mitigating the defect.
This is process termination, not memory exhaustion: Vec::with_capacity() only maps address space and never touches pages, so resident memory stays flat and the abort happens on the refused reservation itself, before any byte is read.
PoC
The reproducer consists of the following zip harness and payload. It requires only the zip crate; use a standard Base64 decoder to materialise the archive before running it.
[package]
name = "zip-poc"
version = "0.1.0"
edition = "2021"
[dependencies]
zip = "8.6.0"
use std::{env, fs::File, io::BufReader};
fn main() {
let archive_path = env::args().nth(1).expect("usage: poc <archive.zip>");
let outdir = std::env::temp_dir().join("zip-poc");
std::fs::create_dir_all(&outdir).unwrap();
let file = File::open(archive_path).unwrap();
let mut archive = zip::ZipArchive::new(BufReader::new(file)).unwrap();
let r = archive.extract(&outdir); // <-- process dies here
…
- Record
GHSA-q7v9-x93c-xqv3- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-400, CWE-789
- Updated
- 2026-11-02
OSVRUSTSEC-2026-0429
7.5
zip: `ZipArchive::extract` aborts the process on a symlink entry with a large declared size
ZipArchive::extract() sizes the symlink-target buffer from the uncompressed
size declared in the central directory record, before reading a single byte of
entry data:
let mut target = Vec::with_capacity(file.size() as usize);
Both inputs are attacker-controlled. ZipFile::size() returns
uncompressed_size verbatim from the central directory, widened to a full u64
by a ZIP64 extended-information extra field, and is never cross-checked against
the compressed size, the remaining archive length, or any platform-appropriate
bound for a symlink target. ZipFile::is_symlink() is decided entirely by the
S_IFLNK bit of the external-attributes field.
Vec::with_capacity() is infallible: when the reservation is refused it calls
handle_alloc_error() and aborts the process. This is not a Rust panic. It is
not caught by catch_unwind(), is unaffected by panic = "unwind", and is not
contained by running the call on a worker thread.
A 112-byte archive is sufficient. One Stored entry flagged S_IFLNK with a
ZIP64 field declaring uncompressed_size = 2^62 and a zero-byte payload makes
extract() fail with:
memory allocation of 4611686018427387904 bytes failed
Aborted (core dumped) # exit 134
The declared value only needs to exceed the host's address space, not its RAM:
Vec::with_capacity() maps address space without touching pages, so resident
memory stays flat and the abort happens on the refused reservation itself. Under
a ulimit -v cap or a memory-constrained container the threshold drops far
enough that a plain 32-bit uncompressed_size with no ZIP64 field suffices.
Applications that extract archives from untrusted input are affected on Unix and Windows targets with default features and no special configuration: upload handling, mail-attachment processing, package and artefact ingestion, backup restore and container-image layer extraction. No confidentiality or integrity property is affected: there is no memory unsafety, no out-of-bounds access, no disclosure and no data corruption.
The fix landed in zip-rs/zip2#984 and shipped in 9.0.0.
For further details see: https://github.com/zip-rs/zip2/security/advisories/GHSA-q7v9-x93c-xqv3
- Record
RUSTSEC-2026-0429- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
OSVUBUNTU-CVE-2026-115909
7.5
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
- Record
UBUNTU-CVE-2026-115909- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-04
OSVAZL-112211 No score
CVE-2026-115909 affecting package rust 1.75.0-31
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
- Record
AZL-112211- Updated
- 2026-11-05
OSVAZL-112220 No score
CVE-2026-115909 affecting package rust 1.98.1-1
zip is a zip library for Rust that supports reading and writing simple ZIP files. From 1.3.0 until 9.0.0, ZipArchive::extract in src/read/mod.rs calls Vec::with_capacity for a symbolic link target using the attacker-controlled ZIP64 uncompressed_size before reading entry data. A small archive can request an impossible capacity, causing the infallible allocation path to abort the process before the entry is validated. The failure cannot be contained with catch_unwind or a worker thread, allowing repeated denial of service in archive-processing applications. This issue is fixed in version 9.0.0.
- Record
AZL-112220- Updated
- 2026-11-05
OSVSUSE-SU-2026:4930-1 No score
Security update for rust-keylime
This update for rust-keylime fixes the following issue:
- CVE-2026-115905: zip: security control bypass via parser differential between central directory and local headers (bsc#1287921)
- CVE-2026-115906: zip: authentication bypass in AES encrypted entries due to improper HMAC verification (bsc#1287921)
- CVE-2026-115907: zip: denial of service via unbounded memory allocation in legacy decoders (bsc#1287921)
- CVE-2026-115908: zip: path traversal via lexical symlink validation during extraction (bsc#1287921)
- CVE-2026-115909: zip: denial of service via uncontrolled memory allocation on symlink targets (bsc#1287921)
Changes for rust-keylime:
- Update to version 0.2.10+12:
- log Agent UUID at push agent startup
- Limit actix-web workers
- Fix clippy needless_late_init warnings
- Unwrap OCTET STRING wrapped EK certificates
- build(deps): bump rand from 0.9.4 to 0.10.1
- build(deps): bump glob from 0.3.3 to 0.3.4
- build(deps): bump reqwest from 0.13.3 to 0.13.4
- Check shutdown marker inside ml_extend loop
- Fix clippy useless_borrows_in_formatting warnings
- build(deps): bump tokio from 1.52.2 to 1.53.1
- Fix payload key upgrade regression by falling back to mTLS key
- Accept any RSA key size for payload encryption
- Bump version to 0.2.10
- keylime: use runtime OpenSSL algorithm detection instead of compile-time symbols
- Record
SUSE-SU-2026:4930-1- Updated
- 2026-11-02
OSVSUSE-SU-2026:4931-1 No score
Security update for rust-keylime
This update for rust-keylime fixes the following issue:
- CVE-2026-115905: zip: security control bypass via parser differential between central directory and local headers (bsc#1287921)
- CVE-2026-115906: zip: authentication bypass in AES encrypted entries due to improper HMAC verification (bsc#1287921)
- CVE-2026-115907: zip: denial of service via unbounded memory allocation in legacy decoders (bsc#1287921)
- CVE-2026-115908: zip: path traversal via lexical symlink validation during extraction (bsc#1287921)
- CVE-2026-115909: zip: denial of service via uncontrolled memory allocation on symlink targets (bsc#1287921)
Changes for rust-keylime:
- Update to version 0.2.10+12:
- log Agent UUID at push agent startup
- Limit actix-web workers
- Fix clippy needless_late_init warnings
- Unwrap OCTET STRING wrapped EK certificates
- build(deps): bump rand from 0.9.4 to 0.10.1
- build(deps): bump glob from 0.3.3 to 0.3.4
- build(deps): bump reqwest from 0.13.3 to 0.13.4
- Check shutdown marker inside ml_extend loop
- Fix clippy useless_borrows_in_formatting warnings
- build(deps): bump tokio from 1.52.2 to 1.53.1
- Fix payload key upgrade regression by falling back to mTLS key
- Accept any RSA key size for payload encryption
- Bump version to 0.2.10
- keylime: use runtime OpenSSL algorithm detection instead of compile-time symbols
- Record
SUSE-SU-2026:4931-1- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
zipcrates.io |
1.3.0 up to 9.0.0 | 9.0.0 |
rustAzure Linux:3 |
through 1.75.0-31 | None known |
rustAzure Linux:3 |
1.85.0 to 1.98.1-1 | None known |
rust-zipDebian:12 |
All versions, plus listed versions 0.6.3-30.6.6-10.6.6-20.6.6-30.6.6-4 Show 7 moreShow less0.6.6-52.5.0-15.1.1+dfsg-15.1.1-15.1.1-25.1.1-35.1.1-4 |
None known |
rust-zipDebian:13 |
All versions, plus listed versions 2.5.0-15.1.1+dfsg-15.1.1-15.1.1-25.1.1-35.1.1-4 |
None known |
rust-zipDebian:14 |
All versions, plus listed versions 2.5.0-15.1.1+dfsg-15.1.1-15.1.1-25.1.1-35.1.1-4 |
None known |
zipcrates.io |
0.0.0-0 up to 9.0.0 | 9.0.0 |
rust-keylimeSUSE:Linux Enterprise Micro 5.4 From an advisory covering 5 CVEs |
before 0.2.10+12-150400.3.22.1 | 0.2.10+12-150400.3.22.1 |
rust-keylimeSUSE:Linux Enterprise Micro 5.5 From an advisory covering 5 CVEs |
before 0.2.10+12-150500.3.22.1 | 0.2.10+12-150500.3.22.1 |
rust-zipUbuntu:26.10 |
All versions, plus listed versions 5.1.1-35.1.1+dfsg-1 |
None known |
rust-zipUbuntu:26.04:LTS |
All versions, plus listed versions 2.5.0-15.1.1-25.1.1-3 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected | OSV | |
| affected, facts | OSV | |
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-115909 is a high vulnerability (CVSS 7.5) in Zip-rs Zip2, published October 22, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.46%, the 40th percentile. Fixed in zip 9.0.0.
References
github.com/zip-rs/zip2/security/advisories/GHSA-q7v9-x93c-xqv3
github.com/zip-rs/zip2/pull/984
github.com/zip-rs/zip2/commit/a543a1fc8f3f3eef36f27bb137230295284cdb98
github.com/zip-rs/zip2/releases/tag/v9.0.0
nvd.nist.gov/vuln/detail/CVE-2026-115909
github.com/advisories/GHSA-q7v9-x93c-xqv3
Show 20 moreShow less
github.com/CVEProject/cvelistV5/tree/main/cves/2026/115xxx/CVE-2026-115909.json
security-tracker.debian.org/tracker/CVE-2026-115909
github.com/zip-rs/zip2
crates.io/crates/zip
rustsec.org/advisories/RUSTSEC-2026-0429.html
suse.com/support/update/announcement/2026/suse-su-20264930-1
bugzilla.suse.com/1287921
suse.com/security/cve/CVE-2026-115905
suse.com/security/cve/CVE-2026-115906
suse.com/security/cve/CVE-2026-115907
suse.com/security/cve/CVE-2026-115908
suse.com/security/cve/CVE-2026-115909
suse.com/support/update/announcement/2026/suse-su-20264931-1
ubuntu.com/security/CVE-2026-115909
cve.org/CVERecord?id=CVE-2026-115909
github.com/zip-rs/zip2/security/advisories/ghsa-q7v9-x93c-xqv3
bugs.debian.org/cgi-bin/bugreport.cgi?bug=1152227
security-tracker.debian.org/tracker/cve-2026-115909
github.com/cveproject/cvelistv5/tree/main/cves/2026/115xxx/cve-2026-115909.json
nvd.nist.gov/vuln/detail/cve-2026-115909
Left out here: the rest of 2 source descriptions. The JSON record has them.