AI and data stack advisories

Severe, 6 weeks459Projects324

459 severe, 6 weeks · 324 projects

vLLMGHSA-gq5q-89c7-vhvx

vLLM DeepSeek-OCR image processing vulnerable to denial of service

vLLM

CVE-2026-115914 · Published Oct 28, 2026

Medium6.5
Fix: upgrade to 0.32.0 or later
GitHub advisory

Summary

Any vLLM deployment serving a model of the DeepSeek-OCR family , deepseek-ai/DeepSeek-OCR, deepseek-ai/DeepSeek-OCR-2, or baidu/Unlimited-OCR (whose UnlimitedOCRForCausalLM subclasses DeepseekOCRForCausalLM) , can be killed by a single unauthenticated chat request carrying an ordinary, schema-valid PNG image, under completely default server configuration.

The image model's input validation contains a guard inherited from the upstream DeepSeek-OCR reference implementation:

```python

vllm/model_executor/models/deepseek_ocr.py:478-479 (_parse_and_validate_image_input)

if torch.sum(pixel_values).item() == 0:

return None

`

In the original research code this was an empty-batch shortcut; in vLLM's serving architecture pixel_values is the actual client image tensor, and the condition is exactly satisfiable by a legitimate image: the in-tree DeepseekOCRProcessor normalizes pixels with ToTensor + Normalize(0.5, 0.5) (vllm/transformers_utils/processors/deepseek_ocr.py:120-140), mapping pixel value 0 → exactly −1.0 and 255 → exactly +1.0 in float32. A 1024×1024 image that is half pure black and half pure white (a perfectly ordinary 1-bit-style scanned-document PNG for an OCR model) therefore yields torch.sum(pixel_values) == 0.0 exactly (equal counts of ±1.0; pairwise reduction stays integer-exact, well inside float32's 2²⁴...

Affected versions

PackageAffectedFixed in
vllm
PyPI
>= 0.11.1, < 0.32.00.32.0
Details and references

## Summary Any vLLM deployment serving a model of the DeepSeek-OCR family , `deepseek-ai/DeepSeek-OCR`, `deepseek-ai/DeepSeek-OCR-2`, or `baidu/Unlimited-OCR` (whose `UnlimitedOCRForCausalLM` subclasses `DeepseekOCRForCausalLM`) , can be **killed by a single unauthenticated chat request carrying an ordinary, schema-valid PNG image**, under completely default server configuration. The image model's input validation contains a guard inherited from the upstream DeepSeek-OCR reference implementation: ```python # vllm/model_executor/models/deepseek_ocr.py:478-479 (_parse_and_validate_image_input) if torch.sum(pixel_values).item() == 0: return None ``` In the original research code this was an empty-batch shortcut; in vLLM's serving architecture `pixel_values` is the actual client image tensor, and the condition is **exactly satisfiable by a legitimate image**: the in-tree `DeepseekOCRProcessor` normalizes pixels with `ToTensor + Normalize(0.5, 0.5)` (`vllm/transformers_utils/processors/deepseek_ocr.py:120-140`), mapping pixel value 0 → exactly −1.0 and 255 → exactly +1.0 in float32. A 1024×1024 image that is half pure black and half pure white (a perfectly ordinary 1-bit-style scanned-document PNG for an OCR model) therefore yields `torch.sum(pixel_values) == 0.0` **exactly** (equal counts of ±1.0; pairwise reduction stays integer-exact, well inside float32's 2²⁴ exactness bound). `ImageOps.pad(image, (1024, 1024))` is the identity map for an already-1024×1024 image (`processors/deepseek_ocr.py:350-355`), so no padding pixels disturb the balance. Returning `None` makes `embed_multimodal` return `None` (`deepseek_ocr.py:606-611`), which immediately trips the engine-side sanity check in the worker: ```python # vllm/v1/worker/utils.py:512 (sanity_check_mm_encoder_outputs) assert isinstance(mm_embeddings, (list, tuple, torch.Tensor)), ( "Expected multimodal embeddings to be a list/tuple of 2D tensors, ...") ``` The `AssertionError` is raised inside the encoder runner of the EngineCore worker process with no per-request containment, escapes to the busy loop's top-level handler (`vllm/v1/engine/core.py:1368`, "EngineCore encountered a fatal error."), triggers `EngineDeadError` at the API server (`vllm/v1/engine/async_llm.py:843`), and the whole process tree shuts down permanently. `/health` refuses connections until a manual restart. Verified end-to-end on the official `vllm/vllm-openai:v0.30.0` image (id d076d77d8dad) with real `deepseek-ai/DeepSeek-OCR` weights: after confirming `/health` 200, a text chat 200, and two control images (all-white and all-black 1024×1024) 200, the single half-black/half-white request returned HTTP 500 (`EngineCore encountered an issue`), the engine log shows `AssertionError: Expected multimodal embeddings to be a list/tuple of 2D tensors ... got <class 'NoneType'>` at `v1/worker/utils.py:512`, the API server shut itself down, and the container exited (`Exited (0)`, `OOMKilled=false`). Every subsequent request, including `GET /health`, fails at the socket. The vulnerable check is unchanged on `main` (verified 2026-09-30 against the raw file on the default branch). **Environment notes (disclosed for completeness; unrelated to the sink):** (1) the v0.30.0 image's Triton version refuses to compile `deepencoder.py`'s fused rel-pos attention kernel (`LOG2E` non-constexpr global) during the startup profile run; per the compiler's own suggestion we set `TRITON_ALLOW_NON_CONSTEXPR_GLOBALS=1` , an environment workaround only, no source file was modified. (2) DeepSeek-OCR ships no `chat_template`, so verification passed a one-line `--chat-template` that concatenates message content text; this does not touch the image data path. (3) The zero-sum check aggregates over the whole per-step modality batch, so the trigger image must be alone in its encoder batch , trivially true on an idle instance (our verification: first attempt, single request), and simply retried otherwise. ## Details **1. The p

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Severity from
GitHub (reviewed advisory)
Weakness
CWE-617
Also known as
CVE-2026-115914

More vLLM advisories

All vLLM