Employees upload headshots or images attached to application forms, and Pillow generates thumbnails for the list view. When we checked the dependencies of such a business system built with Django or FastAPI using pip-audit on October 21, eight vulnerabilities were reported for pillow 12.3.0. Every entry in the fixed-version column was 13.0.0.
There is no way to fix this while staying on the 12 line. The last 12.x release on PyPI is 12.3.0 from July 1, and 13.0.0 can only be installed on Python 3.11 or later. Moreover, the 13.0.0 release notes list 43 vulnerability fixes, and audit tools show only some of them. As of October 21, the CRITICAL JPEG issue did not appear in pip-audit.
This article explains how to tell, condition by condition, which processing is affected, which compatibility changes to check before upgrading to 13.0.0, and how to proceed with Python 3.10 environments.
Fixed only in 13.0.0, which requires Python 3.11 or later
| Release | Published to PyPI (UTC) | Required Python | October fixes |
|---|---|---|---|
| 12.3.0 | 2026-07-01 | 3.10 or later | Not included (last 12.x release) |
| 13.0.0 | 2026-10-16 | 3.11 or later | Included |
Nine Pillow advisories were published in the GitHub Advisory Database on October 20 (UTC), and the fixed version for all of them is 13.0.0. By severity, there was 1 CRITICAL, 6 HIGH and 2 MODERATE. The 13.0.0 release notes state, as the first backward-incompatible change, "Dropped support for Python 3.10 (because it reached end of life in October 2026)". On the Python side as well, the last security fix for 3.10 was 3.10.22 on October 1 (PEP 619).
In the editorial team's test environment, resolving dependencies with the target Python version specified gave the following results.
--python-version 3.10 : pillow -> pillow==12.3.0
--python-version 3.10 : pillow>=13.0.0 -> unsatisfiable(pillow==13.0.0 only supports >=3.11)
--python-version 3.11 : pillow -> pillow==13.0.0
Systems running on Python 3.10 cannot upgrade Pillow alone. Even with requirements.txt, which sets no upper bound, a 3.10 environment stays on 12.3.0 without raising an error.
Audit tools show only some of them
As of October 21, the Security section of the 13.0.0 release notes lists 39 items, or 43 fixes counted by GHSA ID. By severity, there are 3 Critical, 14 High, 23 Medium and 3 Low. The document at release had 38 items and 42 fixes; on October 19, one Medium fix (GHSA-654x-cwwv-5j9c) and the assigned CVE IDs were added. On the documentation site, the latest document showed 43 fixes, while the stable document still showed 42.
Checking these 43 one by one in the GitHub Advisory Database, only the nine above were included as of October 21. Of the remaining 34, 32 were published as security advisories in the Pillow repository on October 17 to 19 but are not in the Advisory Database. Furthermore, checking 12.3.0 with pip-audit (using the default PyPI vulnerability data) returned eight results, which did not include the CRITICAL GHSA-8g5g-8vmr-9782 (a heap buffer overflow in the JPEG decoder). This issue was also absent from PyPI's vulnerability data for 12.3.0.
GHSA-654x, which was added later, is an issue where exporting an image in Arrow format with the environment variable PILLOW_ALIGNMENT set to 2 or higher mixes in the contents of previously used memory. The repository's advisory lists the affected range as "11.2.1 - 13.0.0" and leaves the fixed-version field empty, but the release notes list it as a fix in 13.0.0. When we ran the advisory's reproduction code in the editorial team's test environment (macOS 26.5.1, Python 3.12.12), on 12.3.0, 114 of the 2440 bytes written were values that were not pixels of that image, while on 13.0.0 the export stopped with the exception "Image rows are not contiguous in memory." This suggests it is fixed in 13.0.0.

