Skip to content
Heist/ feed
Back to feed
MediumFix available

Pillow tiled-TIFF decoder reads past tile buffer in copy4 (Unpack.c:1290)

Type: advisorySources: OSV

### Summary Pillow's libImaging `_decodeTile` decoder, used to decode tiled TIFF images, allocates a per-tile buffer using `realloc(state->buffer, TIFFTileSize(tiff))`. After each `TIFFReadTile` it then iterates `tile_y` from 0 up to `current_tile_length` and calls the configured row unpacker (here `copy4`) with the source pointer `state->buffer + tile_y * row_byte_size`. When libtiff returns a `tile_bytes_size` smaller than the worst-case `row_byte_size * tile_length`, the inner stride-and-copy loop walks past the realloc'd region and `copy4` performs a 1024-byte `memcpy` that reads up to `(tile_length-1) * row_byte_size + row_byte_size` bytes from a buffer sized only to `tile_bytes_size`. The supplied PoC TIFF triggers a 1024-byte over-read exactly 0 bytes past a 196608-byte allocation. An attacker who can convince a Pillow-using application to decode a crafted TIFF can trigger a process crash (DoS) and may cause adjacent heap memory to be copied into the decoded pixel buffer. ### Details Pillow's TIFF decoder, when libtiff reports the file is tiled, takes the `_decodeTile` path. The function: 1. Asks libtiff for `TIFFTileSize(tiff)` (the bytes-per-tile) and `TIFFTileRowSize(tiff)` (the bytes per row *as stored by libtiff*). 2. Reallocates `state->buffer` to exactly `tile_bytes_size`. 3. For each tile origin `(x, y)` and each plane, calls `TIFFReadTile` into `state->buffer`, then walks the tile row by row, calling the configured `ImagingShuffler` (here `copy4`) on each row'

Products
pillow
Affected versions
10.0.0, 10.0.1, 10.1.0, 10.2.0, 10.3.0, 10.4.0, 11.0.0, 11.1.0, 11.2.1, 11.3.0, 12.0.0, 12.1.0, 12.1.1, 12.2.0, 12.3.0, 8.2.0, 8.3.0, 8.3.1, 8.3.2, 8.4.0, 9.0.0, 9.0.1, 9.1.0, 9.1.1, 9.2.0, 9.3.0, 9.4.0, 9.5.0
Fixed versions
13.0.0
CVEs
CVE-2026-112395
Confirmed fixed CVEs
Not specified
Updated in this feed
1 Nov 2026, 10:37 UTC · Revision 1

Source evidence

Pillow tiled-TIFF decoder reads past tile buffer in copy4 (Unpack.c:1290)

core-osv · Original source

Published 20 Oct 2026, 20:49 UTC

### Summary Pillow's libImaging `_decodeTile` decoder, used to decode tiled TIFF images, allocates a per-tile buffer using `realloc(state->buffer, TIFFTileSize(tiff))`. After each `TIFFReadTile` it then iterates `tile_y` from 0 up to `current_tile_length` and calls the configured row unpacker (here `copy4`) with the source pointer `state->buffer + tile_y * row_byte_size`. When libtiff returns a `tile_bytes_size` smaller than the worst-case `row_byte_size * tile_length`, the inner stride-and-copy loop walks past the realloc'd region and `copy4` performs a 1024-byte `memcpy` that reads up to `(tile_length-1) * row_byte_size + row_byte_size` bytes from a buffer sized only to `tile_bytes_size`. The supplied PoC TIFF triggers a 1024-byte over-read exactly 0 bytes past a 196608-byte allocation. An attacker who can convince a Pillow-using application to decode a crafted TIFF can trigger a process crash (DoS) and may cause adjacent heap memory to be copied into the decoded pixel buffer. ### Details Pillow's TIFF decoder, when libtiff reports the file is tiled, takes the `_decodeTile` path. The function: 1. Asks libtiff for `TIFFTileSize(tiff)` (the bytes-per-tile) and `TIFFTileRowSize(tiff)` (the bytes per row *as stored by libtiff*). 2. Reallocates `state->buffer` to exactly `tile_bytes_size`. 3. For each tile origin `(x, y)` and each plane, calls `TIFFReadTile` into `state->buffer`, then walks the tile row by row, calling the configured `ImagingShuffler` (here `copy4`) on each row'

osv:GHSA-462x-pgg6-55fp

pillow

Fixed versions: 13.0.0

