~/f4n6 $ grep -r "Pillow PDF parser recursion flaw bypasses CVE-2026-42310 fix" ./investigations/ --include="*.md"

Pillow PDF parser recursion flaw bypasses CVE-2026-42310 fix

Jeff Davies 20 Oct 2026 6 min read

1. Executive summary

A new Pillow vulnerability, tracked as CVE-2026-42310 at CVSS 5.1 MEDIUM (NVD), allows a crafted PDF to drive two recursive parsers in PIL/PdfParser.py into unbounded recursion. The flaw is an incomplete fix for an earlier issue of the same identifier: the trailer-loop guard added previously rejects only an exactly repeated /Prev offset, and linearize_page_tree has no cycle or depth check on /Kids at all. Any service that calls Image.save(..., format="PDF", append=True) on user-supplied PDFs, such as a stamping, watermarking or document-assembly pipeline, parses the attacker's file before writing anything and can be forced into a RecursionError or heavy CPU and memory consumption. The CVE is not in CISA KEV and EPSS records 0%, so there is no evidence of exploitation in the wild; the exposure is to EMEA financial services firms running Pillow-based document processing on untrusted input, where a single malformed 315-byte PDF is enough to trigger the recursion.

2. Regulatory framing

Article Trigger (the fact in this item) Practical impact
DORA Art. 17 A Pillow-based PDF pipeline that appends to client-supplied documents can be driven into uncaught recursion or resource exhaustion, which is an ICT-related disruption to a production service. The incident management process must cover this failure mode: detection of parser crashes or CPU/memory spikes on document-ingest services, escalation, and root-cause handling.
DORA Art. 28 The exposure sits in a third-party Python library (Pillow) embedded in an internal document pipeline, so the dependency is an ICT third-party service supporting a critical function. Third-party risk management must record the Pillow dependency, track its advisory feed, and hold the pipeline owner to a defined patch cadence.

3. Technical analysis & attack chain

The vulnerability is in Pillow's PdfParser, reached whenever an application appends to an existing PDF. The parser is invoked on the supplied file before any image data is written, so the attacker controls the input to both recursive routines.

  1. An attacker supplies a PDF to a service that calls Image.save(..., format="PDF", append=True), for example a stamping or watermarking pipeline.
  2. Pillow opens the file with PdfParser.PdfParser(path, mode="r+b") and begins parsing the existing document.
  3. linearize_page_tree (src/PIL/PdfParser.py:1078-1092) follows the /Kids array of /Pages nodes with no visited set and no depth limit. A page tree whose nodes reference each other in a cycle (the proof of concept uses objects 2 and 3 pointing at each other) recurses until Python raises RecursionError.
  4. Separately, read_prev_trailer (src/PIL/PdfParser.py:697-720) walks the /Prev trailer chain. The processed_offsets check at :714-718, which is the CVE-2026-42310 fix, rejects only an exactly repeated offset. A chain of unique offsets slips past it and recurses without bound.
  5. A duplicated but non-cyclic /Kids tree produces super-linear CPU and memory work rather than a clean exception, which is the more damaging case for a service.

The proof of concept is a 315-byte PDF: a %PDF-1.4 header, three objects (a /Catalog pointing at /Pages 2 0 R, and two /Pages objects whose /Kids reference each other), an xref table and a trailer. Running PdfParser.PdfParser("poc.pdf", mode="r+b") against it raises RecursionError: maximum recursion depth exceeded. The advisory notes that Pillow's policy discourages filing plain Python exceptions as denial of service, so the cyclic-tree case, which burns CPU and memory, is the primary impact.

The fix is a visited set plus a depth limit in both parsers. The advisory does not name a patched Pillow version, so the fixed release is not stated here and must be confirmed against the Pillow release notes before pinning.

The related pypdf advisory (GHSA-5xf7-4p34-54qr) describes a separate infinite loop in pypdf when parsing a page with a not-terminated inline image, fixed in pypdf 6.14.1. It is a different library and a different code path; it is relevant only as a reminder that PDF parsers across the Python ecosystem share this class of unbounded-loop defect.

4. Mitigation & containment

P1 within 24h

  • Identify every service that calls Image.save(..., format="PDF", append=True) or otherwise hands user-supplied PDFs to PdfParser. Search the codebase for append=True alongside format="PDF" and for direct PdfParser use.
  • Where the pipeline accepts untrusted PDFs, put a size and structure limit in front of it: reject files above a defined byte ceiling and run parsing in a worker with a hard CPU-time and memory cap so a recursion or super-linear parse is killed rather than taking down the service.
  • Wrap the Image.save call in a try/except that catches RecursionError and returns a controlled error, so a single malformed file does not surface as an unhandled exception.

