chore(deps): update dependency pillow to v12.3.0 [security]#2574
Open
renovate[bot] wants to merge 1 commit into
Open
chore(deps): update dependency pillow to v12.3.0 [security]#2574renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
renovate
Bot
force-pushed
the
renovate/pypi-pillow-vulnerability
branch
from
April 21, 2026 19:01
b8b3d9f to
a8bfd19
Compare
renovate
Bot
force-pushed
the
renovate/pypi-pillow-vulnerability
branch
from
April 21, 2026 19:01
a8bfd19 to
b8b3d9f
Compare
renovate
Bot
force-pushed
the
renovate/pypi-pillow-vulnerability
branch
from
July 19, 2026 04:22
b8b3d9f to
200d0a3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
12.1.1→12.3.0Pillow has a heap buffer overflow with nested list coordinates
BIT-pillow-2026-42309 / CVE-2026-42309 / GHSA-5xmw-vc9v-4wf2 / PYSEC-2026-2251
More information
Details
Passing nested lists as coordinates to APIs that accept coordinates such as
ImagePath.Path,ImageDraw.ImageDraw.polygonandImageDraw.ImageDraw.linecould cause a heap buffer overflow, as nested lists were recursively unpacked beyond the allocated buffer. Coordinate lists are now validated to contain exactly two numeric coordinates. This was introduced in Pillow 11.2.1.Severity
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Pillow has an OOB Write with Invalid PSD Tile Extents (Integer Overflow)
BIT-pillow-2026-42311 / CVE-2026-42311 / GHSA-pwv6-vv43-88gr / PYSEC-2026-2252
More information
Details
Impact
Processing a malicious PSD file could lead to memory corruption, potentially resulting in a crash or arbitrary code execution.
Patches
Patched version: 12.2.0
Pillow 12.1.1 addressed CVE-2026-25990 by adding checks for tile extents in PSD image decoding/encoding to prevent an out-of-bounds write. However, the bounds checks computed tile extent sums using types susceptible to integer overflow, meaning a PSD image with carefully chosen tile dimensions could produce values that wrap around and bypass the checks, still triggering an out-of-bounds write in src/decode.c and src/encode.c. The fix avoids adding extents together before comparison.
Workarounds
Use any version but affected versions: >= 10.3.0, < 12.2.0
Resources
Severity
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Pillow has a PDF Parsing Trailer Infinite Loop (DoS)
BIT-pillow-2026-42310 / CVE-2026-42310 / GHSA-r73j-pqj5-w3x7 / PYSEC-2026-2874
More information
Details
Impact
An attacker can supply a malicious PDF that causes the process to hang indefinitely, consuming 100% CPU and making the application unresponsive.
Patches
Patched version: 12.2.0.
PdfParser (introduced in Pillow 4.2.0) follows Prev pointers in PDF trailers to read cross-reference sections. If a
trailer's Prev pointer references an offset that has already been processed — either pointing to itself or forming a
longer cycle — the parser enters an infinite loop. Pillow now tracks previously processed trailer offsets and raises an
error if a cycle is detected.
Workarounds
Use any version but the affected versions: >= 4.2.0, < 12.2.0
Resources
Severity
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
FITS GZIP decompression bomb in Pillow
BIT-pillow-2026-40192 / CVE-2026-40192 / GHSA-whj4-6x5x-4v2j / PYSEC-2026-2250
More information
Details
Impact
Pillow did not limit the amount of GZIP-compressed data read when decoding a FITS image, making it vulnerable to decompression bomb attacks. A specially crafted FITS file could cause unbounded memory consumption, leading to denial of service (OOM crash or severe performance degradation).
Patches
The amount of data read is now limited to the necessary amount.
Fixed in Pillow 12.2.0 (PR #9521).
Workarounds
Avoid Pillow >= 10.3.0, < 12.2.0
Only open specific image formats, excluding FITS.
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Pillow has an integer overflow when processing fonts
BIT-pillow-2026-42308 / CVE-2026-42308 / GHSA-wjx4-4jcj-g98j / PYSEC-2026-165
More information
Details
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.
Severity
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
BIT-pillow-2026-42308 / CVE-2026-42308 / GHSA-wjx4-4jcj-g98j / PYSEC-2026-165
More information
Details
Pillow is a Python imaging library. Prior to version 12.2.0, 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 issue has been patched in version 12.2.0.
Severity
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by OSV and the PyPI Advisory Database (CC-BY 4.0).
BIT-pillow-2026-40192 / CVE-2026-40192 / GHSA-whj4-6x5x-4v2j / PYSEC-2026-2250
More information
Details
Pillow is a Python imaging library. Versions 10.3.0 through 12.1.1 did not limit the amount of GZIP-compressed data read when decoding a FITS image, making them vulnerable to decompression bomb attacks. A specially crafted FITS file could cause unbounded memory consumption, leading to denial of service (OOM crash or severe performance degradation). If users are unable to immediately upgrade, they should only open specific image formats, excluding FITS, as a workaround.
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by OSV and the PyPI Advisory Database (CC-BY 4.0).
BIT-pillow-2026-42309 / CVE-2026-42309 / GHSA-5xmw-vc9v-4wf2 / PYSEC-2026-2251
More information
Details
Pillow is a Python imaging library. From version 11.2.1 to before version 12.2.0, passing nested lists as coordinates to APIs that accept coordinates such as ImagePath.Path, ImageDraw.ImageDraw.polygon and ImageDraw.ImageDraw.line could cause a heap buffer overflow, as nested lists were recursively unpacked beyond the allocated buffer. Coordinate lists are now validated to contain exactly two numeric coordinates. This issue has been patched in version 12.2.0.
Severity
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by OSV and the PyPI Advisory Database (CC-BY 4.0).
BIT-pillow-2026-42311 / CVE-2026-42311 / GHSA-pwv6-vv43-88gr / PYSEC-2026-2252
More information
Details
Pillow is a Python imaging library. From version 10.3.0 to before version 12.2.0, processing a malicious PSD file could lead to memory corruption, potentially resulting in a crash or arbitrary code execution. This issue has been patched in version 12.2.0.
Severity
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:HReferences
This data is provided by OSV and the PyPI Advisory Database (CC-BY 4.0).
Pillow has a PDF Parsing Trailer Infinite Loop (DoS)
BIT-pillow-2026-42310 / CVE-2026-42310 / GHSA-r73j-pqj5-w3x7 / PYSEC-2026-2874
More information
Details
Impact
An attacker can supply a malicious PDF that causes the process to hang indefinitely, consuming 100% CPU and making the application unresponsive.
Patches
Patched version: 12.2.0.
PdfParser (introduced in Pillow 4.2.0) follows Prev pointers in PDF trailers to read cross-reference sections. If a
trailer's Prev pointer references an offset that has already been processed — either pointing to itself or forming a
longer cycle — the parser enters an infinite loop. Pillow now tracks previously processed trailer offsets and raises an
error if a cycle is detected.
Workarounds
Use any version but the affected versions: >= 4.2.0, < 12.2.0
Resources
Severity
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the PyPI Advisory Database (CC-BY 4.0).
Pillow
BdfFontFile:Image.new()called without_decompression_bomb_check()— bomb protection bypass via font loadingBIT-pillow-2026-55379 / CVE-2026-55379 / GHSA-45hq-cxwh-f6vc / PYSEC-2026-2255
More information
Details
Summary
PIL/BdfFontFile.pybdf_char()(lines 84–88) reads theBBX width heightfield from a BDF font file and passes the dimensions directly toImage.new()without callingImage._decompression_bomb_check(). This completely bypasses Pillow's documented decompression bomb protection.Image.open()enforcesMAX_IMAGE_PIXELS = 89,478,485and raisesDecompressionBombErrorfor images exceeding2 × MAX = 178,956,970pixels. The BDF font loading path callsImage.new()directly, which only calls_check_size()(validates>= 0) — no pixel count limit.Vulnerable code (
PIL/BdfFontFile.pylines 84–88):Attack trigger: A BDF glyph with
BBX 20000 20000and an emptyBITMAPsection causesImage.frombytes()to raiseValueError, thenImage.new("1", (20000, 20000))allocates 50 MB of C-heap silently. Image.open() would raiseDecompressionBombErrorfor the same dimensions.Steps to reproduce
Minimal malicious BDF file (270 bytes):
Proof of Concept script:
Expected output:
Amplified attack (multiple glyphs):
A BDF file defining 256 glyphs each at
BBX 8000 8000causes256 × 7.6 MB = ~1.95 GBtotal C-heap allocation — all silently, bypassing documented bomb protection.Impact
ImageFont.load("user.bdf"),BdfFontFile(fp)) is affectedself.glyph[ch]for the lifetime of the font object — memory is NOT freed until the font is garbage collectedSeverity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Pillow: WindowsViewer.get_command() OS command injection via unescaped shell path
BIT-pillow-2026-55798 / CVE-2026-55798 / GHSA-4x4j-2g7c-83w6 / PYSEC-2026-2257
More information
Details
1. Summary
WindowsViewer.get_command()constructs acmd.exeshell command by directly embedding afile path into an f-string without escaping. The result is passed to
subprocess.Popen(..., shell=True). Shell metacharacters in the file path — mostimportantly a double-quote (
") that breaks out of the wrapping, followed by&— allowinjection of arbitrary
cmd.execommands.The macOS equivalent (
MacViewer) correctly appliesshlex.quote()to the same parameter.The Linux equivalent (
UnixViewer) does likewise. Windows is the only platform missing thisprotection, despite
shlex.quotebeing already imported on line 21 ofImageShow.py.2. Vulnerable Code
File:
src/PIL/ImageShow.py, lines 133–150Contrast with macOS — SAFE (line 164–168):
Cross-platform summary:
shlex.quote()?shell=True?MacViewerUnixViewerWindowsViewershlex.quoteis imported on line 21. Its omission from the Windows path is a clearoversight, not a deliberate design choice.
3. Proof of Concept
A full working PoC is at
poc_pillow_injection.py. Key parts:Part A — Injection string construction (static, no execution):
Part B — Live execution via
os.system()(verified on Windows 11, Pillow 12.1.1):Severity
CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:LReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Pillow:
FontFile.compile():Image.new()called without_decompression_bomb_check()BIT-pillow-2026-54060 / CVE-2026-54060 / GHSA-5x94-69rx-g8h2 / PYSEC-2026-2254
More information
Details
Description
PIL/FontFile.pyFontFile.compile()assembles per-glyph images into a single combined bitmap usingImage.new("1", (xsize, ysize))without callingImage._decompression_bomb_check(). This is the base-class method shared by bothBdfFontFileandPcfFontFile, and it is triggered whenever a loaded font is converted to anImageFontor saved.Neither
BdfFontFile.BdfFontFile(fp)norPcfFontFile.PcfFontFile(fp)is registered withImage.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.pylines ~64–92):"Slow accumulation" attack — per-glyph dimensions stay BELOW warning threshold:
With PCF-maximum glyph height (65,535):
Steps to reproduce
Proof of Concept script:
Expected output:
Verified live on Pillow 12.2.0 — compile() succeeds with no exception.
Real-world trigger using BDF font file:
Attack scenarios:
BdfFontFile(upload).to_imagefont())to_imagefont()Impact
compile()creates a combined bitmap whose pixel count scales asWIDTH × lines × max_glyph_heightwith 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.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
BdfFontFilenorPcfFontFileis loaded viaImage.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/Pillowmainbranch as of 2026-06-08.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Pillow: Out-of-bounds read via attacker-controlled row stride on Pillow's mmap path (McIdas AREA files)
BIT-pillow-2026-54058 / CVE-2026-54058 / GHSA-62p4-gmf7-7g93 / PYSEC-2026-3493
More information
Details
Summary
When Pillow loads an uncompressed image whose tile uses the
rawcodec and a mode inImage._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 viaPyImaging_MapBuffer(src/map.c). The per-row spacing (stride) is taken from the tile arguments.map.cvalidatesoffset + ysize*stride <= buffer_lenbut never checks thatstrideis at least the natural row widthxsize * pixelsize.The McIdas AREA plugin (
McIdasImagePlugin.py) derivesstride,offset,xsize, andysizedirectly from attacker-controlled 32-bit header words with no validation. By supplying astridefar smaller than the row width, an attacker makes each row pointer readxsize*pixelsizebytes 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.Step 2:
ImageFile.load(mmap branch) - selects mmap and delegates tomap_buffer.Step 3:
PyImaging_MapBuffer- builds row pointers atstridespacing into the mmap; validates everything exceptstride >= row width.im->linesize(the number of bytes any consumer reads per row) isxsize * pixelsize = 200000, but the row pointers are onlystride = 1byte apart and the buffer is onlyoffset + ysize*stride = 2bytes "claimed". Nothing reconciles the two.Step 4: pixel access (
Image.tobytes()→ raw encodercopy1) - readslinesizebytes fromim->image[0], i.e.xsizebytes starting atview.buf + offset, running far past the mmap.Chain Summary
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:xsizereliably crashes the worker with SIGBUS.Suggested fix
Core fix in
src/map.c(PyImaging_MapBuffer): rejectoffset < 0andstride < im->linesize. Defense-in-depth inMcIdasImagePlugin._open: rejectoffset < 0orstride < xsize*pixelsize.Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Pillow: Heap out-of-bounds write
Image.paste()/Image.crop()via signed coordinate overflowBIT-pillow-2026-59199 / CVE-2026-59199 / GHSA-6r8x-57c9-28j4 / PYSEC-2026-3451
More information
Details
Summary
Pillow's public image coordinate APIs can trigger a native heap out-of-bounds
write when given coordinates near the signed 32-bit integer limits. In 4-byte
pixel modes such as
RGBA, this becomes a controlled backward heap underwrite:for a source image of width
W, Pillow writes4 * Wattacker-controlled bytesstarting
4 * Wbytes before the destination row pointer. With successful largeimage allocation, the theoretical upper bound is ~2 GiB backwards from
the destination row.
Minimal public API trigger:
The same root cause is also reachable through
Image.crop()andImage.alpha_composite(). No private API, ctypes, custom Python object, ormalformed image file is needed.
This has been confirmed as an ASAN heap-buffer-overflow write. On normal
non-ASAN Pillow builds, the minimal trigger corrupts the heap and aborts with
double free or corruption (out)Details
src/PIL/Image.py:paste()accepts a 4-tuple box and passes it to the nativeImagingCore.paste()method:src/_imaging.c:_paste()parses the four Python coordinates into signedintvalues and calls
ImagingPaste():src/libImaging/Paste.c:ImagingPaste()computes and clips the region usingsigned
intarithmetic:With
dx0 = 2147483646anddx1 = -2147483648,dx1 - dx0wraps to2.That matches the 2-pixel source image, so the size check passes. The later
dx0 + xsizeclip check wraps around and does not reject the out-of-boundsdestination.
For 4-byte pixel modes such as
RGBA, the paste loop then multipliesdxbypixelsize:For the minimal PoC, this writes 8 attacker-controlled bytes 8 bytes before the
destination row allocation.
The primitive scales with the attacker-controlled source width:
Examples for
RGBA:Pillow's image creation guard currently limits
xsizeto roughlyINT_MAX / 4 - 1, so the theoretical upper bound for thisRGBAunderwrite is2,147,483,640bytes before the destination row pointer. In practice, theusable range depends on memory availability, allocator layout, and process heap
state.
Two other documented APIs reach the same sink:
Image.crop()keepsright - leftsmall, so the Python decompression-bombcheck allows it.
src/libImaging/Crop.cthen computes wrapped pastecoordinates and calls
ImagingPaste().PoC
The following standalone script exercises all three public API paths. Save it
as
b021_poc.pyand run it withpaste,crop, oralpha.Run against an ASAN build:
Observed ASAN signature for the direct
Image.paste()path:On non-ASAN Pillow
12.2.0and local12.3.0.dev0, the direct minimalImage.paste()trigger returns frompaste()and then the process abortsduring cleanup with:
Observed ASAN signature for the
Image.crop()andImage.alpha_composite()paths: