Pillow Heap Buffer Overflow in JPEG Decoding via thumbnail() on Lossless JPEGs (CVE-2026-112403)
What happened
A GitHub Security Advisory (GHSA-8g5g-8vmr-9782) discloses a heap buffer overflow in Pillow's JPEG decoder, found by Anthropic using Claude and validated by Ada Logics. The root cause: Image.thumbnail() automatically calls JpegImageFile.draft(), which asks libjpeg to decode at a reduced scale (1/2, 1/4, or 1/8) and shrinks the output buffer accordingly. However, libjpeg-turbo 3.x silently ignores the requested scale for lossless (SOF3) JPEG streams and returns full-resolution scanlines instead. Pillow's decoder never checks libjpeg's actual output dimensions against the buffer it allocated, so each scanline write overflows the undersized heap buffer with attacker-controlled pixel bytes.
The advisory notes the trigger file is a fully standards-compliant JPEG — Pillow's own parser already accepts SOF3 ("spatial lossless") markers at 8-bit precision — so format allowlisting does not prevent it. The reproducer demonstrates the overflow under AddressSanitizer against Pillow's main branch (and reportedly against 12.3.0 as well).
Why it matters
Pillow is one of the most widely used image libraries in the Python ecosystem, and Image.thumbnail() is a common, often default, code path for handling user-uploaded images (galleries, avatars, previews, document pipelines). Any application that opens untrusted JPEG input and calls thumbnail() — which triggers draft() implicitly — on an image large enough to trigger 2x+ scaling (roughly 1024px+ on a side for a 256px thumbnail target) is potentially exposed to a heap overflow with attacker-influenced content, provided the deployment links against libjpeg-turbo 3.x, which is what official Pillow wheels bundle. The exploit status is noted as PoC-public.
What defenders should do now
- Inventory services that call
Image.thumbnail()(or otherwise rely ondraft()) on untrusted JPEG uploads — this is the only affected path; plainopen().load()orresize()are not impacted. - Check Pillow and libjpeg-turbo versions in image-processing services and container base images; watch for an official Pillow patch and apply it once released.
- As an interim mitigation, consider calling
img.load()beforethumbnail(), or passingreducing_gap=Nonetothumbnail(), to avoid the draft-scaling code path on untrusted input. - Where feasible, run image-processing workers with memory-safety hardening (e.g., sandboxing, seccomp, or decoding in an isolated/ephemeral process) given the heap-corruption nature of the bug.
- Hunt for crash/error telemetry from image-processing pipelines (segfaults, unexpected
OSErrorduring JPEG decode) that could indicate exploitation attempts, though the advisory notes the visibleOSErroris a downstream symptom, not the overflow itself.
Developing intel
This is a same-day advisory and the underlying CVE (CVE-2026-112403) is net-new; a vendor patch timeline was not confirmed at the time of writing. Details may evolve as Pillow maintainers respond. See the original advisory for full technical detail: GHSA-8g5g-8vmr-9782.