CVE Patch Review

CVE-2026-59936: Root-Cause Fix for pypdf Inline Image EOF Infinite Loop

CVE-2026-59936 · GHSA-5XF7-4P34-54QR · Updated 2026-07-23 Root-cause

Summary

pypdf before 6.14.1 could spin indefinitely while parsing a malformed, truncated inline image stream, causing CPU-bound denial of service. The patch adds an explicit end-of-stream check that raises PdfStreamError and includes a regression test for truncated inline image content.

Analysis

Vulnerability

CVE-2026-59936 describes a denial-of-service condition in pypdf versions prior to 6.14.1 where malformed PDFs containing a truncated inline image block can drive a parser thread to 100% CPU. The issue is in inline image parsing logic within pypdf/generic/_data_structures.py, where end-of-stream handling was insufficient during token consumption. According to the advisory and CVE records, an unauthenticated attacker can trigger the condition by supplying a crafted PDF, causing the parser to loop instead of terminating cleanly with an error. See the upstream advisory at GHSA-5XF7-4P34-54QR, the CVE record at MITRE, and the NVD entry at NVD.

The supplied patch context shows the failure mode is specifically tied to a missing EOF guard while reading inline image data. In a truncated stream, token reads can return an empty value, and without a terminating condition the parser can continue iterating indefinitely.

Patch

The upstream fix in commit ec3b14596186c40caca7cf8ab9b2155203e01b5b and pull request #3891 adds an explicit empty-token check and converts the malformed-input case into a deterministic exception:

if not tok:
                raise PdfStreamError("Unexpected end of stream.")

This is paired with a regression test in tests/generic/test_data_structures.py that exercises a broken content stream and asserts that _read_inline_image raises PdfStreamError on truncated input:

with pytest.raises(expected_exception=PdfStreamError, match=r"^Unexpected end of stream\.$"):
    content_stream._read_inline_image(BytesIO(b"\n/IM true\n/W001"))

The test is important because it captures the exact malformed-stream class described in the patch notes: a content stream that ends before the inline image parser can complete, including cases caused by upstream filter/decompression failures.

Review

Pros

  • The fix directly addresses the parser control-flow defect by adding a hard stop on EOF, which is the relevant root cause for the infinite loop shown in the patch context.
  • Failure semantics are appropriate: malformed/truncated PDF content now raises PdfStreamError instead of consuming CPU indefinitely.
  • The regression test is narrowly scoped and validates both exception type and message, reducing the chance of silent reintroduction.
  • The patch is minimal and low-risk: it adds a single guard rather than restructuring parsing logic, which limits compatibility impact while eliminating the DoS condition.
  • The chosen exception aligns with stream corruption/truncation behavior and should be understandable to callers already handling PDF parsing failures.

Cons

  • The visible patch is narrowly targeted to one EOF path in inline image parsing; without broader parser audit evidence, it does not prove that all malformed inline image edge cases are covered.
  • The regression test covers a truncated stream example but does not demonstrate additional adversarial cases such as partial terminators, repeated empty reads across wrappers, or variant malformed inline image dictionaries.
  • The patch summary does not show whether any higher-level caller behavior was updated to ensure graceful handling of PdfStreamError in batch or service contexts.

Verdict

Root-cause.

Based on the provided diff, this patch fixes the actual loop-enabling condition: token reads reaching EOF without terminating the parser. The new guard converts that state into an explicit exception, and the added regression test matches the reported malformed-input scenario. While the change is intentionally small, it is source-grounded and sufficient for the described vulnerability class in the advisory and the fixing commit.

Sources