{"id":"GHSA-cp9v-662q-vvx9","summary":"Pillow: NULL pointer dereference in Image.merge via an unvalidated band 0","details":"### Summary\n`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.\n\n### Details\nThe validation loop skips band 0:\n\n- `src/PIL/Image.py:3795` iterates `for band in bands[1:]` and checks mode and size only for those.\n\nFor an `I` or `F` image the 8-bit plane `image8` is NULL (only `image32` is set). `ImagingMerge` dereferences `bands[0]-\u003eimage8[y]` for the multi-band output modes:\n\n- `src/libImaging/Bands.c:302`, `:311`, `:321`\n\nThe IPTC loader builds the band list from an attacker-typed sub-image and passes it to merge with the sub-image at index 0:\n\n- `src/PIL/IptcImagePlugin.py:156-160`\n- `src/PIL/IptcImagePlugin.py:169` registers the opener with no accept function.\n\n### PoC\n```python\nfrom PIL import Image\n\n# Band 0 is a single-band \"I\" image: its image8 plane is NULL. merge() validates only\n# bands[1:], so the native ImagingMerge dereferences the NULL plane for band 0.\nband0 = Image.new(\"I\", (4, 4))\nImage.merge(\"RGB\", [band0, Image.new(\"L\", (4, 4)), Image.new(\"L\", (4, 4))])\n```\n\nRun it:\n```\n$ python 02_poc.py\nSegmentation fault (core dumped)\n```\n\nObserved on a source build of commit `3e2ab96`.\n\nUnder AddressSanitizer:\n```\n==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000\n    #0 ImagingMerge   src/libImaging/Bands.c:311\n    #1 _merge         src/_imaging.c:2492\nSUMMARY: AddressSanitizer: SEGV src/libImaging/Bands.c:311 in ImagingMerge\n```\nThe null address confirms the dereference of the missing `image8` plane for band 0.\n\n### Impact\nNULL 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.","aliases":["CVE-2026-112405"],"modified":"2026-10-21T20:45:04.636143652Z","published":"2026-10-21T20:30:25Z","database_specific":{"github_reviewed_at":"2026-10-21T20:30:25Z","nvd_published_at":"2026-10-19T22:17:08Z","cwe_ids":["CWE-476"],"severity":"HIGH","github_reviewed":true},"references":[{"type":"WEB","url":"https://github.com/python-pillow/Pillow/security/advisories/GHSA-cp9v-662q-vvx9"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112405"},{"type":"WEB","url":"https://github.com/python-pillow/Pillow/commit/b6ee66e8b2c2c0932bdf65f963268d9f2927ef86"},{"type":"PACKAGE","url":"https://github.com/python-pillow/Pillow"},{"type":"WEB","url":"https://github.com/python-pillow/Pillow/releases/tag/13.0.0"}],"affected":[{"package":{"name":"pillow","ecosystem":"PyPI","purl":"pkg:pypi/pillow"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"12.0.0"},{"fixed":"13.0.0"}]}],"versions":["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"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"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"}]}