GHSA-254f-3936-288h: Pip/Pillow vulnerability

Published Oct 21, 2026
·
Updated

Summary PdfParser's getvalue() can recurse without any limit and crash with an uncaught RecursionError. It is reachable through Image.save(im, fp=<pdf>, format="PDF", append=True) when the existing PDF file is attacker-controlled. This is the same class of bug as https://github.com/advisories/GHSA-r73j-pqj5-w3x7 (the /Prev trailer chain loop, fixed earlier this year), in the same file, but in two different functions that fix did not touch.

Details getvalue() in src/PIL/PdfParser.py

This is a classmethod that takes maxnesting: int = -1 and has a guard:

python if maxnesting == 0: return None, None

Every recursive call (nested arrays, dictionaries, indirect object definitions) passes maxnesting=maxnesting - 1. Since the default and every call site start at -1, this only ever counts down further negative: -1, -2, -3, and so on. It never equals exactly 0, so the guard never runs. A PDF object with a value nested a few thousand levels deep is enough to hit Python's recursion limit. It does not even need a valid /Pages tree, since readpdfinfo() parses the /Root object's own dictionary first, before checking anything else.

This advisory reports a single vulnerability: an ineffective recursion-depth guard in PdfParser.getvalue() (the sentinel -1 default never reaches the == 0 check). The mention of GHSA-r73j-pqj5-w3x7 is comparative context for an already-fixed, already-CVE'd bug in a different function — it is not a second vulnerability being reported. The three trigger paths (nested arrays/dicts/indirect objects) are not independently fixable; they share one root cause and one fix location in getvalue() .

Steps to reproduce Both PDFs below are plain text, built directly in Python, no PDF library needed.

python from PIL.PdfParser import PdfParser

def buildpdf(root): out = bytearray(b"%PDF-1.4\n") offset = len(out) out += f"1 0 obj\n".encode() out += root out += b"\nendobj\n" out += b"xref\n" out += f"0 2\n".encode() out += b"0000000000 65535 f \n" out += f"{offset:010d} 00000 ".encode() + b"n \n" xrefoffset = len(out) out += b"xref\n" out += f"0 2\n".encode() out += b"0000000000 65535 f \n" out += f"{offset:010d} 00000 ".encode() + b"n \n" out += b"trailer\n" out += f"<< /Size 2 /Root 1 0 R >>\n".encode() out += b"startxref\n" out += f"{xrefoffset}\n".encode() out += b"%%EOF" return bytes(out)

bomb = b"[" 3000 + b"]" 3000 root = b"<< /Type /Catalog /Pages 2 0 R /Bomb " + bomb + b" >>" with open("nestingbomb.pdf", "wb") as f: f.write(buildpdf(root))

PdfParser("nestingbomb.pdf") # raises RecursionError

Expected: a clean PdfFormatError.

Actual: raises a raw RecursionError: maximum recursion depth exceeded.

Impact - Availability: an attacker who controls the content of a PDF file that an application later appends an image page to (Image.save(..., format="PDF", append=True)) can crash that request with a RecursionError, using a file of a few hundred bytes. - Confidentiality: none. - Integrity: none. - Pillow cannot open PDFs for reading through Image.open(). PdfParser is only reached through the append-mode save path, so the app has to be doing that specific thing (for example, stamping or watermarking an existing PDF) for this to matter.

Affected Software

1 affected componentFixes available
pip/Pillow>=5.1.0<13.0.0
13.0.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/Pillow to a version that resolves this vulnerability.

    Fixed in 13.0.0

Event History

Oct 21, 2026
Advisory Published
via GitHub·09:11 PM
Data Sourced
via GitHub·09:11 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Which workflows are exposed?

The issue is reachable when Pillow saves an image to an existing PDF with Image.save(im, fp=<pdf>, format="PDF", append=True) and that PDF is attacker-controlled. Processing a PDF supplied by an untrusted party in this append workflow is the relevant exposure condition.

2

What must an attacker provide to trigger the crash?

An attacker needs to control the existing PDF and include an object value nested deeply enough to exceed Python's recursion limit. The PDF does not need a valid /Pages tree, because the parser reads the /Root object's dictionary before validating that structure.

3

Does the nesting limit protect the default behavior?

No. The default max_nesting value is -1, and recursive calls decrement it further, so it never reaches the zero value that would stop recursion. As a result, deeply nested arrays, dictionaries, or indirect object definitions can recurse until an uncaught RecursionError occurs.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203