The severity ratings also differ between the release notes and the Advisory Database. The zero-width image issue (GHSA-58r4) and the GIF issue (GHSA-65qm) are Low in the release notes but HIGH in the Advisory Database. Do not set update priorities based on the counts or severities shown by audit tools; plan on the premise that if you use 12.3.0 or earlier, you will upgrade to 13.0.0 to get all 43 fixes. What audit tools display will change as their data is updated.
Conditions for exposure by type of processing
These are the conditions described in the nine Advisory Database entries. Severities are those from the Advisory Database.
| ID (severity) | Affected processing | Summary of conditions |
|---|---|---|
| GHSA-8g5g(CRITICAL) | JPEG thumbnail() | Code paths that use reduced-size decoding (draft()). formats=["JPEG"] does not prevent it |
| GHSA-c6hw・q45j(HIGH)、462x(MODERATE) | Opening and loading TIFF | Affected with default settings. Heap memory contents are returned as pixels, or a crash occurs |
| GHSA-pchr(HIGH) | Re-saving TIFF as TIFF | Saving with libtiff while keeping the loaded metadata |
| GHSA-65qm(HIGH) | Reading GIF frames in sequence | Catching the seek() exception and continuing to use the same image |
| GHSA-p4wg(HIGH) | Appending to PDF | Appending to a user's PDF with save(..., append=True) |
| GHSA-58r4(HIGH) | ImageMorph processing | Accepting zero-width PPM or PGM files and applying morphological operations |
| GHSA-vx6w(MODERATE) | Loading EPS | Ghostscript installed and rendering with load(scale=N) (N>1) |
JPEG thumbnails: reduced-size decoding in thumbnail()
GHSA-8g5g is a problem where JpegImageFile.draft(), which thumbnail() calls automatically, tries to decode a JPEG reduced to 1/2, 1/4 or 1/8, and for lossless (SOF3) JPEGs the data is written without being reduced, overrunning a heap buffer. According to the advisory, the file is a standards-compliant JPEG, so restricting formats with Image.open(..., formats=["JPEG"]) does not prevent it. The condition is that the linked libjpeg supports lossless decoding (libjpeg-turbo 3.0 or later), which the official wheels bundle. The 12.3.0 wheel in our test environment included libjpeg-turbo 3.1.4.1.
The advisory says that when thumbnail((256,256)) is called with the default reducing_gap=2.0, images that are 1024 pixels or larger in both width and height are affected. As workarounds if you cannot upgrade right away, it lists calling img.load() before thumbnail(), or using thumbnail(..., reducing_gap=None).
In the editorial team's test environment, we used a harmless ordinary JPEG (2000×1500) and recorded how draft() is called. This does not reproduce the vulnerability.
thumbnail((256,256)) -> draft(None, (512, 512)) が 1/2 の縮小デコードを選ぶ
load() の後に thumbnail((256,256)) -> draft は呼ばれるが None(縮小デコードしない)
thumbnail((256,256), reducing_gap=None) -> draft を呼ばない
The results were the same on 12.3.0 and 13.0.0. Both workarounds avoid reduced-size decoding. Because they decode the whole image instead, large images take more memory and time. Django's ImageField form validation only calls verify() after Image.open() and does not use thumbnail(). Check your own thumbnail code and the processing in packages you use.
Do you accept TIFF?
Three of the four TIFF issues (c6hw, q45j, 462x) are triggered simply by opening and loading a crafted TIFF. The c6hw and q45j advisories say that in services that return thumbnails, previews or converted images to users, the heap memory of the worker process (such as other users' image data) may be returned as pixels. q45j states that "load() raises no exception", so the application cannot notice anything is wrong.
Even if you do not expect users to upload TIFF, that does not mean you do not accept it. Pillow determines the format from the file contents, not the extension. In the editorial team's test environment, Django 5.2.18's ImageField allowed 70 extensions by default, including tif, tiff, gif, eps, ppm and pgm.
If you cannot upgrade right away, the editorial team suggests narrowing the accepted formats to only those you need, as in Image.open(file, formats=["JPEG", "PNG", "WEBP"]). This closes the TIFF, GIF, EPS and PPM paths, but as noted above, it does not help with the JPEG issue.
GIF frames, appending to PDF, EPS, and ImageMorph
The remaining four apply only when specific processing is used.
- GIF (GHSA-65qm): If you read animation frames in sequence with
seek(), catch the "image is too large" exception and continue using the same image, a GIF of about 60 bytes can make the process allocate several GB of memory. Using onlyImage.open()andload()is not affected, andImageSequence.Iteratorlets the exception propagate - PDF (GHSA-p4wg): Processing that appends images to a user's PDF with
save(..., format="PDF", append=True)(stamps, watermarks and so on) can cause unbounded recursion and CPU or memory consumption - EPS (GHSA-vx6w): Rendering untrusted EPS with
load(scale=N)(N>1) on a server with Ghostscript installed - ImageMorph (GHSA-58r4): Accepting a zero-width PPM or PGM and applying
ImageMorph.MorphOp.apply()causes a crash
We verified GHSA-58r4 by running the advisory's reproduction code (applying dilation4 to a zero-width image) in a separate process. 12.3.0 terminated with a segmentation fault (exit code 139), while 13.0.0 returned a value without raising an exception. Because the whole process crashes, the entire worker stops, not just a single request.
To check your code, search for thumbnail(, seek(, append=True, load(scale and ImageMorph to identify the relevant processing.
Compatibility to check before upgrading to 13.0.0
From the release notes' "Backwards-incompatible changes" and "API changes", we picked the ones business systems are most likely to run into and checked them by running the same code on 12.3.0 and 13.0.0.
| Change | 12.3.0 | 13.0.0 |
|---|---|---|
| Saving an I mode (32-bit) image as PNG | Saves. The value 70000 is clamped to 65535 | OSError: cannot write mode I as PNG |
ExifTags.IFD.Makernote | Works (37500) | AttributeError. Changed to MakerNote |
Saving an image smaller than 16×16 as ICO without sizes | Produces an ICO with no image that cannot be opened | Produces an ICO at the image's size |
Image._show | Present | Removed. Use ImageShow.show |
For saving I mode PNGs, what used to be saved by clamping to a 16-bit PNG now raises an exception. If you have code that writes 32-bit I mode images to PNG, insert im.convert("I;16") as in the release notes' example.
Some changes may also affect appearance. reducing_gap, which thumbnail() uses by default, is now also applied to RGBA and LA images, where it was previously ignored. The look of thumbnails of transparent PNGs may change. For text rendering, the wheels now use the bundled HarfBuzz, and the release notes say rendering may change slightly for characters that rely on autohinting. When a TIFF has the same tag duplicated, the first value is now used instead of the last. We did not compare the visual results of these three points in our test environment. For systems that generate thumbnails or images for reports and forms, compare the output side by side before and after the update.
There are two deprecations: reading and writing the IM format is scheduled for removal in 15.0.0 (October 15, 2028), and JpegImageFile.load_djpeg in 14 (October 15, 2027).
Environments still on Python 3.10 should start by updating Python
Systems running on Python 3.10 first need to update to Python 3.11 or later to get the Pillow fixes. Since security fixes for 3.10 itself also ended in October, the editorial team recommends using the Pillow issue as an occasion to plan a Python update. How to proceed if you use lockfiles or constraint files is touched on in our article on uv 0.13's breaking changes and constraint files.
Until you update Python, combine the workarounds listed above.
- Call
load()beforethumbnail(), or usereducing_gap=None(the workaround in the GHSA-8g5g advisory) - Use
formatsofImage.open()to narrow the accepted formats to those you need (the editorial team's suggestion; closes the TIFF, GIF, EPS and PPM paths) - In GIF frame processing, do not catch
DecompressionBombErrorand continue (avoids the GHSA-65qm condition) - Separate image processing from the web process and run it in workers with memory limits (the editorial team's suggestion; keeps crashes and memory exhaustion from spreading to the whole system)
These address the conditions of the nine Advisory Database entries; for the remaining 34 in the release notes, we have not confirmed the conditions for exposure, except for GHSA-654x mentioned above. Workarounds cannot be used to postpone the update indefinitely, so treat them strictly as a stopgap until you update. How to check vulnerabilities in image processing libraries on the Node.js side is covered in our article on the sharp AVIF vulnerability.
Scope of verification
On October 21, 2026, we directly retrieved and checked the JSON for the nine Pillow advisories in github/advisory-database, the release notes (docs/releasenotes/13.0.0.rst) and pyproject.toml at the 13.0.0 tag of python-pillow/Pillow, Pillow's release and vulnerability information on PyPI, PEP 619, and the source of Django's
ImageField(desk research). In addition, on Linux (Ubuntu 24.04, Python 3.11.17) using the official Pillow 12.3.0 and 13.0.0 wheels, we checked running the GHSA-58r4 reproduction code, howdraft()is called inthumbnail(), four compatibility changes, the extensions accepted by Django 5.2.18, the resolution result on Python 3.10 usinguv pip compile, and the output of pip-audit 2.10.1 (hands-on testing). We did not verify reproduction of the JPEG, TIFF, GIF, PDF or EPS vulnerabilities, the content of the 34 issues not in the Advisory Database (except GHSA-654x), behavior on a Python 3.10 interpreter, Windows wheels, distribution packages, how Dependabot and OSV display them, or a visual comparison of thumbnails before and after the update. On October 21, before publication, we also checked the release notes of the development version (main), the stable and latest documents on the documentation site, the security advisories and GitHub Releases of the Pillow repository (via the GitHub API), and CVE-2026-112403 on NVD, and ran the GHSA-654x reproduction code on macOS 26.5.1 (Python 3.12.12) using the official 12.3.0 and 13.0.0 wheels.
For dependency update plans for business systems built with Python, or how to proceed with updating Python itself, please contact us via Development, AI and automation inquiries.
References
- Pillow 13.0.0 release notes (python-pillow/Pillow, docs/releasenotes/13.0.0.rst)
- Pillow 13.0.0 release notes (main, after the October 19 addition)
- GHSA-654x-cwwv-5j9c: Arrow export leaks stale heap memory when PILLOW_ALIGNMENT > 1 (security advisory for python-pillow/Pillow)
- Pillow 13.0.0 pyproject.toml (requires-python)
- GHSA-8g5g-8vmr-9782: Heap buffer overflow in ImagingJpegDecode(github/advisory-database)
- GHSA-c6hw-5grf-ff96: OOB read in TiffDecode.c tile and RGBA paths(github/advisory-database)
- GHSA-q45j-58j2-45rg: TiffDecode returns uninitialized memory as pixels for short JPEG strips(github/advisory-database)
- GHSA-462x-pgg6-55fp: tiled-TIFF decoder reads past tile buffer(github/advisory-database)
- GHSA-pchr-8j38-3x4p: TIFF encoder ColorMap plane-size mismatch(github/advisory-database)
- GHSA-65qm-jvmh-6pqg: GIF seek keeps rejected size after DecompressionBombError(github/advisory-database)
- GHSA-p4wg-p73j-c528: Unbounded recursion parsing a PDF page tree or trailer chain(github/advisory-database)
- GHSA-vx6w-vv9f-6gvg: EPS load(scale) lets Ghostscript render past the decompression bomb limit(github/advisory-database)
- GHSA-58r4-6rv3-gfh8: Out-of-bounds access in _imagingmorph on a zero-width image(github/advisory-database)
- Pillow — PyPI
- PEP 619 – Python 3.10 Release Schedule
- django/forms/fields.py(django/django、ImageField)








