Forty-One Advisories in Forty-Eight Hours, and the Fix Drops Python 3.10
Between 17 October 2026 at 10:32:48Z and 19 October at 09:47:19Z, the Pillow project published 41 security advisories. Two are critical, thirteen high, twenty-three medium, three low. Thirty-eight carry CVE identifiers, running in three blocks — CVE-2026-96636 through 96644 and 96778, CVE-2026-112390 through 112406, and CVE-2026-112891 through 112901. The first tranche reached NVD at 21:17Z on 19 October, roughly two and a half days behind the advisories.
Pillow is not a peripheral dependency. It is the Python imaging library — 13,904 stars, created in July 2012, and the thing that opens a JPEG in essentially every Python web application, thumbnailer, document pipeline and multimodal model preprocessor written in the last decade. A batch this size against this package is a fleet-wide event.
Three things about the batch are worth more attention than the count. Sixteen of the 41 advisories are machine-discovered findings from Anthropic’s disclosure programme, identifiable by the ANT-2026- identifier embedded in their text. The only release that fixes any of them is 13.0.0, a major version that raises the minimum Python from 3.10 to 3.11, with no 12.3.x backport in existence. And three advisories carry no CVE, one of which has no patched version at all — it was published two days after the release that was supposed to close the batch, and remains open.
The findings that are not human
Sixteen advisory bodies open with a fixed-width header block of a shape that does not come from a person writing prose. We quote one verbatim, from CVE-2026-112901:
ANT-2026-USJRDKVK [medium] CWE-1333 dos — ReDoS in XbmImagePlugin: 512-byte file stalls Image.open() for minutes — Location: src/PIL/XbmImagePlugin.py:58 in XbmImageFile._open() — Commit: a707c6b21c313880d0771e54965d142dd42358b6.
Those ANT-2026- strings are finding identifiers from Anthropic’s coordinated vulnerability disclosure dashboard, where each one resolves to a published record. Anthropic’s own description of the programme, read directly, is that it “uses Claude models, including an early snapshot of Claude Mythos Preview, to find security vulnerabilities in open-source software,” partners with external firms to triage, and reports human-reviewed findings to maintainers. The dashboard’s headline numbers, as last updated 2 October 2026 at 19:47 UTC: 6,157 vulnerabilities disclosed across 591 open-source projects, 516 known patched, 584 identifiers issued (219 CVE records and 365 GHSAs). The funnel behind that is stated as 29,439 triaged findings narrowing to 6,123 candidates, of which 5,674 were reviewed by external security firms, with a claimed 92.7% true-positive rate.
Pillow does not appear on that dashboard yet, which is consistent rather than suspicious: the page predates this batch by a fortnight. We note the sixteen identifiers as read from the advisory text, not as a count Anthropic has published.
The sixteen are not the whole batch, and the distinction matters for how the story gets told. The second critical, CVE-2026-112398 — a heap buffer overflow in the FlashPix JPEG-tile decoder that the advisory states leads to arbitrary code execution from a single Image.open() — carries no ANT identifier and names its researcher in the text: Alex Bouffard. The FPX path is reachable because Image.open() detects OLE2 compound documents by the D0CF11E0 magic regardless of file extension, so any service that thumbnails untrusted uploads with the optional olefile dependency installed is in scope with no user interaction. A human found the one with a working exploit. That is worth stating plainly in a week when the easy framing is that the machines did it all.
What the machine findings actually look like
The ANT findings are not low-grade noise, and reading three of them is the fastest way to judge the class.
CVE-2026-96778 (ANT-2026-02G3S0EW, high, CWE-787) is a heap overflow in ImagingJpegEncode() at src/libImaging/JpegEncode.c:330. State 4 calls jpeg_write_marker() for the COM comment marker without checking the output buffer has room, while JpegImagePlugin._save() sizes that buffer from EXIF and ICC/XMP data only and reserves nothing for a comment. A 65,498-byte ICC profile fills the 65,536-byte buffer exactly; libjpeg’s emit_byte() then stores one byte past the end and decrements free_in_buffer from zero, the size_t wraps, and the marker, the comment and the entire compressed image are written out of bounds. The trigger is the documented way to preserve colour — im.save(..., "JPEG", icc_profile=im.info.get("icc_profile")) — with the comment forwarded from im.info automatically and no non-default setting involved. The advisory is candid about the limit of its own evidence: the overwritten memory sits in front of function pointers the encoder calls next, so code execution is plausible, but the attached reproducer demonstrates only the crash.
CVE-2026-112901 (ANT-2026-USJRDKVK) is the XBM regular expression. The xbm_head pattern runs over the first 512 bytes of any stream whose prefix starts with #define, and several adjacent quantifiers can consume the same characters. A crafted 512-byte file measured 375 seconds of single-core CPU at k = 60, against about 40 ms for a well-formed XBM of the same size, with cost growing roughly as the fifth power of k. The sharp part is structural: this happens before any format is identified, so none of Pillow’s size, pixel-count or decompression-bomb limits apply, and an upload size limit does not help because the file is tiny. The advisory names its own ancestor — the same class as CVE-2021-25292 in PdfParser, in a different plugin, reachable from plain Image.open().
CVE-2026-112895 is the one that should worry a maintainer most, because it is labelled in its own summary as an incomplete fix for CVE-2026-42310. The trailer-loop guard added for that earlier CVE rejects only an exactly repeated /Prev offset, so a chain of unique offsets still recurses without bound; and linearize_page_tree follows /Kids with no visited set or depth limit at all, which the earlier fix never touched. A cycle-detection patch that checks for the repeat of a single value rather than membership in a set is a recognisable shape, and it survived until something re-read the function.
The upgrade is the problem
Here is the operational finding, and it is the reason this batch is harder than its severity distribution suggests.
Every one of the 38 CVEs is fixed in 13.0.0 and only in 13.0.0, released 16 October 2026 at 22:37:44Z. We checked PyPI directly: the only releases in the relevant range are 12.3.0 and 13.0.0. There is no 12.3.1. And 13.0.0 is a major version whose removals, read from the release notes, include “Drop support for Python 3.10”. We confirmed this in the packaging metadata rather than the changelog prose: requires-python reads >=3.10 at tag 12.3.0 and >=3.11 at tag 13.0.0.
So for anyone still on Python 3.10, the security update is not an update. pip install --upgrade pillow resolves to 12.3.0 and reports success, and the resolver is behaving correctly while leaving thirty-eight CVEs in place, including a CVSS 9.8 memory-corruption issue. There is no warning in that transaction. The same release also removes the undocumented GifImagePlugin._save_netpbm, removes ExifTags.IFD.Makernote, and deprecates the IM image format, ImageQt.align8to32() and JpegImageFile.load_djpeg — an ordinary major-version clear-out that is now coupled to the only path off a critical.
This is not a complaint about Pillow’s release engineering so much as an observation about what happens when a disclosure programme operating at machine throughput meets a project on a fixed quarterly calendar. The findings arrived in a volume that made a point release impractical; the release train was already carrying a major version; the two merged. The cost lands on the conservative part of the install base, which is the same group it landed on in the Gitea 28.2.0 batch last week and for the same structural reason.
Credit where it is due, and it is a real contrast with Gitea: the Pillow 13.0.0 release notes carry a proper Security section, grouped by severity, with each entry naming its GHSA and, where assigned, its CVE, above the line “CVE identifiers will be added to these entries as they are assigned.” The fixes are not hidden under Bug Fixes. An operator who reads the release notes learns what happened.
The three advisories with no CVE
Three of the 41 carry no CVE identifier, which means that for any scanner keyed on CVE numbers they do not exist. Two are rated high.
GHSA-78g4-xqwv-q3g8 is the most interesting thing in the batch and it is not a Pillow bug at all. Every Pillow wheel from 11.1.0 onward bundles a libopenjp2 containing a heap-buffer-overflow write (OSV-2025-219) that is reachable from Image.open(fp) plus im.load() with default settings. The reporter states the inversion that makes it easy to miss, and we quote it because it is the generalisable part: “OpenJPEG 2.5.2 and earlier are safe. The bug was introduced in 954c6e3c (2024-06-25), so keeping the dependency current is what creates the exposure.” Pillow 10.4.0 and 11.0.0 bundled 2.5.2 and are unaffected; 11.1.0 through 11.3.0 bundle 2.5.3 and 12.0.0 through 12.3.0 bundle 2.5.4, and both are affected. Because the wheel carries its own copy, a patched host OS does not help.
The defect is in opj_j2k_read_sod, where tp_index is indexed by a value taken directly from the one-byte TPsot field of the SOT marker with neither a null check nor a bounds check — while opj_j2k_add_tlmarker, a few lines later in the same file, writes the same array at the same index behind a correct if (tp_index && l_current_tile_part < nb_tps) guard. One writer checked, its sibling not. It is fixed upstream in 91d08b11 dated 10 February 2026, and the reporter’s measurement is that the newest OpenJPEG tag remains v2.5.4 from 20 September 2025, with the fix branch ahead 6, behind 0 — in no release, eight months on. The reporter’s stated reason for filing against Pillow rather than only upstream is the whole supply-chain argument in one sentence: “Pillow is where the vulnerable binary actually reaches users.” We filed the same shape when Trail of Bits looked at repository integrity across 25 package managers: the artefact a user installs and the source tree a scanner reads are different objects, and the binary wins.
The third, GHSA-654x-cwwv-5j9c, is the one to watch. Published 19 October at 09:47:19Z — three days after 13.0.0 shipped — it records that exporting an image to Arrow with PILLOW_ALIGNMENT set above 1 returns uninitialised heap memory mixed into the pixel data, which can include pixels from images the process freed earlier. Its affected range reads 11.2.1 – 13.0.0 and its patched-versions field is empty. In a multi-tenant service that exports images, one user’s output can contain another user’s pixels, and upgrading to 13.0.0 does not fix it. The reporter notes a patch is ready and characterises PILLOW_ALIGNMENT as obscure configuration, which it is — but “obscure” is a statement about defaults, not about the fleets that set it.
The pattern across the memory-safety findings
Read as a set rather than as 41 tickets, the batch has one dominant theme, and it is not parsing untrusted input. It is the encoder and the writer. CVE-2026-112393 (heap overflow in _tiffWriteProc when saving to a BytesIO, scored 9.2 CVSS 4.0 at NVD, with file paths and real file descriptors explicitly unaffected), CVE-2026-112896 (TIFF encoder ColorMap plane-size mismatch), CVE-2026-96778 (the JPEG comment-after-ICC overflow), CVE-2026-112891 (quadratic GIF comment copying during save), GHSA-xvh5-g6gv-559v (libtiff encoder memory disclosure from crafted tags, no CVE), and the Arrow export leak are all defects on the way out.
That inverts the usual threat model. A great deal of defensive engineering around image handling assumes the dangerous moment is Image.open() on a file from a stranger, and that having validated and re-encoded the image you are now holding something safe. Several of these say the re-encode is the dangerous step, and two of them — the TIFF write and the Arrow export — are reachable only when the destination is an in-memory buffer rather than a file on disk. An in-memory buffer is what a web service, a model-serving pipeline and a document converter all use. The configuration that avoids the disk is the configuration that is exposed.
The other recurring shape is a limit that runs after the allocation it is meant to prevent. CVE-2026-112400 stores a rejected frame size on the image object before calling the decompression-bomb check and never restores it, so a roughly 60-byte GIF leaves im.size reporting dimensions up to 131070×131070 while the pixel buffer is still a 1×1 first frame, and the next Image.new() or rotate(expand=True) sizes itself from the stale value. CVE-2026-112899 lets EpsImageFile.load(scale=N) hand Ghostscript a geometry N² times the declared size, checking the bomb limit only after gs has rendered the full raw image to a temporary file. CVE-2026-112399 allocates a full APNG canvas before the bomb check. The check exists in all three. It is in the wrong place in all three, and it was in the wrong place for years — the GIF ordering has been there since the bomb check was added in June 2022 for CVE-2022-45198.
What to do
Upgrade to Pillow 13.0.0, and verify the version that actually resolved rather than the command’s exit status. If the answer is 12.3.0, the blocker is your Python, not your pin: 13.0.0 requires Python 3.11 or newer and there is no backport to receive. Treat the Python upgrade as the security work, because it is.
If you cannot move off Python 3.10 immediately, the mitigations are narrow but real and they come from the advisories themselves. Pass a formats= allowlist to Image.open() — it is the only documented mitigation for the XBM ReDoS and it removes several other plugins from reach at the same time. Uninstall olefile unless you genuinely need FlashPix or MIC support: it is an optional dependency and its absence takes the FPX code-execution path and the MIC infinite loop off the table. If you do not render EPS, ensure Ghostscript is not installed in the image. Prefer saving to a file path over a BytesIO where you have the choice, which the TIFF advisory states explicitly takes CVE-2026-112393 out of scope. If you set PILLOW_ALIGNMENT above 1 and export to Arrow, unset it — that one has no fixed version in any release.
For the bundled JPEG 2000 library, note that your host distribution’s patched libopenjp2 is irrelevant to a wheel install, and that no Pillow release currently resolves it: main still pinned 2.5.4 as of 6 October per the advisory, and the upstream fix is in no OpenJPEG release. If you do not open JPEG 2000 files, an allowlist excluding JPEG2000 is the available control.
Finally, the scanner-coverage point. Three of these advisories have no CVE and one has no fixed version, so a CVE-keyed scanner reporting your Pillow as clean after a 13.0.0 upgrade is giving you an accurate answer to a narrower question than the one you asked. Query the GitHub advisory database by package rather than the CVE feed.
None of the 38 CVEs is in the CISA KEV catalogue (checked directly, version 2026.10.16, 1,748 entries, zero hits), and no exploitation has been reported for any of them.
The part worth arguing about
We have written about Claude-discovered vulnerabilities before — the Podman CVSS 10.0 sandbox bypass that turned out to be a 2022 feature, and Anthropic’s cyber mission and its open-source scanner — and the honest position has not changed: the findings are real, the triage claim is 92.7% by the discloser’s own measure, and the quality of the sixteen ANT write-ups here is high. They cite file and line, they name the introducing commit, they state what the reproducer demonstrated and what it did not, and CVE-2026-112895 correctly identifies itself as someone else’s incomplete fix. That is better than a great deal of human advisory prose.
The unresolved question is not whether the findings are good. It is that a disclosure pipeline that can produce 29,439 triaged findings is now pointed at maintainer teams whose release cadence is quarterly and whose headcount is small. Pillow absorbed 41 advisories in 48 hours and shipped a real security section, which is close to a best case. The visible cost is that the fix is bound to a major version, three findings never got an identifier, and one arrived after the release and is still open. Multiply that across 591 projects and the scarce resource is not vulnerability discovery. It is the ability of a maintainer to ship a patch release that an operator on an old interpreter can actually install.
Our verification was read-only. The 41 advisories, their GHSA identifiers, CVE assignments, severities, CVSS vectors and scores, affected and patched version ranges, publication timestamps, CWE assignments, credits and full bodies were enumerated and read first-hand from the GitHub repository security-advisory API, deduplicated to 61 total advisories of which 41 published on or after 17 October 2026; the counts of 2 critical, 13 high, 23 medium and 3 low, the 38 distinct CVE identifiers, the 16 ANT-2026- identifiers and the 3 advisories without a CVE were computed from that set, not quoted. All quoted advisory prose, file paths, line numbers, C and Python source, and the OpenJPEG commit hashes are verbatim from those advisory bodies. CVE-2026-112390 through 112395 publication timestamps, vulnStatus, source identifier, descriptions, CVSS 4.0 and 3.1 vectors and CWEs were read first-hand from the NVD 2.0 API. The 13.0.0 release body, publish timestamp of 2026-10-16T22:37:44Z, the full release list and repository metadata (13,904 stars, 2,543 forks, Python, created 2012-07-24) came from the GitHub API; the Security section of the release notes was read directly from the published documentation. The requires-python values of >=3.10 at tag 12.3.0 and >=3.11 at tag 13.0.0 were read from pyproject.toml at each tag, and the absence of any 12.3.x release from the PyPI JSON API. The Anthropic disclosure figures — 6,157 vulnerabilities, 591 projects, 516 patched, 584 identifiers comprising 219 CVE records and 365 GHSAs, 29,439 triaged findings, 6,123 candidates, 5,674 externally reviewed, 92.7% true positives, last updated 2026-10-02 19:47 UTC — were read directly from Anthropic’s coordinated vulnerability disclosure dashboard, and the quoted programme description likewise. KEV status was checked directly against the published catalogue. Brave Search was used for discovery only and returned nothing but automated CVE mirrors with no independent technical analysis of any record in the batch. We installed nothing, built no wheel, crafted no image file, and executed no proof of concept; every reproduction measurement cited is the reporter’s, attributed as such.
Sources:
- Pillow — repository security advisories (41 published 17–19 October 2026; 38 CVE identifiers)
- Pillow 13.0.0 release (16 October 2026, 22:37:44Z; removals include dropping Python 3.10)
- Pillow 13.0.0 release notes — Security section, grouped by severity with GHSA and CVE references
- GHSA-v9pv-5gc8-w2m6 / CVE-2026-96639 — stale native image storage reused after mode transitions (critical, CVSS 3.1 9.8)
- GHSA-5x8w-8jj5-q52h / CVE-2026-112398 — FlashPix JPEG heap overflow leading to code execution (critical; researcher Alex Bouffard)
- GHSA-78g4-xqwv-q3g8 — bundled libopenjp2 in every wheel since 11.1.0 carries OSV-2025-219 (high, no CVE)
- GHSA-654x-cwwv-5j9c — Arrow export leaks stale heap memory when PILLOW_ALIGNMENT > 1 (affected through 13.0.0; no patched version)
- GHSA-p4wg-p73j-c528 / CVE-2026-112895 — unbounded PDF recursion, stated as an incomplete fix for CVE-2026-42310
- NVD — CVE-2026-112393 record (published 2026-10-19T21:17:07.613Z, CVSS 4.0 9.2, CWE-122)
- Anthropic — coordinated vulnerability disclosure dashboard (6,157 disclosures across 591 projects; last updated 2 October 2026)
- CISA — Known Exploited Vulnerabilities catalogue (no record in the batch listed; checked 19 October 2026, 1,748 entries)
- al-ice.ai — Gitea 28.2.0 and twelve CVEs under one commit subject (18 October 2026)
- al-ice.ai — the Podman CVSS 10.0 sandbox bypass that was a 2022 feature, found by Claude
- al-ice.ai — Anthropic’s cyber mission and unreviewed model output for open source
- al-ice.ai — Trail of Bits on repository integrity across 25 package managers (18 October 2026)
- al-ice.ai — thirty-eight Camel advisories in one morning and a CVSS 10.0 already fixed (19 October 2026)