{"id":"CVE-2026-112397","kind":"vulnerability","aliases":["CVE-2026-112397","GHSA-58r4-6rv3-gfh8"],"related":[],"title":"Pillow: Out-of-bounds access in _imagingmorph on a zero-width image","description":"### Summary\nA PPM or PGM file can declare a width of 0, which Pillow accepts, producing an image of size `(0, N)`. Passing that image to `ImageMorph.MorphOp.apply()` makes the native morphology code index a row buffer at `outrow[width - 1]`, which is `outrow[-1]` when the width is 0. The process crashes.\n\n### Details\nThe PPM parser reads width and height with no lower-bound check:\n\n- `src/PIL/PpmImagePlugin.py:119`\n\nThe native `apply()` writes the row edges with no guard that the width is positive:\n\n- `src/_imagingmorph.c:81` sets `outrow[0] = outrow[width - 1] = 0;`\n\n`match()` and `get_on_pixels()` use the same edge-indexing pattern.\n\n### PoC\n```python\nfrom PIL import Image, ImageMorph\n\n# A width-0 image (also producible from a crafted PPM header). apply() indexes\n# outrow[width - 1] == outrow[-1] in native code.\nim = Image.new(\"L\", (0, 5))\nImageMorph.MorphOp(op_name=\"dilation4\").apply(im)\n```\n\nRun it:\n```\n$ python 03_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 (pc ... in apply)\n    #0 apply   src/_imagingmorph.c:81\nSUMMARY: AddressSanitizer: SEGV src/_imagingmorph.c:81 in apply\n```\nWith width 0 the `outrow[width - 1]` write lands on an unmapped page, so it surfaces as a SEGV pinned to the edge-write line rather than a redzone overflow.\n\n### Impact\nOut-of-bounds write in a C extension. Affects any application that runs a morphology operation on an image opened from untrusted input. Result is a crash and possible memory corruption.","summary":"Pillow's native morphology code writes outside a buffer when it runs on a zero-width image, which a crafted PPM or PGM file can produce. An attacker who gets an application to open such a file and run ImageMorph on it crashes the process and may corrupt memory.","severity":"high","cvss":8.7,"epss":0.004360000137239695,"exploited":false,"exploited_at":null,"products":[{"name":"Pillow","fixed":["13.0.0"],"affected":"<= 12.3.0","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-787","image-processing","package","pillow","python"],"not_affected_if":["no-imagemorph-usage","pillow-13-or-later","trusted-input-only"],"how_to_check":"Run pip show pillow and check whether the version is 12.3.0 or lower.\nSearch your code for ImageMorph or MorphOp usage on images from untrusted input.","fix":"Upgrade Pillow to 13.0.0 or later.\nUntil then, reject images with width 0 before calling ImageMorph.MorphOp.apply().","status":"open","published_at":"2026-10-19T22:17:06+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T20:13:19+00:00","updated_at":"2026-10-23T21:57:19+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-23T20:13:19Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade Pillow to 13.0.0 or later.\nUntil then, reject images with width 0 before calling ImageMorph.MorphOp.apply().","summary":"Pillow's native morphology code writes outside a buffer when it runs on a zero-width image, which a crafted PPM or PGM file can produce. An attacker who gets an application to open such a file and run ImageMorph on it crashes the process and may corrupt memory.","category":"package","who_cares":["python","pillow","image-processing"],"confidence":0.8,"analyst_note":"","how_to_check":"Run pip show pillow and check whether the version is 12.3.0 or lower.\nSearch your code for ImageMorph or MorphOp usage on images from untrusted input.","needs_analyst":false,"not_affected_if":["pillow-13-or-later","no-imagemorph-usage","trusted-input-only"],"severity_if_unknown":"high"},"cost_usd":0.000423,"input_tokens":2630,"output_tokens":320,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-0':198C '-1':128C,213C '-112397':17A,25A '-2026':16A,24A '-787':28B '0':96C,105C,133C,172C,176C,220C,256C,269C '03_poc.py':231C '1':124C,175C,211C,273C '119':152C '3e2':243C '5':221C '58r4':20A '6rv3':21A '81':169C,259C,264C 'ab96':244C 'accept':99C 'access':6A 'address':252C 'addresssanit':246C,248C,261C 'affect':308C 'also':200C 'appli':155C,207C,226C,255C,257C,266C 'applic':67B,310C 'attack':63B 'bound':5A,149C,302C 'buffer':43B,120C 'build':240C 'c':306C 'check':150C 'code':39B,116C,216C 'commit':242C 'core':234C 'corrupt':83B,330C 'craft':55B,204C 'crash':78B,136C,326C 'cve':15A,23A 'cwe':27B 'declar':92C 'detail':137C 'dilation4':225C 'dump':235C 'edg':159C,186C,290C 'edge-index':185C 'edge-writ':289C 'error':247C 'extens':307C 'fault':233C 'file':59B,72B,90C 'get':65B,179C 'gfh8':22A 'ghsa':19A 'ghsa-58r4-6rv3-gfh8':18A 'guard':162C 'header':206C 'height':144C 'im':217C,227C 'imag':14A,30B,52B,102C,109C,194C,199C,318C 'image-process':29B 'image.new':218C 'imagemorph':75B,195C 'imagemorph.morphop':222C 'imagemorph.morphop.apply':111C 'imagingmorph':8A 'impact':298C 'import':193C 'index':117C,187C,208C 'input':322C 'l':219C 'land':275C 'line':292C 'lower':148C 'lower-bound':147C 'make':112C 'match':177C 'may':82B 'memori':84B,329C 'morpholog':38B,115C,314C 'n':106C 'name':224C 'nativ':37B,114C,154C,215C 'observ':236C 'op':223C 'open':69B,319C 'oper':315C 'out-of-bound':2A,299C 'outrow':122C,127C,171C,173C,209C,212C,271C 'outsid':41B 'overflow':297C 'packag':32B 'page':279C 'parser':140C 'pass':107C 'pattern':188C 'pc':253C 'pgm':58B,89C 'pil':192C 'pillow':1A,26A,33B,35B,98C 'pin':286C 'pixel':181C 'poc':189C 'posit':167C 'possibl':328C 'ppm':56B,87C,139C,205C 'process':31B,80B,135C 'produc':61B,100C,201C 'python':34B,190C,230C 'rather':293C 'read':141C 'redzon':296C 'result':323C 'row':119C,158C 'run':46B,74B,228C,312C 'segment':232C 'segv':249C,262C,285C 'set':170C 'size':104C 'sourc':239C 'src/_imagingmorph.c':168C,258C,263C 'src/pil/ppmimageplugin.py':151C 'summari':85C,260C 'surfac':282C 'unknown':251C 'unmap':278C 'untrust':321C 'use':182C 'width':13A,51B,94C,123C,131C,142C,165C,174C,197C,210C,268C,272C 'write':40B,156C,274C,291C,303C 'zero':12A,50B 'zero-width':11A,49B","nicknames":[],"mitigations":[],"who_affected":null,"tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-19T22:17:06+00:00","kev":null,"cvss_vector":"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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"security-advisories@github.com","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-58r4-6rv3-gfh8","url":"https://github.com/advisories/GHSA-58r4-6rv3-gfh8","severity":"high","published_at":"2026-10-20T20:56:22+00:00","modified_at":"2026-10-20T20:56:23+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-112397","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112397","severity":null,"published_at":"2026-10-19T22:17:06+00:00","modified_at":"2026-10-20T19:48:50+00:00","first_seen_at":"2026-10-23T21:57:19+00:00","fetched_at":"2026-10-23T22:28:57+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}