Eight CVEs, One Inference Engine: vLLM’s 0.32.0 Batch Pairs a Pickle RCE With a Ghostscript One

Eight consecutive CVE records — CVE-2026-115911 through CVE-2026-115918 — reached the advisory databases on 28 October 2026, and every one of them is fixed in vLLM 0.32.0. We read all eight entries first-hand, including each one’s linked GitHub advisory, fix commit, and fix pull request. The batch is heavier than the nine-CVE October batch we covered on 6 October, which was eight denial-of-service bugs and no code execution: this time two of the eight paths reach remote code execution, and the engine-crash bugs around them are mostly single-request, default-configuration kills. If you serve vLLM anywhere shared, this is a stop-what-you’re-doing upgrade — with one packaging catch that a version bump alone does not fix.

The two code-execution paths

CVE-2026-115912 (High, CVSS 7.5, CWE-502) is the one to patch first on shared multimodal deployments. When --mm-processor-cache-type shm is enabled, vLLM’s multimodal shared-memory cache identifies a cached item by an address plus monotonic_id pair — and treats that pair as authority to dereference shared memory in the worker. A public scale-out request can supply its own features.kwargs_data, so an authenticated caller forges an inner shared-memory carrier pointing at attacker-chosen bytes already staged in the ring buffer, and the worker walks it straight into pickle.loads(). The live chain in the report runs through ordinary public HTTP routes — /v1/chat/completions/render to obtain a valid feature envelope, /v1/chat/completions to stage chosen bytes, /inference/v1/generate to submit the forged carrier — and it explicitly does not require VLLM_ALLOW_INSECURE_SERIALIZATION=1. Forged handle in, arbitrary Python in the worker out. Fixed by PR 59357 (3d5625cc), advisory GHSA-6757-hggm-hp2w.

CVE-2026-115915 (High, CVSS 8.1, CWE-183/20) is the stranger chain and the one with the packaging catch. In 0.30.0, ImageMediaIO handed attacker-controlled image bytes to Pillow’s format autodetection with no allowlist, and the request’s MIME label did not constrain decoder selection — so an EPS payload labelled image/png reached Pillow’s EPS handler, which shells out to the host’s Ghostscript binary. On a host whose Ghostscript is still affected by CVE-2024-29510 (the format-string flaw bypassing the -dSAFER sandbox in Ghostscript 9.50 through 10.03.0, fixed upstream in 10.03.1 back in May 2024), the EPS file runs commands with the vLLM process’s privileges. Two honest caveats travel with this one, both stated in the advisory itself: the reporter reproduced command execution at the loader level using the PyPI wheel, Pillow 12.3.0, and Debian’s Ghostscript 10.03.0, and traced the full HTTP request path in source without exercising it end to end; and the report claims no new Ghostscript vulnerability, only vLLM’s untrusted-format dispatch. Exploitation additionally needs an image-capable request path the caller can reach and a vulnerable host Ghostscript build. Fixed by PR 60022 (710ac56e, “untrusted images are limited to raster formats”), advisory GHSA-h3rc-j82p-2c3h.

The Ghostscript wrinkle deserves emphasis because a bare pip install -U vllm does not close it. vLLM’s own requirements do not install Ghostscript — your base image does, if it is a general-purpose CUDA image with document tooling or a PDF-ingest sidecar baked in. The same advisory documents a Pillow-only sibling, CVE-2026-59203 in Pillow 12.0.0 through 12.2.0, where a negative byte count in an EPS file loops inside Image.open() (eight concurrent malicious loads blocked every media-loading thread in the reporter’s test), and 0.30.0 left Pillow unpinned — so a stale environment keeps the bad Pillow after the vLLM upgrade. Close the loop deliberately: 0.32.0 plus Pillow 12.3.0 or later plus a Ghostscript at 10.03.1 or newer, or no Ghostscript in the image at all.

The engine kills

CVE-2026-115911 (Medium, 6.5) wedges every chat-style endpoint with a single short template. vLLM compiles request chat templates with tokenizer.apply_chat_template() and renders them in an ImmutableSandboxedEnvironment — which blocks code-execution escapes but imposes no wall-clock timeout, no output-size cap, and no nested-loop or operator limit — inside a ThreadPoolExecutor whose max_workers defaults to 1, with CPython worker threads that cannot be interrupted. One authenticated request permanently wedges /v1/chat/completions, /v1/responses, /tokenize, the pooling-chat paths, and the invocations-chat paths for every tenant until the process restarts; a string-multiplication variant OOM-kills the API server instead. The detail that removes the “but our deploy isn’t configured that way” defence: a separately reported gate omission (GHSA-jmjw-q4x3-hmc2) on /v1/responses makes the remotely authenticated vector work under default vllm serve with no operator opt-in. Affected 0.19.0 through 0.32.0, fixed by PR 50300 (cff08b4), advisory GHSA-542m-9234-8gvf.

