On this page (7 sections)

Pillow, the widely used Python imaging library, has a critical heap buffer overflow in its JPEG decoder. According to the GitHub advisory, a specially built lossless JPEG can make Pillow write past the end of a memory buffer when an application asks for a downscaled image through thumbnail() or draft(). The fix is in Pillow 13.0.0.

If your site or API accepts image uploads from users and resizes them with Python, this is relevant to you. That includes web apps built on Django or Flask, media pipelines, and any worker that generates thumbnails.

What happened

The Pillow maintainers published an advisory titled "Heap buffer overflow in ImagingJpegDecode". The GitHub advisory shows it as published on Oct 17, 2026 and updated on Oct 20, 2026, while the CVE record lists 2026-10-19 as the publication date. The 13.0.0 release went out on 16 Oct and contains the fix. So far only the primary project sources cover this, and no independent write-up confirms the details.

How the flaw works

JPEG decoders can shrink an image while decoding, which is faster than decoding at full size and resizing afterwards. Pillow's draft() method asks for this by requesting 1/2, 1/4 or 1/8 scale output, and thumbnail() calls draft() automatically. Pillow then allocates a one-row buffer sized for the smaller width.

The problem is a mismatch. Per the advisory, libjpeg-turbo 3.x can decode lossless JPEG (the SOF3 variant) but does not apply scaling to it, so the requested scale is silently discarded. It then returns full-width rows into a buffer that was sized for the reduced width. Pillow did not compare the decoder's real output width and component count against its buffer before reading scanlines, so each row writes extra bytes past the end of the allocation.

The advisory states this can corrupt heap memory, crash the process, or potentially allow code execution. Code execution is described as a possibility, not as something demonstrated here. The decode ends in an error after the overflow has already happened, so the caller sees an exception and no image, but the memory write has occurred by then.

Conditions that must be true

  • The application uses draft() or thumbnail() on an attacker-supplied JPEG.
  • Pillow is linked to libjpeg-turbo 3.x. The CVE record says older libjpeg-turbo 2.x and IJG builds reject the lossless input.
  • The draft step selects a reduction of 2 or more. For thumbnail((256,256)) with the default reducing_gap of 2.0, the advisory says the image needs to be at least 1024 pixels wide and tall.
  • The file is an 8-bit lossless (SOF3) JPEG, which Pillow's parser already accepts as a normal JPEG.
Caution. Restricting uploads with Image.open(..., formats=["JPEG"]) does not protect you. The advisory notes the malicious file is a standards-compliant JPEG, so a format allow-list lets it through.

Who is realistically exposed

Exposure depends on two things: whether untrusted people can get a JPEG in front of your code, and which libjpeg-turbo your Pillow build uses. Typical exposed setups are avatar and gallery uploads, product image pipelines for online stores, CMS media libraries, and background workers that fetch remote images and make previews. The research does not say which Pillow builds or wheels link to which libjpeg-turbo release, so treat any Pillow 12.3.0 or earlier as in scope unless you have verified otherwise.

Internal-only tools are lower risk but not risk-free if they process files from customers, email or scraped pages.

How to check your exposure

Start by finding every Python environment that has Pillow installed: web servers, workers, containers and CI images. This read-only command prints the installed Pillow version in the current environment. Anything at 12.3.0 or lower is affected.

Show the installed Pillow version (run inside each virtualenv or container)Shell
python -m pip show Pillow

This second command lists Pillow along with the other installed packages in the current environment, which helps when Pillow is pulled in as a dependency of another library.

List Pillow among installed packages (run in each environment)Shell
python -m pip list | grep -i pillow

Then search your code for draft( and thumbnail( calls on user-supplied files. Those are the paths the advisory identifies as the trigger. Also check pinned versions in requirements.txt, poetry.lock or container images, because a rebuilt image can silently keep the old pin.

What to do now

The only fix the research confirms is upgrading to Pillow 13.0.0. The upstream patch adds a check that the decoder's output width and component size fit the allocated buffer, and rejects the file as broken data if they do not.

Caution. Pillow 13.0.0 removes some deprecated features and drops support for Python 3.10, per the release notes. Test in staging before rolling out, and confirm your Python version is supported.

This command upgrades Pillow to the fixed release in the current environment. Run it in each affected environment, then restart the app.

Upgrade Pillow to the fixed release (run in each affected environment, then restart the app)Shell
python -m pip install --upgrade "Pillow>=13.0.0"

After upgrading, restart your web workers and background processes so they load the new library, and rebuild any container images that pinned an older version.

Tip. If you cannot upgrade right away, reduce exposure by limiting who can upload images and by processing uploads in an isolated worker with minimal permissions. The research does not list an official workaround, so treat this as risk reduction, not a fix.

Detection and monitoring

The advisory says the decode always ends in an OSError after the overflow, so a burst of image-processing exceptions or worker crashes tied to uploads may be a sign of someone probing. That is an inference from the advisory, not a confirmed indicator, and the research reports no exploitation in the wild. Look at application logs for repeated decode errors from the same client, unexpected restarts of image workers, and unusually large JPEG uploads.

Because a successful memory corruption could lead to code execution, also watch for unexpected files or processes on servers that handle uploads. Our guides on checking whether a website is hacked and what to do if it is cover next steps. A web application firewall can add a layer of filtering for uploads, but it is not a substitute for patching a library flaw like this one.

Summary of key points

CVE-2026-112403 at a glance for operators
QuestionAnswer
Which versions are vulnerable?Pillow 12.3.0 and earlier
Which version fixes it?13.0.0
What triggers it?draft() or thumbnail() on a crafted 8-bit lossless JPEG with libjpeg-turbo 3.x
Does a JPEG-only format filter help?No, the file is a valid JPEG
Is exploitation reported?Not in the research; a reproducer is included in the advisory

Sources

This article is based on the public sources below. Details can change as vendors and researchers publish more; check the original advisories before acting on version numbers.

  1. CVE-2026-112403 - GitHub Advisory Databasegithub.com
  2. Reject JPEG streams whose scanlines are wider or have more components… · python-pillow/Pillow@242a059github.com
  3. Release 13.0.0 · python-pillow/Pillowgithub.com
  • Pillow
  • Python
  • CVE-2026-112403
  • heap overflow
  • image processing
  • JPEG