Friday 23 October 2026
Security

Pillow heap overflow strikes TIFF saves to BytesIO

A flaw in Pillow's libtiff write callback lets crafted TIFF uploads overwrite heap memory or leak stale bytes when saved to a BytesIO, with no special save options; fixed in Pillow 13.0.0.

Illustration: Pillow software logo on screen
Illustration: Five Nines

Pillow's libtiff in-memory write callback contains a heap buffer overflow and a stale heap byte disclosure when saving TIFF images to non-file targets such as io.BytesIO, according to GitHub advisory GHSA-332p-7hrc-92wf [1]. The advisory identifies CVE-2026-112393 and gives no CVSS score [1].

The flaw lives in _tiffWriteProc in src/libImaging/TiffDecode.c, introduced in commit e782fe721e01 and present since Pillow 2.0.0 [1]. The advisory's references point to fix commit 11582e5 and release 13.0.0 [1].

The write callback sizes its realloc from state->size plus the incoming write size, not from the current write position [1]. While writing the directory, libtiff seeks beyond the data already written and places tag values there; the undersized buffer lets the first out-of-line tag value run beyond the heap block by as much as the directory size [1]. The overflow writes the tail of an attacker-chosen tag value from the uploaded file [1]. No save options are required, since compression and tag values carry over from the source image [1].

A person who can hand an image to a service that opens it and re-saves it as TIFF through a memory sink can trigger this under default settings [1]. The included reproducer aborts the process with glibc's "malloc(): corrupted top size" [1]. The advisory says code execution is not demonstrated in the reproducer, but it describes a separate proof of concept that, with two addresses leaked by an unrelated information disclosure, ran an attacker-chosen shell command against a stock build that had ASLR, PIE, and NX enabled, using default settings [1].

The overflow only occurs if an attacker-controlled out-of-line tag value reaches libtiff [1]. Directly saving the opened TIFF makes that happen through its ImageDescription, DocumentName or XMP tag, or via an embedded ICC profile that survives copy() [1]. For single-band images the first out-of-line value can be DocumentName or ImageDescription, and the number of tags sets the overrun length, around 12 bytes per directory entry [1]. The advisory reports an observed 2,384-byte overflow with a 331-entry IFD [1].

The write callback's seek routine does not constrain the location, so libtiff can seek past the end of data [1]. The overflow happens because the buffer growth loop uses state->size instead of state->loc, so the first write after a seek can land past the allocation [1]. The source code walk-through in the advisory shows that with a crafted PackBits TIFF, libtiff seeks to 65,662 and writes a 65,536-byte description while the buffer has only 131,072 bytes, leaving 126 bytes past the end [1].

The same code also leaks stale heap memory into the saved file: any compressed TIFF over 64 KiB written to a BytesIO-like target can disclose one byte per alignment hole at predictable offsets, without a crafted tag [1]. The advisory's tests with MALLOC_PERTURB_=170 show 0x55 bytes at four offsets in a 369,463-byte BytesIO output where a file save has zeros [1]. File-path and real-fd targets are unaffected by both issues [1].

Administrators should upgrade to Pillow 13.0.0 or apply commit 11582e5 [1].

Lena Vasquez

Lena Vasquez covers vulnerabilities, breaches and identity for Five Nines. She writes from advisories, vendor bulletins and published reporting.

Corrections. Report an error to corrections@thefivenines.com. We list substantive corrections on the corrections page.