{"id":"CVE-2026-112402","kind":"vulnerability","aliases":["CVE-2026-112402","GHSA-7mwm-ghpf-9vrm"],"related":[],"title":"Pillow: Double free in ImagingScaleAffine on calloc failure via Image.transform","description":"```\n============================================================================\nDouble free in ImagingScaleAffine on calloc failure via Image.transform\nLocation: src/libImaging/Geometry.c:866  in ImagingScaleAffine()\nCommit:   a707c6b21c313880d0771e54965d142dd42358b6\n============================================================================\n```\n\nThere is a double free in Pillow's `Image.transform()` that crashes the process when an oversized transform runs out of memory. Instead of only raising `MemoryError`, Pillow frees the output image's memory once while handling the error and then again when the Python object is cleaned up. Anyone who can choose the output size of `Image.transform()` can trigger it, but only on a worker with a memory limit (a common hardening for image workers) or a 32-bit process. The result is a crash of the worker, plus corruption of its heap that has not been shown to be controllable.\n\nThe bug is in `ImagingScaleAffine()` in `src/libImaging/Geometry.c` (line 866). When its `calloc()` for the per-column lookup table of `4 × width` bytes fails, it calls `ImagingDelete(imOut)`. When the call comes from `Image.transform()`, `imOut` is the `Imaging` struct owned by the live Python `ImagingCore` output object. `_dealloc()` in `src/_imaging.c` later runs `ImagingDelete()` on the freed struct again, writing its mutex and refcount in freed memory and freeing the pixel blocks a second time. The path is reached with `Transform.EXTENT`, or `Transform.AFFINE` with a pure scale/translate matrix, under the default NEAREST resampling. A very wide, 1-row output size makes the lookup table the allocation that fails while the image itself still fits. On 64-bit Linux with default overcommit, that requires an address-space limit such as `ulimit -v`, `uwsgi --limit-as` or `systemd LimitAS=`. `resize()`, `thumbnail()` and `rotate()` are not affected. The attached reproducer runs on a stock build with a 1 GB limit: `transform((300000000, 1), EXTENT)` raises and catches `MemoryError`, then the process dies with SIGSEGV in `free()` called from `_dealloc()`. A memory-checking build reports the same path as a heap use-after-free. The faulty line was inherited from PIL 1.1.7 in the original fork commit, so every Pillow release since 1.0 is affected. A patch is attached.\n\n## Details\n\n`ImagingScaleAffine()` frees an image it does not own. On the one error path where its `calloc()` for the per-column lookup table fails, it calls `ImagingDelete(imOut)`. When reached through `Image.transform()`, `imOut` is the `Imaging` struct held by a live Python `ImagingCore` object; that object's `_dealloc()` later calls `ImagingDelete()` on the already-freed struct and frees its pixel blocks a second time.\n\nShortest demonstration on the stock build (the address-space limit stands in for `ulimit -v` / `uwsgi --limit-as` / `systemd LimitAS=` on a worker):\n\n```python\nimport resource, gc\nfrom PIL import Image\nresource.setrlimit(resource.RLIMIT_AS, (1_000_000_000,) * 2)\nsrc = Image.new(\"L\", (8, 8))\ntry:\n    src.transform((300_000_000, 1), Image.Transform.EXTENT, (0, 0, 8, 8))\nexcept MemoryError:\n    pass                      # expected, and the process should carry on\ngc.collect()                  # instead: SIGSEGV in _dealloc -> ImagingDelete -> free()\n```\n\nThe 300 MB output image allocates; `calloc(300000000, 4)` (1.2 GB) does not. `MemoryError` is raised as intended, but the interpreter then dies with SIGSEGV, gdb showing `free()` ← `memory_return_block`/`ImagingDestroyArray` (`src/libImaging/Storage.c:396,408`) ← `ImagingDelete` (`Storage.c:271`) ← `_dealloc` (`src/_imaging.c:188`). An AddressSanitizer build of the same sources (with `max_allocation_size_mb` used to make the `calloc` fail instead of an rlimit) reports `heap-use-after-free` in `ImagingDelete src/libImaging/Storage.c:257` called from `_dealloc src/_imaging.c:188`, on a 128-byte region \"freed by\" `ImagingDelete` ← `ImagingScaleAffine src/libImaging/Geometry.c:866` ← `ImagingTransformAffine Geometry.c:1043` ← `ImagingTransform Geometry.c:1135` ← `_transform src/_imaging.c:2153`, \"previously allocated by\" `ImagingNewPrologueSubtype Storage.c:53` ← … ← `_new src/_imaging.c:756`.\n\n### Code path\n\n`Image.transform()` creates the output image as an ordinary Python-owned image and then transforms *into* it:\n\n```python\n# src/PIL/Image.py:3008…3021\n        im = new(self.mode, size, fillcolor)\n        …\n            im.__transformer(\n                (0, 0, *size), self, method, data, resample, fillcolor is None\n            )\n```\n\n`__transformer()` rewrites `Transform.EXTENT` into an axis-aligned affine matrix (`src/PIL/Image.py:3040-3046`: `data = (xs, 0, x0, 0, ys, y0)`, `method = Transform.AFFINE`) and calls `self.im.transform(box, image.im, method, data, resample, fill)` (`Image.py:3117`). `resample` defaults to `Resampling.NEAREST` (`Image.py:2936`).\n\nIn C, `_transform()` passes its *own* image as the destination:\n\n```c\n/* src/_imaging.c:2153 */\n    imOut = ImagingTransform(\n        self->image, imagep->image, method, x0, y0, x1, y1, a, filter, fill\n    );\n```\n\n`ImagingTransformAffine()` takes the scaling fast path because the filter is NEAREST and the matrix has no shear:\n\n```c\n/* src/libImaging/Geometry.c:1035-1043 */\n    if (filterid || imIn->type == IMAGING_TYPE_I16) {\n        return ImagingGenericTransform(…);\n    }\n    if (a[1] == 0 && a[3] == 0) {\n        /* Scaling */\n        return ImagingScaleAffine(imOut, imIn, x0, y0, x1, y1, a, fill);\n    }\n```\n\nand `ImagingScaleAffine()` allocates a table of `imOut->xsize` ints:\n\n```c\n/* src/libImaging/Geometry.c:863-868 */\n    /* malloc check ok, uses calloc for overflow */\n    xintab = (int *)calloc(imOut->xsize, sizeof(int));\n    if (!xintab) {\n        ImagingDelete(imOut);                         /* <-- imOut belongs to the caller */\n        return (Imaging)ImagingError_MemoryError();\n    }\n```\n\n`ImagingDelete()` (`src/libImaging/Storage.c:250-279`) drops the refcount to 0, returns the pixel blocks to the arena / `free()`s them via `im->destroy`, and `free(im)`. `self->image` in the `ImagingCore` object still points at the freed struct. `_transform()` returns `NULL`, `Image.transform()` propagates `MemoryError`, the half-built output `Image` loses its last reference, and `_dealloc()` (`src/_imaging.c:188`) calls `ImagingDelete(imagep->image)` again: it locks `im->mutex` and decrements `im->refcount` inside freed memory, then walks `im->blocks` (already freed, typically reused by then) and frees every entry a second time. In the observed stock-build runs the freed `blocks` array had been overwritten, so `free()` receives a mangled pointer and the process segfaults; with other heap states it is a plain double free of a 300 MB block.\n\nEvery other error return in `Geometry.c` (`ImagingError_ModeError`, `ImagingError_Mismatch`, `\"bad filter number\"`, `\"bad transform method\"`) leaves `imOut` alone; line 866 is the only one that deletes it. The delete is only correct for the other caller, `_resize()` (`src/_imaging.c:2026-2030`), where `imOut` is a fresh `ImagingNewDirty()` temporary that nothing else references. The delete predates Pillow's git history: it came in with the PIL 1.1.7 import, `9a640e315715` (\"Forking PIL\", 2010-07-30), so every release since Pillow 1.0 is affected.\n\nThe output width is what decides whether the path is hit: the output image costs `width × height × pixelsize` bytes, the table an additional `4 × width`. A very wide, 1-row output (300 000 000 × 1 \"L\": 300 MB image, 1.2 GB table) makes the table the allocation that fails. `ImagingNewPrologueSubtype()` caps `xsize` at `INT_MAX/4 - 1`, so the table is at most ~2 GB.\n\n## Impact\n\nApplications that let a user choose the output size of `Image.transform()` — `Transform.EXTENT` (crop-and-scale), `Transform.AFFINE` with a pure scale/translate matrix, or the `ImageTransform.ExtentTransform`/`AffineTransform` helpers, all with the default NEAREST resampling — can have an allocation failure turned into a use-after-free and double free inside the worker process instead of the `MemoryError` the API is meant to raise. `resize()`, `thumbnail()` and `rotate()` do not reach this path; `Image.resize(..., NEAREST, box=...)` goes through `ImagingScaleAffine()` but owns its output, and raises a clean `MemoryError`. The second precondition is that the output image itself allocates but the extra `4 × width` bytes do not; on 64-bit Linux with default overcommit this requires an address-space limit on the process (`ulimit -v`, `uwsgi --limit-as`, `systemd LimitAS=`, an in-app `setrlimit`), which is a common hardening for image workers; 32-bit processes can reach it through ordinary address-space exhaustion. The demonstrated consequence is a crash of the process handling the request after `MemoryError` has already been raised and possibly handled. The second `ImagingDelete()` also writes into the freed 128-byte struct (mutex, refcount) and double-frees an attacker-sized block, which is heap corruption rather than a clean abort, but no control over the written data is shown.\n\n## Reproducing\n\nRun `repro` (a self-contained Python script) from any directory. It uses the installed stock build, so nothing is rebuilt, and has no companion files. It runs the demonstration above in a child interpreter (`python3 -c`, taking `WIDTH` as an argument): the child disables core files, sets `RLIMIT_AS` to 1 GB (the kind of limit image workers are commonly given), creates an 8×8 \"L\" image, calls `src.transform((WIDTH, 1), Image.Transform.EXTENT, (0, 0, 8, 8))` with the default NEAREST resampling, prints `MemoryError caught` if that raises, then runs `gc.collect()` and prints `alive`. EXTENT with NEAREST is a pure scale, so the call goes through `ImagingScaleAffine()`, which callocs a `4 × WIDTH`-byte table in addition to the `WIDTH`-byte output image.\n\nThe script runs the child twice:\n\n- **Control:** width 50 000 000. The 50 MB image and the 200 MB table both fit under the limit, and the transform succeeds, which shows the limit itself is harmless.\n- **Attack:** width 300 000 000. The 300 MB output image is allocated, but `calloc(300000000, 4)` (1.2 GB) fails. Pillow correctly raises `MemoryError`, and the child catches it. But `ImagingScaleAffine()` has already called `ImagingDelete()` on the output image it does not own. When the output image is released, it is freed again. The child prints `MemoryError caught` and then dies with SIGSEGV instead of printing `alive`.\n\nIf the attack child was killed by a signal and `gdb` is installed, the script runs the attack again under `gdb` and prints the backtrace. The crash is in `free()` ← `memory_return_block`/`ImagingDestroyArray` (`Storage.c:396,408`) ← `ImagingDelete` (`Storage.c:271`) ← `_dealloc` (`_imaging.c:188`). In other words, the Python object's destructor is deleting the already-freed struct a second time. The verdict does not depend on this step.\n\nThe last line is `BUG` when the control exits 0 and the attack prints `MemoryError caught` and then dies from a signal without printing `alive`. Otherwise it is `OK`. With the attached patch applied, the attack prints `MemoryError caught`, then `alive`, exits 0, and the script prints `OK`.\n\n## Suggested fix\n\n`ImagingScaleAffine()` never owns `imOut`. The attached patch removes the `ImagingDelete()` at `src/libImaging/Geometry.c:866` and makes the one caller that relied on it, `_resize()` in `src/_imaging.c`, clean up its own temporary, the same idiom `_gaussian_blur()`/`_unsharp_mask()` already use. This also fixes `_resize()` passing a possible `NULL` from `ImagingNewDirty()` into `ImagingTransform()`, and leaking the temporary on the other `ImagingTransform()` error returns:\n\n```diff\n--- a/src/libImaging/Geometry.c\n+++ b/src/libImaging/Geometry.c\n@@ -863,7 +863,7 @@ ImagingScaleAffine(\n     /* malloc check ok, uses calloc for overflow */\n     xintab = (int *)calloc(imOut->xsize, sizeof(int));\n     if (!xintab) {\n-        ImagingDelete(imOut);\n+        /* imOut is owned by the caller; don't delete it here */\n         return (Imaging)ImagingError_MemoryError();\n     }\n--- a/src/_imaging.c\n+++ b/src/_imaging.c\n@@ -2024,10 +2024,16 @@ _resize(ImagingObject *self, PyObject *args) {\n         a[5] = box[1];\n \n         imOut = ImagingNewDirty(imIn->mode, xsize, ysize);\n+        if (!imOut) {\n+            return NULL;\n+        }\n \n-        imOut = ImagingTransform(\n-            imOut, imIn, IMAGING_TRANSFORM_AFFINE, 0, 0, xsize, ysize, a, filter, 1\n-        );\n+        if (!ImagingTransform(\n+                imOut, imIn, IMAGING_TRANSFORM_AFFINE, 0, 0, xsize, ysize, a, filter, 1\n+            )) {\n+            ImagingDelete(imOut);\n+            return NULL;\n+        }\n     } else {\n```\n\nEvery `ImagingTransform*()` path returns the same `imOut` on success, so wrapping the original pointer in `PyImagingNew()` in `_resize()` is unchanged behaviour. With the patch applied to a clean checkout and rebuilt, the reproducer prints `OK` and `Image.resize(..., NEAREST, box=...)` under the same limit raises a clean `MemoryError`.\n\n## The patch\n\nStop ImagingScaleAffine() deleting the caller-owned output image on calloc failure; make _resize() free its own temporary instead\n\nApplies to `src/libImaging/Geometry.c` at the commit above.\n\n```diff\ndiff --git a/src/_imaging.c b/src/_imaging.c\nindex 7e9c0e391..ac1873292 100644\n--- a/src/_imaging.c\n+++ b/src/_imaging.c\n@@ -2024,10 +2024,16 @@ _resize(ImagingObject *self, PyObject *args) {\n         a[5] = box[1];\n \n         imOut = ImagingNewDirty(imIn->mode, xsize, ysize);\n+        if (!imOut) {\n+            return NULL;\n+        }\n \n-        imOut = ImagingTransform(\n-            imOut, imIn, IMAGING_TRANSFORM_AFFINE, 0, 0, xsize, ysize, a, filter, 1\n-        );\n+        if (!ImagingTransform(\n+                imOut, imIn, IMAGING_TRANSFORM_AFFINE, 0, 0, xsize, ysize, a, filter, 1\n+            )) {\n+            ImagingDelete(imOut);\n+            return NULL;\n+        }\n     } else {\n         imOut = ImagingResample(imIn, xsize, ysize, filter, box);\n     }\ndiff --git a/src/libImaging/Geometry.c b/src/libImaging/Geometry.c\nindex 1bf92e26e..2baf000a5 100644\n--- a/src/libImaging/Geometry.c\n+++ b/src/libImaging/Geometry.c\n@@ -863,7 +863,7 @@ ImagingScaleAffine(\n     /* malloc check ok, uses calloc for overflow */\n     xintab = (int *)calloc(imOut->xsize, sizeof(int));\n     if (!xintab) {\n-        ImagingDelete(imOut);\n+        /* imOut is owned by the caller; don't delete it here */\n         return (Imaging)ImagingError_MemoryError();\n     }\n```\n\n## The reproducer\n\n<details>\n<summary>Reproducer — <code>repro</code>, 2,064 bytes (click to open)</summary>\n\nIts last line is `BUG` when the issue triggers and `OK` when it does not. Re-run on a clean build of that commit it printed `BUG`.\n\n```python\n#!/usr/bin/env python3\n# UAF/double free in ImagingScaleAffine() (src/libImaging/Geometry.c:866):\n# on calloc() failure it ImagingDelete()s imOut, which for Image.transform()\n# is the Imaging struct owned by the live Python output image; that image's\n# _dealloc then ImagingDelete()s it again. Stock /src build, no rebuild.\n# A 1 GB RLIMIT_AS makes the 300 MB output image fit but the 1.2 GB\n# 4*width lookup table fail. Expected: MemoryError, process survives.\nimport subprocess, sys, textwrap, signal\n\nchild = textwrap.dedent('''\n    import resource, gc, sys\n    from PIL import Image\n    resource.setrlimit(resource.RLIMIT_CORE, (0, 0))\n    resource.setrlimit(resource.RLIMIT_AS, (1_000_000_000,) * 2)\n    w = int(sys.argv[1])\n    src = Image.new(\"L\", (8, 8))\n    try:\n        src.transform((w, 1), Image.Transform.EXTENT, (0, 0, 8, 8))\n        print(\"transform ok\")\n    except MemoryError:\n        print(\"MemoryError caught\")\n    gc.collect()\n    print(\"alive\")\n''')\n\ndef run(width):\n    p = subprocess.run([sys.executable, \"-u\", \"-c\", child, str(width)],\n                       capture_output=True, text=True, timeout=50)\n    print(f\"-- width={width}: rc={p.returncode}\\n{p.stdout.rstrip()}\")\n    return p\n\nctrl = run(50_000_000)      # 50 MB image + 200 MB table fit under the limit\natt = run(300_000_000)      # 300 MB image fits, 1.2 GB table does not\nbug = (ctrl.returncode == 0 and \"MemoryError caught\" in att.stdout\n       and \"alive\" not in att.stdout and att.returncode < 0)\nif att.returncode < 0:\n    print(\"attack child killed by\", signal.Signals(-att.returncode).name)\n    import shutil\n    if shutil.which(\"gdb\"):   # evidence only; verdict does not depend on it\n        g = subprocess.run([\"gdb\", \"-q\", \"-batch\", \"-ex\", \"run\", \"-ex\", \"bt 6\", \"--args\",\n                            sys.executable, \"-c\", child, \"300000000\"],\n                           capture_output=True, text=True, timeout=120)\n        print(\"\\n\".join(l for l in g.stdout.splitlines()\n                        if l.startswith(\"#\") or \"signal\" in l))\nprint(\"BUG\" if bug else \"OK\")\n```\n</details>\n\n---","summary":"Pillow's Image.transform() frees its caller-owned output image on an allocation failure, so the Python object later frees it again. An attacker who can choose the output size can crash the worker process (heap corruption, not shown to be controllable).","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-416","image-processing","package","pillow","python","self-hosted"],"not_affected_if":["no-address-space-limit","no-user-controlled-transform-size","resize-only-usage","thumbnail-only-usage"],"how_to_check":"Check the installed Pillow version with pip show pillow; affected versions are up to and including 12.3.0. Find whether Image.transform() gets user-chosen sizes and whether the worker has an address-space limit (ulimit -v, uwsgi --limit-as, systemd LimitAS=).","fix":"Upgrade Pillow to 13.0.0 or later.\nIf upgrading is not possible, reject user-supplied output sizes for Image.transform() or avoid EXTENT/AFFINE transforms with user-chosen dimensions.","status":"open","published_at":"2026-10-19T22:17:07+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T20:10:19+00:00","updated_at":"2026-10-23T21:57:19+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-10-23T20:10:19Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade Pillow to 13.0.0 or later.\nIf upgrading is not possible, reject user-supplied output sizes for Image.transform() or avoid EXTENT/AFFINE transforms with user-chosen dimensions.","summary":"Pillow's Image.transform() frees its caller-owned output image on an allocation failure, so the Python object later frees it again. An attacker who can choose the output size can crash the worker process (heap corruption, not shown to be controllable).","category":"package","who_cares":["python","pillow","image-processing","self-hosted"],"confidence":0.85,"analyst_note":"","how_to_check":"Check the installed Pillow version with pip show pillow; affected versions are up to and including 12.3.0. Find whether Image.transform() gets user-chosen sizes and whether the worker has an address-space limit (ulimit -v, uwsgi --limit-as, systemd LimitAS=).","needs_analyst":false,"not_affected_if":["resize-only-usage","thumbnail-only-usage","no-user-controlled-transform-size","no-address-space-limit"],"severity_if_unknown":"unknown"},"cost_usd":0.000874,"input_tokens":6769,"output_tokens":394,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-112402':13A,21A '-2026':12A,20A '-416':24B '0':530C,531C '000':514C,515C,516C,526C,527C '1':288C,348C,353C,513C,528C '1.0':404C '1.1.7':393C '1.2':560C '128':631C '188':591C,628C '2':517C '257':623C '271':588C '300':525C,552C '300000000':352C,558C '32':169C '396':584C '4':213C,559C '408':585C '64':307C '7mwm':16A '8':521C,522C,532C,533C '866':87C,201C,639C '9vrm':18A 'a707c6b21c313880d0771e54965d142dd42358b6':91C 'address':317C,485C 'address-spac':316C,484C 'addresssanit':593C 'affect':337C,406C 'alloc':46B,297C,556C,601C 'alreadi':466C 'already-fre':465C 'anyon':140C 'attach':339C,410C 'attack':57B 'bit':170C,308C 'block':263C,473C,581C 'bug':194C 'build':345C,374C,482C,594C 'byte':215C,632C 'call':218C,223C,367C,437C,461C,624C 'caller':40B 'caller-own':39B 'calloc':7A,81C,204C,427C,557C,608C 'carri':542C 'catch':357C 'check':373C 'choos':60B,143C 'clean':138C 'column':209C,432C 'come':224C 'commit':90C,398C 'common':162C 'control':75B,192C 'corrupt':70B,181C 'crash':65B,102C,176C 'cve':11A,19A 'cwe':23B 'dealloc':240C,369C,459C,548C,589C,626C 'default':282C,311C 'demonstr':478C 'detail':411C 'die':362C,573C 'doubl':2A,76C,95C 'error':129C,423C 'everi':400C 'except':534C 'expect':537C 'extent':354C 'fail':216C,299C,435C,609C 'failur':8A,47B,82C 'faulti':387C 'fit':305C 'fork':397C 'free':3A,37B,53B,77C,96C,119C,260C,366C,385C,413C,470C,550C,578C,619C 'freed':248C,257C,467C,634C 'gb':349C,561C 'gc':505C 'gc.collect':544C 'gdb':576C 'ghpf':17A 'ghsa':15A 'ghsa-7mwm-ghpf-9vrm':14A 'handl':127C 'harden':163C 'heap':69B,184C,381C,616C 'heap-use-after-fre':615C 'held':449C 'host':33B 'imag':26B,43B,122C,165C,230C,302C,415C,447C,509C,555C 'image-process':25B 'image.new':519C 'image.transform':10A,36B,84C,100C,148C,226C,443C 'image.transform.extent':529C 'imagingcor':237C,454C 'imagingdelet':219C,245C,438C,462C,549C,586C,621C,636C 'imagingdestroyarray':582C 'imagingscaleaffin':5A,79C,89C,197C,412C,637C 'imagingtran':640C 'imout':220C,227C,439C,444C 'import':503C,508C 'inherit':390C 'instead':113C,545C,610C 'intend':568C 'interpret':571C 'l':520C 'later':52B,243C,460C 'limit':160C,319C,326C,350C,487C,495C 'limit-a':325C,494C 'limita':330C,498C 'line':200C,388C 'linux':309C 'live':235C,452C 'locat':85C 'lookup':210C,294C,433C 'make':292C,606C 'matrix':279C 'max':600C 'mb':553C,603C 'memori':112C,124C,159C,258C,372C,579C 'memory-check':371C 'memoryerror':117C,358C,535C,564C 'mutex':253C 'nearest':283C 'object':51B,136C,239C,455C,457C 'one':422C 'origin':396C 'output':42B,62B,121C,145C,238C,290C,554C 'overcommit':312C 'overs':107C 'own':41B,232C 'packag':28B 'pass':536C 'patch':408C 'path':268C,378C,424C 'per':208C,431C 'per-column':207C,430C 'pil':392C,507C 'pillow':1A,22A,29B,34B,98C,118C,401C 'pixel':262C,472C 'plus':180C 'process':27B,68B,104C,171C,361C,540C 'pure':277C 'python':30B,50B,135C,236C,453C,502C 'rais':116C,355C,566C 'reach':270C,441C 'refcount':255C 'region':633C 'releas':402C 'report':375C,614C 'reproduc':340C 'requir':314C 'resampl':284C 'resiz':331C 'resourc':504C 'resource.rlimit':511C 'resource.setrlimit':510C 'result':173C 'return':580C 'rlimit':613C 'rotat':334C 'row':289C 'run':109C,244C,341C 'scale/translate':278C 'second':265C,475C 'self':32B 'self-host':31B 'shortest':477C 'show':577C 'shown':72B,189C 'sigsegv':364C,546C,575C 'sinc':403C 'size':63B,146C,291C,602C 'sourc':598C 'space':318C,486C 'src':518C 'src.transform':524C 'src/_imaging.c':242C,590C,627C 'src/libimaging/geometry.c':86C,199C,638C 'src/libimaging/storage.c':583C,622C 'stand':488C 'still':304C 'stock':344C,481C 'storage.c':587C 'struct':231C,249C,448C,468C 'systemd':329C,497C 'tabl':211C,295C,434C 'thumbnail':332C 'time':266C,476C 'transform':108C,351C 'transform.affine':274C 'transform.extent':272C 'tri':523C 'trigger':150C 'ulimit':322C,491C 'use':383C,604C,617C 'use-after-fre':382C 'uwsgi':324C,493C 'v':323C,492C 'via':9A,83C 'wide':287C 'width':214C 'worker':67B,156C,166C,179C,501C 'write':251C","nicknames":[],"mitigations":[],"who_affected":null,"tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-19T22:17:07+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-7mwm-ghpf-9vrm","url":"https://github.com/advisories/GHSA-7mwm-ghpf-9vrm","severity":"high","published_at":"2026-10-21T20:03:56+00:00","modified_at":"2026-10-21T20:03:57+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-112402","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112402","severity":null,"published_at":"2026-10-19T22:17:07+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"}