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.
- An attacker supplies a PDF to a service that calls
Image.save(..., format="PDF", append=True), for example a stamping or watermarking pipeline. - Pillow opens the file with
PdfParser.PdfParser(path, mode="r+b")and begins parsing the existing document. linearize_page_tree(src/PIL/PdfParser.py:1078-1092) follows the/Kidsarray of/Pagesnodes 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 raisesRecursionError.- Separately,
read_prev_trailer(src/PIL/PdfParser.py:697-720) walks the/Prevtrailer chain. Theprocessed_offsetscheck 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. - A duplicated but non-cyclic
/Kidstree 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 toPdfParser. Search the codebase forappend=Truealongsideformat="PDF"and for directPdfParseruse. - 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.savecall in a try/except that catchesRecursionErrorand 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_treeandread_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 tosrc/PIL/PdfParser.pydirectly. - 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.
Published via PulseTrace — Adverse Trace threat intelligence.