---
id: "CVE-2026-114501"
kind: "vulnerability"
aliases: ["CVE-2026-114501", "GHSA-654x-cwwv-5j9c"]
title: "Pillow: Arrow export leaks stale heap memory when PILLOW_ALIGNMENT > 1"
severity: "medium"
cvss: 6.3
epss: 0.00384
exploited: false
status: "open"
category: "package"
products: [{"name": "Pillow", "fixed": ["13.0.0"], "affected": ">= 11.2.1, < 13.0.0", "ecosystem": "pypi"}]
ecosystems: ["pypi"]
tags: ["cwe-226", "cwe-908", "multi-tenant-image-services", "package", "pillow", "python"]
not_affected_if: ["no-arrow-export", "pillow-13-or-later", "pillow_alignment-unset"]
published_at: "2026-10-21T22:16:49+00:00"
first_seen_at: "2026-10-23T21:56:55+00:00"
updated_at: "2026-10-23T21:57:29+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-654x-cwwv-5j9c", "first_seen_at": "2026-10-23T21:56:55+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-114501", "first_seen_at": "2026-10-23T21:57:29+00:00"}]
---

# Pillow: Arrow export leaks stale heap memory when PILLOW_ALIGNMENT > 1

Pillow's Arrow export returns uninitialized heap memory mixed into image pixel data when the PILLOW_ALIGNMENT environment variable is set above 1. That memory can contain pixels from images the process freed earlier, so a caller can read another image's data.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- pypi:Pillow affected >= 11.2.1, < 13.0.0 fixed 13.0.0

## How to check

Run: python -c "import os; print(os.environ.get('PILLOW_ALIGNMENT'))" in the service environment; a value above 1 plus Arrow export (Image.fromarrow / to Arrow) means exposure.
Check installed version: pip show pillow.

## Fix

Upgrade Pillow to 13.0.0 or later (pip install -U 'pillow>=13.0.0').
As an interim step, unset PILLOW_ALIGNMENT or keep it at 1.

## Timeline

- 2026-10-23T21:56:55+00:00 ghsa https://github.com/advisories/GHSA-654x-cwwv-5j9c
- 2026-10-23T21:57:29+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-114501

## From the sources

### Summary

When [`PILLOW_ALIGNMENT`](https://pillow.readthedocs.io/en/stable/reference/block_allocator.html#memory-pools) is set above 1, exporting an image to Arrow returns uninitialized heap memory mixed in with the pixel data. That memory can include pixels from images the process freed earlier. 

### Details

With an alignment above 1, the default allocator pads each row up to `aligned_linesize` and starts the first row at an aligned offset into the block. It then exports `im->blocks[0].ptr` as `xsize * ysize` tightly packed pixels, so exported data includes the padding bytes, and can include a few bytes from before the first row.

Tested vulnerable on 11.2.1, 12.3.0, 4c1ed27a5.

### PoC

Whether things leak may depend on allocator configuration. This does show a leak on my Mac.

```python
import os

os.environ["PILLOW_ALIGNMENT"] = "64"

from PIL import Image

# Image A: 64 px wide, so its rows fill the whole aligned stride
a = Image.new("L", (64, 40), 0x41).transpose(Image.Transpose.FLIP_LEFT_RIGHT)
del a

# Image B: 61 px wide, so each row has 3 bytes of padding; its pixels are all 0
b = Image.new("L", (61, 40), 0).transpose(Image.Transpose.FLIP_LEFT_RIGHT)

try:
    exported = Image.fromarrow(b, b.mode, b.size).tobytes()
except ValueError as e:
    print("Not vulnerable:", e)
else:
    if leaked := len(exported) - exported.count(0):
        raise RuntimeError(f"{leaked} of {len(exported)} exported bytes are not B's pixels")
```

### Impact

Information disclosure for applications that set `PILLOW_ALIGNMENT` and export images to Arrow. Likely any image whose row length isn't a multiple of the alignment will do. In a service handling several users' images, one user's exported data can contain pixels from another user's image, or other stale heap contents.

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