CVE-2026-115913 (Medium, 6.5, CWE-770) is the most cinematic: a 19.2 MB silent 600-second WAV file takes the whole server down in about six seconds. The Phi-4 multimodal audio encoder runs its conformer attention in eager mode by default (the official microsoft/Phi-4-multimodal-instruct config never opts into scaled dot-product attention), fully materialising a [n_chunks, 16, 500, 500] attention-probability matrix per layer and then pinning it on the module as self.attn — a live reference in every one of 24 encoder layers. The only server-side audio gates are the VLLM_MAX_AUDIO_CLIP_FILESIZE_MB=25 and VLLM_MAX_AUDIO_DECODE_DURATION_S=600 environment limits, so a 10-minute clip is legal input; 600 seconds unfold to roughly 120 chunks, each layer’s matrix runs 1–2 GiB depending on dtype, and GPU memory climbs monotonically inside a single request until allocation fails. The report verifies it end to end on the official vllm/vllm-openai:v0.31.0 image: torch.OutOfMemoryError inside get_audio_features, fatal EngineCore error, Api EngineDeadError, whole server down with /health refusing connections. A secondary effect lingers after the crash you survive: self.attn persists on the module after the request ends (a 5-second control request left +50 MiB pinned), so the resident footprint equals the largest audio processed since startup. Advisory GHSA-8397-8qf6-f2gm, PR 60614 (3d52578e).

CVE-2026-115914 (Medium, 6.5, CWE-617) needs even less: any deployment serving a DeepSeek-OCR-family model (deepseek-ai/DeepSeek-OCR, DeepSeek-OCR-2, or Baidu’s Unlimited-OCR, whose class subclasses the DeepSeek-OCR one) dies on a single unauthenticated chat request carrying an ordinary schema-valid PNG, under completely default server configuration — an inherited input-validation guard from the upstream reference implementation trips a reachable assertion. Affected 0.11.1 onward, advisory GHSA-gq5q-89c7-vhvx, PR 59417 (c15672cf).

CVE-2026-115916 (Medium, 6.5) crashes the engine with one out-of-vocabulary token id. For models whose vocabulary size is an exact multiple of the embedding padding size of 64 — any Llama-2-family model at vocab 32000 qualifies — a pre-tokenized prompt carrying token id 32000 passes input-id validation but sits one past the last embedding row; at tensor-parallel size 1 nothing masks the index, so it reaches F.embedding out of range and raises a CUDA device-side assert that poisons the engine’s CUDA context. Reachable by any client that can send pre-tokenized input, including the kv_transfer_params["prompt_token_ids"] reuse path. Advisory GHSA-q97c-p368-75c4, PR 59491 (37cecc81).

The two bypasses

CVE-2026-115918 reopens, on one endpoint, the exact gate 115911 just installed everywhere else: /v1/responses accepts request-level chat_template_kwargs without applying the --trust-request-chat-template gate that protects the Chat Completions, render, tokenize, and pooling paths. With the documented default (trust_request_chat_template=False, no explicit --chat-template), a Responses request smuggles an attacker Jinja template through a key that ChatParams.get_apply_chat_template_kwargs() preserves and binds as the shared renderer’s template — and prior advisories in this codebase establish that request-controlled Jinja templates mean CPU and memory exhaustion. Same fix commit as 115911 (cff08b4 / PR 50300), advisory GHSA-x567-49g5-p4cf. The lesson is the familiar one about allowlist-gated sinks: the gate has to cover every path that reaches the renderer, and “every path except the newest endpoint” is not coverage.

CVE-2026-115917 (Low, 3.4) is the lowest severity in the batch and the easiest to leave exposed by accident: the OpenAI-compatible server registers Starlette’s CORSMiddleware with allowed_origins defaulting to ["*"] and performs no authentication unless --api-key is set, so any web page can send requests to a vLLM server the victim’s browser can reach — localhost, an internal address, a VPN route — and read the responses. allow_credentials=False blocks cookie-bearing cross-origin requests, but vLLM authenticates with a bearer header rather than cookies, so the wildcard still permits untokenized access to an unauthenticated server (and read access whenever the caller already holds a token). Advisory GHSA-vvpg-rrww-wpmh, PR 61009 (fcae5c38). Low in isolation; on a shared GPU endpoint behind an internal gateway, a single bad request taking out everyone’s in-flight work is the outage your on-call team gets paged for — which is why we would patch the crashes before feeling good about the Low.

What to do

  • Upgrade to 0.32.0, then finish the job the version bump starts. Raise Pillow to 12.3.0 or later, and either bring host Ghostscript to 10.03.1+ or remove it from the serving image entirely. Rebuild the container; a wheel upgrade over a stale image keeps both the bad Pillow and the bad Ghostscript.
  • Patch the two RCEs first if you must sequence. 115912 matters wherever shared-memory multimodal caching is on and callers are not fully trusted; 115915 matters wherever an image-capable path meets a document-tooled base image. Both depend on configuration you can check in minutes.
  • Then the engine crashes, in roughly this order: the Jinja wedge (115911, default-reachable via /v1/responses plus its 115918 sibling), the Phi-4 audio OOM (115913, if you serve that model family), the DeepSeek-OCR PNG kill (115914), and the OOV token-id assert (115916).
  • Set --api-key and narrow CORS. 115917 is the cheapest finding in the batch to hold against you: an unauthenticated internal endpoint with wildcard origins is one visited page away from someone else’s browser driving your inference bill.

Our write-up is analysis of the eight GitLab Advisory Database mirror entries for CVE-2026-115911 through CVE-2026-115918 (each dated 28 October 2026, carrying affected and fixed versions, CVSS vectors, CWE mappings, and links to the GitHub advisory, fix commit, fix pull request, release tag v0.32.0, and NVD record), read first-hand in full, with Rajesh Beri’s 26 October secondary assessment used for deployment context (advisory-list scale, the container-rebuild point, and the Ghostscript provenance note). All verification was read-only: we installed and ran no vLLM build, sent no request to any inference server, staged no shared-memory carrier, rendered no image or audio payload, and contacted no maintainer or reporter. CVE-2026-115915’s HTTP-path caveat is the advisory’s own and we carry it as such.

Sources: