diff --git a/crates/zip/RUSTSEC-0000-0000.md b/crates/zip/RUSTSEC-0000-0000.md new file mode 100644 index 0000000000..10157299e0 --- /dev/null +++ b/crates/zip/RUSTSEC-0000-0000.md @@ -0,0 +1,68 @@ +```toml +[advisory] +id = "RUSTSEC-0000-0000" + +package = "zip" + +date = "2026-10-22" + +categories = ["denial-of-service"] + +keywords = ["dos", "symlink", "unbounded-allocation", "extract"] + +aliases = ["CVE-2026-115909", "GHSA-q7v9-x93c-xqv3"] + +cvss = "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + +[versions] +patched = [">= 9.0.0"] +``` + +# 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: + +```rust +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](https://github.com/zip-rs/zip2/pull/984) and +shipped in 9.0.0. + +For further details see: https://github.com/zip-rs/zip2/security/advisories/GHSA-q7v9-x93c-xqv3