P2 within 72h

  • Upgrade Pillow to a release that contains the visited-set and depth-limit fix in both linearize_page_tree and read_prev_trailer. Confirm the fixed version from the Pillow release notes; the advisory does not state it. If no fixed release is available, apply the upstream patch to src/PIL/PdfParser.py directly.
  • Add a regression test that feeds the 315-byte cyclic-tree PDF from the advisory into the pipeline and asserts a clean rejection.

P3 within 7 days

  • Add the Pillow advisory feed to dependency monitoring so follow-ups to CVE-2026-42310 are picked up automatically.
  • Review other Python PDF libraries in the estate (pypdf and similar) for the same unbounded-loop class and confirm they are on current releases; pypdf 6.14.1 carries the fix for its own inline-image loop.

5. Indicators of compromise

No indicators of compromise available in the source material.

The advisory provides a proof-of-concept PDF structure (cyclic /Pages objects, unique-offset /Prev chain) but no hashes, domains, IPs or file paths beyond the Pillow source line references. The behavioural signal is a RecursionError or CPU/memory spike on a PDF-ingest service when processing a small file.

Behavioural indicators

behaviour where to observe confidence
RecursionError: maximum recursion depth exceeded raised during PDF append/save application logs on the document pipeline high (reproduced in advisory PoC)
CPU and memory spike on a worker processing a small (hundreds of bytes) PDF host or container resource metrics medium (described, not measured)
PDF with /Pages nodes whose /Kids reference each other in a cycle PDF structure inspection at ingest high (PoC structure given)
PDF with a long chain of unique /Prev trailer offsets PDF structure inspection at ingest medium (described, no PoC)

6. Detection

The sources give no file hashes or malware strings. The usable artefacts are the PDF structural features that trigger the recursion. A YARA rule can match the cyclic /Pages structure and the append-mode entry point is not visible in the file itself, so the rule targets the document structure.

rule PDF_Cyclic_PageTree_Recursion
{
    meta:
        author = "Adverse Trace"
        date = "2026-10-20"
        reference = "https://github.com/advisories/GHSA-p4wg-p73j-c528"
        description = "Detects PDFs with /Pages nodes whose /Kids reference each other, triggering unbounded recursion in Pillow PdfParser.linearize_page_tree"
    strings:
        $pdf = "%PDF-"
        $pages = "/Type /Pages"
        $kids = "/Kids"
        $catalog = "/Type /Catalog"
    condition:
        $pdf at 0 and $catalog and 2 of ($pages) and $kids
}

The rule is deliberately broad: it flags any PDF with multiple /Pages objects and a /Kids array, which will match legitimate multi-page documents. Tune it by adding a size ceiling (the PoC is 315 bytes) or by pairing it with a downstream parser that rejects cyclic trees.

A Sigma rule for the application-side signal:

title: Pillow PdfParser RecursionError on PDF Append
id: 8f2c1a4e-3b7d-4c9e-a1f6-2d5e9b0c7a13
status: experimental
author: Adverse Trace
date: 2026-10-20
references:

    - https://github.com/advisories/GHSA-p4wg-p73j-c528
logsource:
    category: application
    product: python
detection:
    keywords:

        - 'RecursionError: maximum recursion depth exceeded'
    condition: keywords
falsepositives:

    - Legitimate deeply nested data structures unrelated to PDF parsing
level: medium

CVE assessment

1 referenced CVE

CVE CVSS Exploited EPSS Summary
CVE-2026-42310 5.1 Medium — 0% Pillow is a Python imaging library. From version 4.2.0 to before version 12.2.0, an attacker can supply a malicious PDF that ca…

7. Sources

  • GitHub Security Advisories, "Pillow: Unbounded recursion parsing a PDF page tree or trailer chain (incomplete fix for CVE-2026-42310)", https://github.com/advisories/GHSA-p4wg-p73j-c528, 2026-10-20.
  • GitHub Security Advisories, "pypdf: Possible infinite loop for not terminated inline images", https://github.com/advisories/GHSA-5xf7-4p34-54qr, date not stated in source.

8. Adverse Trace position

Adverse Trace assesses CVE-2026-42310 as CVSS 5.1 MEDIUM per NVD, not in CISA KEV, with EPSS at 0%, so there is no evidence of exploitation and the practical risk is denial of service against Pillow-based PDF pipelines rather than code execution or data compromise. The client impact is concentrated in document-ingest services that append to untrusted PDFs: a 315-byte file is enough to raise RecursionError or drive super-linear CPU and memory use, and the fix for the earlier CVE-2026-42310 does not cover either parser. The advisory does not name a patched Pillow version, so clients should confirm the fixed release before pinning and apply the upstream patch in the interim. We will track the Pillow release notes for the version that carries the visited-set and depth-limit fix and update this advisory when it is published.


Read the original source →

Published via PulseTrace — Adverse Trace threat intelligence.

Post this to LinkedIn
Formatting is converted automatically — headings, bullets, a link back & hashtags. Paste straight in.
J
Jeff Davies