Complete source record
{
  "id": "72c41622dc6d61a554fd9fe50df7465ee0d0030d765485d48ecfef165a86e17e",
  "url": "https://osv.dev/vulnerability/GHSA-462x-pgg6-55fp",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "Pillow tiled-TIFF decoder reads past tile buffer in copy4 (Unpack.c:1290)",
    "summary": "### Summary Pillow's libImaging `_decodeTile` decoder, used to decode tiled TIFF images, allocates a per-tile buffer using `realloc(state->buffer, TIFFTileSize(tiff))`. After each `TIFFReadTile` it then iterates `tile_y` from 0 up to `current_tile_length` and calls the configured row unpacker (here `copy4`) with the source pointer `state->buffer + tile_y * row_byte_size`. When libtiff returns a `tile_bytes_size` smaller than the worst-case `row_byte_size * tile_length`, the inner stride-and-copy loop walks past the realloc'd region and `copy4` performs a 1024-byte `memcpy` that reads up to `(tile_length-1) * row_byte_size + row_byte_size` bytes from a buffer sized only to `tile_bytes_size`. The supplied PoC TIFF triggers a 1024-byte over-read exactly 0 bytes past a 196608-byte allocation. An attacker who can convince a Pillow-using application to decode a crafted TIFF can trigger a process crash (DoS) and may cause adjacent heap memory to be copied into the decoded pixel buffer. ### Details Pillow's TIFF decoder, when libtiff reports the file is tiled, takes the `_decodeTile` path. The function: 1. Asks libtiff for `TIFFTileSize(tiff)` (the bytes-per-tile) and `TIFFTileRowSize(tiff)` (the bytes per row *as stored by libtiff*). 2. Reallocates `state->buffer` to exactly `tile_bytes_size`. 3. For each tile origin `(x, y)` and each plane, calls `TIFFReadTile` into `state->buffer`, then walks the tile row by row, calling the configured `ImagingShuffler` (here `copy4`) on each row'",
    "evidence": "osv:GHSA-462x-pgg6-55fp",
    "products": [
      {
        "name": {
          "value": "pillow",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "13.0.0",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": [
          {
            "value": "10.0.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "10.0.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "10.1.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "10.2.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "10.3.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "10.4.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "11.0.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "11.1.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "11.2.1",
            "evidence": "/affected/0/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"
          },
          {
            "value": "8.2.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "8.3.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "8.3.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "8.3.2",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "8.4.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "9.0.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "9.0.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "9.1.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "9.1.1",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "9.2.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "9.3.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "9.4.0",
            "evidence": "/affected/0/versions"
          },
          {
            "value": "9.5.0",
            "evidence": "/affected/0/versions"
          }
        ]
      }
    ],
    "severity": "medium",
    "structured": {
      "id": "GHSA-462x-pgg6-55fp",
      "aliases": [
        "BIT-pillow-2026-112395",
        "CVE-2026-112395",
        "GHSA-462x-pgg6-55fp"
      ],
      "metrics": [
        {
          "type": "CVSS_V3",
          "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:L"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "ECOSYSTEM",
              "events": [
                {
                  "introduced": "8.2.0"
                },
                {
                  "fixed": "13.0.0"
                }
              ]
            }
          ],
          "package": {
            "name": "pillow",
            "purl": "pkg:pypi/pillow",
            "ecosystem": "PyPI"
          },
          "versions": [
            "10.0.0",
            "10.0.1",
            "10.1.0",
            "10.2.0",
            "10.3.0",
            "10.4.0",
            "11.0.0",
            "11.1.0",
            "11.2.1",
            "11.3.0",
            "12.0.0",
            "12.1.0",
            "12.1.1",
            "12.2.0",
            "12.3.0",
            "8.2.0",
            "8.3.0",
            "8.3.1",
            "8.3.2",
            "8.4.0",
            "9.0.0",
            "9.0.1",
            "9.1.0",
            "9.1.1",
            "9.2.0",
            "9.3.0",
            "9.4.0",
            "9.5.0"
          ],
          "database_specific": {
            "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-462x-pgg6-55fp/GHSA-462x-pgg6-55fp.json"
          }
        }
      ],
      "modified": "2026-10-26T08:56:01.770928143Z",
      "provider": "osv",
      "references": [
        "https://github.com/python-pillow/Pillow/security/advisories/GHSA-462x-pgg6-55fp",
        "https://nvd.nist.gov/vuln/detail/CVE-2026-112395",
        "https://github.com/python-pillow/Pillow/commit/76869efdbbf2ad7dccd7536520fb61b7a00ab61e",
        "https://github.com/python-pillow/Pillow",
        "https://github.com/python-pillow/Pillow/releases/tag/13.0.0"
      ],
      "provider_metadata": {
        "cwe_ids": [
          "CWE-122"
        ],
        "severity": "MODERATE",
        "github_reviewed": true,
        "nvd_published_at": "2026-10-19T21:17:07Z",
        "github_reviewed_at": "2026-10-20T20:49:26Z"
      }
    },
    "related_cves": [
      {
        "value": "CVE-2026-112395",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/severity/0/score"
  },
  "source_id": "core-osv",
  "document_id": 446808,
  "published_at": "2026-10-20T20:49:26+00:00"
}