Skip to content
Heist/ feed
Back to feed
HighFix available

Pillow: Heap use-after-free when encoding TIFF to an in-memory buffer

Type: advisorySources: OSV2 source records

### 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 th

Products
pillow
Affected versions
11.3.0, 12.0.0, 12.1.0, 12.1.1, 12.2.0, 12.3.0
Fixed versions
13.0.0
CVEs
CVE-2026-112396
Confirmed fixed CVEs
Not specified
Updated in this feed
1 Nov 2026, 17:55 UTC · Revision 2

Source evidence

Pillow: Heap use-after-free when encoding TIFF to an in-memory buffer

core-osv · Original source

Published 21 Oct 2026, 21:24 UTC

### 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 th

osv:GHSA-4rr5-89j8-mwwm

pillow

Fixed versions: 13.0.0

Complete source record
{
  "id": "b89ffa4f99c291a368555973cd6a6da6b515a438f647097c609413acd3220c90",
  "url": "https://osv.dev/vulnerability/GHSA-4rr5-89j8-mwwm",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "Pillow: Heap use-after-free when encoding TIFF to an in-memory buffer",
    "summary": "### 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 th",
    "evidence": "osv:GHSA-4rr5-89j8-mwwm",
    "products": [
      {
        "name": {
          "value": "pillow",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "13.0.0",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": [
          {
            "value": "11.3.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.0.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.1.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.1.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.2.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.3.0",
            "evidence": "/affected/0/versions"
          }
        ]
      }
    ],
    "severity": "medium",
    "structured": {
      "id": "GHSA-4rr5-89j8-mwwm",
      "aliases": [
        "BIT-pillow-2026-112396",
        "CVE-2026-112396",
        "GHSA-4rr5-89j8-mwwm",
        "PYSEC-2026-4328"
      ],
      "metrics": [
        {
          "type": "CVSS_V3",
          "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "ECOSYSTEM",
              "events": [
                {
                  "introduced": "11.3.0"
                },
                {
                  "fixed": "13.0.0"
                }
              ]
            }
          ],
          "package": {
            "name": "pillow",
            "purl": "pkg:pypi/pillow",
            "ecosystem": "PyPI"
          },
          "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"
          }
        }
      ],
      "modified": "2026-10-27T11:10:49.694224501Z",
      "provider": "osv",
      "references": [
        "https://github.com/python-pillow/Pillow/security/advisories/GHSA-4rr5-89j8-mwwm",
        "https://nvd.nist.gov/vuln/detail/CVE-2026-112396",
        "https://github.com/python-pillow/Pillow/commit/dc95da20207827a062cfbf551ee9f6031465a8bb",
        "https://github.com/python-pillow/Pillow",
        "https://github.com/python-pillow/Pillow/releases/tag/13.0.0"
      ],
      "provider_metadata": {
        "cwe_ids": [
          "CWE-416"
        ],
        "severity": "MODERATE",
        "github_reviewed": true,
        "nvd_published_at": "2026-10-19T22:17:06Z",
        "github_reviewed_at": "2026-10-21T21:24:19Z"
      }
    },
    "related_cves": [
      {
        "value": "CVE-2026-112396",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/severity/0/score"
  },
  "source_id": "core-osv",
  "document_id": 461508,
  "published_at": "2026-10-21T21:24:19+00:00"
}

PYSEC-2026-4328

core-osv · Original source

Published 19 Oct 2026, 22:17 UTC

Pillow is a Python imaging library. From 11.3.0 until 13.0.0, Pillow has a heap use-after-free when libtiff rejects a tag while encoding TIFF data to an in-memory buffer. When an application saves attacker-influenced TIFF tags to BytesIO, libtiff can reject a tag during encoding in ImagingLibTiffEncode; the error path then frees the output buffer without closing or clearing the TIFF handle, and later cleanup flushes through the write callback into freed memory, which can crash the process and may enable code execution. This issue is fixed in version 13.0.0.

osv:PYSEC-2026-4328

pillow

Fixed versions: 13.0.0

Complete source record
{
  "id": "b83f1b49eb85bc84265ff94313c51d5ae983248d1ed15a7a107631d63b31878f",
  "url": "https://osv.dev/vulnerability/PYSEC-2026-4328",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "PYSEC-2026-4328",
    "summary": "Pillow is a Python imaging library. From 11.3.0 until 13.0.0, Pillow has a heap use-after-free when libtiff rejects a tag while encoding TIFF data to an in-memory buffer. When an application saves attacker-influenced TIFF tags to BytesIO, libtiff can reject a tag during encoding in ImagingLibTiffEncode; the error path then frees the output buffer without closing or clearing the TIFF handle, and later cleanup flushes through the write callback into freed memory, which can crash the process and may enable code execution. This issue is fixed in version 13.0.0.",
    "evidence": "osv:PYSEC-2026-4328",
    "products": [
      {
        "name": {
          "value": "pillow",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "13.0.0",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": [
          {
            "value": "11.3.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.0.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.1.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.1.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.2.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "12.3.0",
            "evidence": "/affected/0/versions"
          }
        ]
      }
    ],
    "severity": "high",
    "structured": {
      "id": "PYSEC-2026-4328",
      "aliases": [
        "BIT-pillow-2026-112396",
        "CVE-2026-112396",
        "GHSA-4rr5-89j8-mwwm",
        "PYSEC-2026-4328"
      ],
      "metrics": [
        {
          "type": "CVSS_V3",
          "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "ECOSYSTEM",
              "events": [
                {
                  "introduced": "11.3.0"
                },
                {
                  "fixed": "13.0.0"
                }
              ]
            }
          ],
          "package": {
            "name": "pillow",
            "purl": "pkg:pypi/pillow",
            "ecosystem": "PyPI"
          },
          "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/pypa/advisory-database/blob/main/vulns/pillow/PYSEC-2026-4328.yaml"
          },
          "ecosystem_specific": {}
        }
      ],
      "modified": "2026-10-27T11:10:49.694224501Z",
      "provider": "osv",
      "references": [
        "https://github.com/python-pillow/Pillow/releases/tag/13.0.0",
        "https://github.com/python-pillow/Pillow/commit/dc95da20207827a062cfbf551ee9f6031465a8bb",
        "https://github.com/python-pillow/Pillow/security/advisories/GHSA-4rr5-89j8-mwwm"
      ]
    },
    "related_cves": [
      {
        "value": "CVE-2026-112396",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/severity/0/score"
  },
  "source_id": "core-osv",
  "document_id": 461509,
  "published_at": "2026-10-19T22:17:06.837+00:00"
}