medium

CVE-2026-96638

PyPI · pillow

Summary

Pillow: BMP RLE decoder memory-amplification DoS via unbounded delta escape / absolute mode

Severity
medium
CVSS
6.5
EPSS
0.4% (p28)
CWE
CWE-400, CWE-770
Also known as
GHSA-24rc-8hj2-v98c
Published
2026-10-22
Updated
2026-10-22

Advisory details

Summary

BmpRleDecoder can be forced to allocate gigabytes of memory from a kilobyte-sized crafted BMP file via unbounded growth in the delta escape and absolute mode branches.

Affected versions

All versions up to and including current HEAD (v13.0.0.dev0, commit e7a7ea84)

Root cause

BmpRleDecoder.decode() (BmpImagePlugin.py:327-395) builds a Python bytearray data with dest_length = xsize*ysize, looping while len(data) < dest_length. The loop condition is only checked at the TOP; individual branches append attacker-controlled amounts UNBOUNDED by dest_length:

Because a single delta can push len(data) to ~256 * (row size), a tiny RLE stream combined with a large-but-legal xsize (xsize*ysize just under MAX_IMAGE_PIXELS, ~89M) yields a multi-GB data bytearray (then copied again by bytes(data) and set_as_raw). No heap OOB (bytearray/set_as_raw are bounds-safe), but a severe memory-amplification DoS: the attacker file stays tiny.

Reproduction

Python 3.13, Pillow HEAD e7a7ea84

Attached: pillow_poc_bmp_rle_dos.py

A 1084-byte BMP (BI_RLE8, width=3,000,000, height=1 -> 3M px, passes bomb check) with one delta escape (00 02 00 150) produces ~429 MB data; measured peak RSS 911 MB. Scaling width toward the bomb limit and up=255 produces tens of GB from a ~1 KB file.

Suggested fix

Cap growth at dest_length in the delta and absolute branches, e.g. clamp the delta fill to (dest_length - len(data)) and stop appending once len(data) >= dest_length; reject deltas that would move the cursor beyond the image, matching the existing per-row clamp at line 341-343.

Credit: Yazan Balawneh - CyStack Security Team (GitHub: sonicnew)

pillow_poc_bmp_rle_dos.py

References

Related advisories

Is your project exposed to this? Stateward checks every dependency on every pull request and flags it only if your code actually reaches it.

Check my repo

Summarize with AI

ChatGPTClaudePerplexity

Sources: CISA KEV (public domain), OSV.dev & GitHub Advisory Database (CC-BY-4.0), FIRST EPSS, NVD/CWE (public domain). Served live from the Stateward advisory database.