Multiple denial-of-service vectors in gix-pack: unchecked array indexing causes panics on crafted delta data, and uncapped attacker-controlled size headers enable OOM process kills. Both are triggered by malicious pack data received during clone/fetch.
Bug 1: Unchecked array indexing in delta application (CWE-248)
The apply() function in gix-pack/src/data/delta.rs (lines 33-87) reads delta instructions using unchecked data[i] indexing at 7 locations (lines 41, 45, 49, 53, 57, 61, 65). The command byte's bits indicate how many additional bytes follow, but if the delta data is truncated, the index panics:
pub(crate) fn apply(base: &[u8], mut target: &mut [u8], data: &[u8]) -> Result<(), apply::Error> {
let mut i = 0;
while let Some(cmd) = data.get(i) { // first byte: safely checked
i += 1;
match cmd {
cmd if cmd & 0b1000_0000 != 0 => {
let (mut ofs, mut size): (u32, u32) = (0, 0);
if cmd & 0b0000_0001 != 0 {
ofs = u32::from(data[i]); // PANIC: no bounds check
i += 1;
}
// ... 6 more unchecked data[i] at lines 45, 49, 53, 57, 61, 65
Lines 83-84 use assert_eq! (not debug_assert_eq!) that panics in both debug and release builds:
assert_eq!(i, data.len());
assert_eq!(target.len(), 0);
A second location in parse_header_info() (gix-pack/src/data/entry/decode.rs:116-129) also panics on truncated input via unchecked data[0] and data[i].
Note: PR #2059 (merged 2025-06-25) fixed the explicit panic!() for command code 0. The unchecked array indexing is a distinct class that remains unfixed.
Bug 2: Uncapped allocation from attacker-controlled size headers (CWE-770)
Pack entry headers and delta headers encode object sizes as LEB128-encoded u64 values. These sizes are used to allocate buffers before validating the actual data, with no upper bound:
bytes_to_entries.rs:109 Vec::with_capacity(entry.decompressed_size as usize) // UNCAPPED
resolve.rs:461 out.resize(decompressed_len, 0) // UNCAPPED
resolve.rs:190 fully_resolved_delta_bytes.resize(result_size as usize, 0) // UNCAPPED
A 10-byte crafted pack entry can claim decompressed_size = 0xFFFFFFFFFFFF (281 TB). At bytes_to_entries.rs:109, gitoxide calls Vec::with_capacity(281TB) before any decompression occurs. The OS immediately OOM-kills the process. No MAX_SIZE, max_object_size, or equivalent limit exists anywhere in gix-pack.
The allocation at resolve.rs:461 is equally dangerous: decompressed_size from the pack header is cast to usize and passed to Vec::resize(), which allocates and zeroes the full claimed size before the zlib decompressor runs.
Compiled and executed in Rust 1.94.1 --release mode. All 5 panics confirmed:
[1] delta apply: cmd=0x81, truncated -> PANIC: index out of bounds: len is 1 but index is 1
[2] delta apply: cmd=0xFF, only 3 extra bytes -> PANIC: index out of bounds: len is 4 but index is 4
[3] parse_header_info: empty data -> PANIC: index out of bounds: len is 0 but index is 0
[4] parse_header_info: byte=0x80, truncated -> PANIC: index out of bounds: len is 1 but index is 1
[5] delta apply: assert_eq!(i, data.len()) -> PANIC: assertion failed
For the OOM vector: the allocation path is parse_header_info() -> entry.decompressed_size (u64) -> Vec::with_capacity(size as usize) with no intermediate validation. A minimal pack with a single entry claiming a multi-terabyte size triggers immediate process kill.
Any application built on gitoxide that clones or fetches from an untrusted remote can be crashed by a malicious server:
This affects the gix CLI, any application using the gix crate, and CI/CD systems that clone repositories using gitoxide. No fuzz targets exist for gix-pack (issue #703 tracks oss-fuzz integration).
For panics: replace unchecked data[i] with data.get(i).ok_or(Error::...) and replace assert_eq! with proper error returns.
For OOM: add a configurable maximum object size (similar to git's transfer.maxPackSize) and validate claimed sizes against it before allocating. At minimum, cap allocations to a reasonable default (e.g., 4 GB) and use try_reserve() consistently.
High. Network vector, no privileges required, user interaction required (clone/fetch). The OOM vector is a single-packet process kill with no recovery.
| Software | From | Fixed in |
|---|---|---|
gix-pack
|
- | 0.69.0 |
A security vulnerability is a weakness in software, hardware, or configuration that can be exploited to compromise confidentiality, integrity, or availability. Many vulnerabilities are tracked as CVEs (Common Vulnerabilities and Exposures), which provide a standardized identifier so teams can coordinate patching, mitigation, and risk assessment across tools and vendors.
CVSS (Common Vulnerability Scoring System) estimates technical severity, but it doesn't automatically equal business risk. Prioritize using context like internet exposure, affected asset criticality, known exploitation (proof-of-concept or in-the-wild), and whether compensating controls exist. A "Medium" CVSS on an exposed, production system can be more urgent than a "Critical" on an isolated, non-production host.
A vulnerability is the underlying weakness. An exploit is the method or code used to take advantage of it. A zero-day is a vulnerability that is unknown to the vendor or has no publicly available fix when attackers begin using it. In practice, risk increases sharply when exploitation becomes reliable or widespread.
Recurring findings usually come from incomplete Asset Discovery, inconsistent patch management, inherited images, and configuration drift. In modern environments, you also need to watch the software supply chain: dependencies, containers, build pipelines, and third-party services can reintroduce the same weakness even after you patch a single host. Unknown or unmanaged assets (often called Shadow IT) are a common reason the same issues resurface.
Use a simple, repeatable triage model: focus first on externally exposed assets, high-value systems (identity, VPN, email, production), vulnerabilities with known exploits, and issues that enable remote code execution or privilege escalation. Then enforce patch SLAs and track progress using consistent metrics so remediation is steady, not reactive.
SynScan combines attack surface monitoring and continuous security auditing to keep your inventory current, flag high-impact vulnerabilities early, and help you turn raw findings into a practical remediation plan.