GHSA-x494-mj8g-cj27: gix-pack has multiple DoS vectors Unchecked Indexing Panics And Uncapped Vulnerability

HIGH
CWE-770 CWE-248
Published
Advisory Summary

GHSA-x494-mj8g-cj27 is a HIGH unchecked indexing panics and uncapped vulnerability (CWE-770: Allocation of Resources Without Limits or Throttling) affecting gix-pack has multiple DoS vectors libraries in the crates.io ecosystem. The issue involves unchecked indexing panics and uncapped oom allocations from crafted pack data. Remediation is available by upgrading affected packages to version 0.69.0.

GHSA-x494-mj8g-cj27 at a Glance

CVE Identifier N/A
GHSA Identifier GHSA-x494-mj8g-cj27
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-770: Allocation of Resources Without Limits or Throttling
Affected Product gix-pack has multiple DoS vectors
Package Ecosystem crates.io
Published Date
Last Updated

How to Fix GHSA-x494-mj8g-cj27

To remediate this vulnerability, upgrade all affected gix-pack has multiple DoS vectors dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

gix-pack fixed: 0.69.0
Upgrade gix-pack to 0.69.0
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for gix-pack

How to Check if You Are Affected

To determine whether your application is affected by GHSA-x494-mj8g-cj27, inspect your project dependencies for gix-pack has multiple DoS vectors:

inspect dependencies for gix-pack

Analyze the terminal output to locate any version of gix-pack has multiple DoS vectors in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 0.69.0), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-770: Allocation of Resources Without Limits or Throttling, where the product allocates a reusable resource or group of resources on behalf of an actor without imposing any intended restrictions on the size or number of resources that can be allocated. In gix-pack has multiple DoS vectors, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### Summary 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. ### Details **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: ```rust 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: ```rust 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. ### PoC 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. ### Impact Any application built on gitoxide that clones or fetches from an untrusted remote can be crashed by a malicious server: - **Panic DoS**: 1-2 bytes of crafted delta data causes an immediate process abort - **OOM DoS**: A single crafted pack entry header causes the process to attempt a multi-terabyte allocation, triggering an immediate OOM kill by the OS 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). ### Suggested fix 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. ### Severity High. Network vector, no privileges required, user interaction required (clone/fetch). The OOM vector is a single-packet process kill with no recovery.

GHSA-x494-mj8g-cj27 Affected gix-pack has multiple DoS vectors Versions

1 component(s)
Fixed Versions:
0.69.0
Product / Package Ecosystem Affected Versions Fixed Version
gix-pack
crates.io
< 0.69.0
0.69.0

References

Frequently Asked Questions

What is GHSA-x494-mj8g-cj27?
GHSA-x494-mj8g-cj27 is a security vulnerability in gix-pack has multiple DoS vectors involving unchecked indexing panics and uncapped OOM allocations from crafted pack data under CWE-770: Allocation of Resources Without Limits or Throttling.
Which gix-pack has multiple DoS vectors versions are affected?
Affected versions include releases prior to 0.69.0 across affected packages: gix-pack.
What version fixes GHSA-x494-mj8g-cj27?
Upgrading to 0.69.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is gix-pack affected?
Yes, gix-pack is affected in releases prior to 0.69.0. Upgrade your dependency to 0.69.0 or later.
What CWE is associated with GHSA-x494-mj8g-cj27?
GHSA-x494-mj8g-cj27 is classified under CWE-770: Allocation of Resources Without Limits or Throttling (The product allocates a reusable resource or group of resources on behalf of an actor without imposing any intended restrictions on the size or number of resources that can be allocated.).
How can I check crates.io dependencies for GHSA-x494-mj8g-cj27?
Run the dependency tree command: 'inspect dependencies for gix-pack'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-x494-mj8g-cj27",
  "modified" : "2026-05-05T19:24:15Z",
  "published" : "2026-05-05T19:24:15Z",
  "aliases" : [ ],
  "summary" : "gix-pack has multiple DoS vectors: unchecked indexing panics and uncapped OOM allocations from crafted pack data",
  "details" : "### Summary\n\nMultiple 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.\n\n### Details\n\n**Bug 1: Unchecked array indexing in delta application (CWE-248)**\n\nThe `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:\n\n```rust\npub(crate) fn apply(base: &[u8], mut target: &mut [u8], data: &[u8]) -> Result<(), apply::Error> {\n    let mut i = 0;\n    while let Some(cmd) = data.get(i) {  // first byte: safely checked\n        i += 1;\n        match cmd {\n            cmd if cmd & 0b1000_0000 != 0 => {\n                let (mut ofs, mut size): (u32, u32) = (0, 0);\n                if cmd & 0b0000_0001 != 0 {\n                    ofs = u32::from(data[i]);     // PANIC: no bounds check\n                    i += 1;\n                }\n                // ... 6 more unchecked data[i] at lines 45, 49, 53, 57, 61, 65\n```\n\nLines 83-84 use `assert_eq!` (not `debug_assert_eq!`) that panics in both debug and release builds:\n\n```rust\n    assert_eq!(i, data.len());\n    assert_eq!(target.len(), 0);\n```\n\nA 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]`.\n\nNote: 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.\n\n**Bug 2: Uncapped allocation from attacker-controlled size headers (CWE-770)**\n\nPack 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:\n\n```\nbytes_to_entries.rs:109  Vec::with_capacity(entry.decompressed_size as usize)  // UNCAPPED\nresolve.rs:461           out.resize(decompressed_len, 0)                       // UNCAPPED\nresolve.rs:190           fully_resolved_delta_bytes.resize(result_size as usize, 0)  // UNCAPPED\n```\n\nA 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.\n\nThe 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.\n\n### PoC\n\nCompiled and executed in Rust 1.94.1 `--release` mode. All 5 panics confirmed:\n\n```\n[1] delta apply: cmd=0x81, truncated -> PANIC: index out of bounds: len is 1 but index is 1\n[2] delta apply: cmd=0xFF, only 3 extra bytes -> PANIC: index out of bounds: len is 4 but index is 4\n[3] parse_header_info: empty data -> PANIC: index out of bounds: len is 0 but index is 0\n[4] parse_header_info: byte=0x80, truncated -> PANIC: index out of bounds: len is 1 but index is 1\n[5] delta apply: assert_eq!(i, data.len()) -> PANIC: assertion failed\n```\n\nFor 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.\n\n### Impact\n\nAny application built on gitoxide that clones or fetches from an untrusted remote can be crashed by a malicious server:\n\n- **Panic DoS**: 1-2 bytes of crafted delta data causes an immediate process abort\n- **OOM DoS**: A single crafted pack entry header causes the process to attempt a multi-terabyte allocation, triggering an immediate OOM kill by the OS\n\nThis 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).\n\n### Suggested fix\n\nFor panics: replace unchecked `data[i]` with `data.get(i).ok_or(Error::...)` and replace `assert_eq!` with proper error returns.\n\nFor 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.\n\n### Severity\n\nHigh. Network vector, no privileges required, user interaction required (clone/fetch). The OOM vector is a single-packet process kill with no recovery.",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "crates.io",
      "name" : "gix-pack"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.69.0"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 0.68.0"
    }
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/GitoxideLabs/gitoxide/security/advisories/GHSA-x494-mj8g-cj27"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/GitoxideLabs/gitoxide"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-248", "CWE-770" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-05-05T19:24:15Z",
    "nvd_published_at" : null
  }
}