vLLM DeepSeek-OCR image processing vulnerable to denial of service (CVE-2026-115914)
Published Last modified Sources: OSV, Hersteller, CVE.org
What is affected, what to do
Update vllm to version 0.32.0.
- Affected
- vLLM
gte 0.11.1, < 0.32.0..*
- vLLM
Not sure whether and how you are affected? Ask a question about this vulnerability
Metrics
6.5
Priority
XIEM Effective CVSS: highest available CVSS rating from NVD and vendor data.
Source: Vendor (CNA), CVSS v3
6.5
CVSS v3
Severity Medium on the scale from 0 to 10.
Source: OSV, Hersteller, CVE.org
0.4 %
EPSS score
Low — model estimates < 1% exploitation likelihood.
Source: FIRST EPSS, percentile 32.8
Show all metrics
- Severity
- Medium
Priority (0–325)
The priority score is a consolidated metric that condenses several individual signals into a single, action-guiding ranking: technical severity (CVSS), exploitation likelihood (EPSS), known-exploited status (KEV) and the context of the affected asset. The aim is to answer the only truly important question in vulnerability management: what first?
Read in glossary- 71.13 Methodology
Exploit maturity
An exploit is code or a technique that specifically uses a concrete vulnerability to force unintended behaviour — such as executing code or escalating privileges. It is the tool that turns a theoretical flaw into a practical attack.
Read in glossary- no public PoC known
XIEM Effective CVSS
XIEM® is the tiered managed-security operating model from NEOSEC and a registered trademark of NEOSEC GmbH. It extends classic SIEM — the central collection, normalisation and correlation of security-relevant events — with endpoint forensics, network visibility and deception technology, and consolidates them into three building tiers: Sentry, Orchestrate and Command.
Read in glossary- 6.5 Methodology
CVSS v3
CVSS is the globally established, open standard for assessing the technical severity of a vulnerability on a scale from 0 to 10. It describes how easily a flaw can be exploited and what damage it can cause — but not how likely its actual exploitation is. CVSS and the complementary EPSS deliberately answer different questions.
Read in glossary- 6.5
EPSS score
The EPSS score is the absolute model output for an individual vulnerability: the estimated probability of real-world exploitation within the next 30 days, expressed as a value between 0 and 1 (i.e. 0 % to 100 %). A score of 0.42 means „the model estimates a 42 % chance of real-world exploitation within 30 days“.
Read in glossary- 0.4 %
- Low — model estimates < 1% exploitation likelihood.
EPSS percentile
The EPSS percentile is the relative position of a vulnerability within all EPSS-scored CVEs, expressed as a value between 0 and 1. A percentile of 0.90 means „this CVE has a higher EPSS score than 90 % of all other scored CVEs“. Unlike the EPSS score, it is not a probability — but a positioning within a ranking.
Read in glossary- 32.8
- Elevated — this CVE ranks above at least 10% of all CVEs scored today.
KEV status
The KEV catalogue is a list maintained by the US authority CISA of vulnerabilities for which active exploitation in the wild has been confirmed. It answers not the question „how severe?“ but „is it already being attacked?“ — thereby sharply prioritising patching.
Read in glossary- —
- Published
CWE
CWE is a catalogue of typical weakness types in software and hardware — such as flawed input validation, unsafe memory handling or poor authentication. Where a CVE names a concrete vulnerability in a concrete product, a CWE describes the underlying class of defect.
Read in glossary- CWE-617
Detection rules
No NEOSEC detection rule is available for this vulnerability yet.
Classification under NIS2 and CRA
Rule-based classification from exploitation status and severity. It does not replace a case-by-case assessment.
NIS2
NIS2 is EU Directive (EU) 2022/2555 to raise the level of cybersecurity across the Union. It substantially broadens obligations compared with the first NIS directive: more sectors, mandatory risk management, staged reporting duties and personal accountability of senior management.
Read in glossary- Handle the vulnerability in regular vulnerability handling under Art. 21(2)(e) NIS2.
CRA
The CRA is EU Regulation (EU) 2024/2847 setting horizontal cybersecurity requirements for „products with digital elements“ — from industrial controllers and software to connected consumer devices. For the first time manufacturers must ensure security across the entire product lifecycle, handle vulnerabilities and provide security updates.
Read in glossary- From 11.12.2027, Annex I Part II CRA requires manufacturers of affected products with digital elements to handle vulnerabilities without delay and provide security updates.
ISO/IEC 27001
ISO/IEC 27001 is the internationally leading standard for an information security management system (ISMS). It describes a risk-based, process-oriented framework to systematically plan, implement, review and improve information security.
Read in glossary- Control A.8.8, management of technical vulnerabilities: assess relevance to your systems, plan remediation and keep evidence.
Description
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:
# 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:
# 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 processor maps an ordinary binary image to exactly ±1.0 pixels. DeepseekOCRProcessor.tokenize_with_images (vllm/transformers_utils/processors/deepseek_ocr.py:338-389) builds the global view as ImageOps.pad(image, (base_size, base_size), color=mean*255) with base_size = 1024 (:21). For a 1024×1024 input the pad is a no-op (same-size ImageOps.contain resampling is the identity: the bicubic kernel passes through sample points exactly). ImageTransform (:120-140) then applies T.ToTensor() (uint8 0 → 0.0, 255 → 1.0, exact in float32) and T.Normalize((0.5,)*3, (0.5,)*3) (0.0 → −1.0, 1.0 → +1.0, exact). A left-half-black/right-half-white 1024×1024 RGB image therefore produces 1,572,864 elements of −1.0 and 1,572,864 of +1.0; torch.sum (pairwise/tree reduction, partial sums bounded by 1.58M ≪ 2²⁴) evaluates to exactly 0.0, and .item() == 0 is True. All of this runs in the API process and passes every validation layer: the image is 1 MP, far under the default VLLM_MAX_IMAGE_PIXELS cap of 178,956,970 (vllm/envs.py:86).
2. The zero guard turns a valid image into a None encoder output. _parse_and_validate_image_input (deepseek_ocr.py:468-497) returns None on the exact-zero sum; embed_multimodal (:606-611) propagates that None to its caller. The same pattern exists in deepseek_ocr2.py:339 (if pixel_values is None or torch.sum(pixel_values).item() == 0: return None), and UnlimitedOCRForCausalLM (unlimited_ocr.py:244) inherits the method unmodified. (A sibling check in _encode_local_features at deepseek_ocr.py:522-523 guards crop tensors, which pass through a lossy resize and are not needed for this report.)
3. The worker asserts on the None and the engine dies. With the default configuration (cudagraph_mm_encoder=False, config/compilation.py), the V2 model runner executes the encoder eagerly: execute_model (v1/worker/gpu/model_runner.py:1849) → model_states/default.py:108 prepare_inputs_embeds → encoder_runner.execute_mm_encoder (v1/worker/gpu/mm/encoder_runner.py:168) → model.embed_multimodal(**mm_kwargs_batch) → None → sanity_check_mm_encoder_outputs(batch_outputs, expected_num_items=1) (encoder_runner.py:170) → AssertionError at v1/worker/utils.py:512. There is no per-request try/except anywhere on this path; the exception reaches the busy loop's top-level except Exception at core.py:1368 (via step_with_batch_queue at core.py:660 and collective_rpc), which logs "EngineCore encountered a fatal error.", sends engine-dead, and re-raises (fault tolerance off by default; engine_core_sentinel re-raises). The API server's output_handler receives EngineDeadError (async_llm.py:843) and shuts down. In the verified run the container exited (code 0, OOMKilled=false) and the port stayed closed — no self-recovery.
4. Why the guard exists and why it is wrong here. The torch.sum(...) == 0 → return None shortcut comes from the upstream DeepSeek-OCR reference implementation, where it skipped empty batches during batch inference. In vLLM, embed_multimodal is only invoked when the scheduler has real multimodal items to encode, so the guard never protects anything — it is purely a landmine that converts one specific, perfectly legal pixel distribution into a contract violation (embed_multimodal must return embeddings, not None) that the engine treats as a fatal invariant breach.
Version history. The check was introduced together with the model itself in #27247 ("[Model] Upstream Deepseek-OCR model", commit 675aa2ec, 2025-10-22; first stable release v0.11.1) and is unchanged since. DeepSeek-OCR-2 support (#33165, 2026-02-02) carries the same check at deepseek_ocr2.py:339. On main (checked 2026-09-30) the guard in _parse_and_validate_image_input is still present verbatim.
Impact
Any unauthenticated client that can reach the OpenAI-compatible API of a deployment serving DeepSeek-OCR, DeepSeek-OCR-2, or Unlimited-OCR can **
Source: OSV (Open Source Vulnerabilities), GitHub Security Advisories, NVD (NIST), cvelistv5
Affected products
The following versions are affected. Older releases of the same product line are also vulnerable unless stated otherwise.
| Affected versions | Fixed in | ||
|---|---|---|---|
| vllm-project | vllm |
|
|
| vllm | vllm |
|
|
| pip | vllm |
|
|
| pypi | vllm |
|
|
Vendors and products
Weakness classes (CWE)
CWE-617Reachable Assertion
Base
The product contains an assert() or similar statement that can be triggered by an attacker, which leads to an application exit or other behavior that is more severe than necessary.
Entry at MITRE CWE-617
References & sources
- github.comvllm-project/vllm/commit/c15672cfb49152ebf65cfd188e71b84593ec56…x_refsource_MISC, Patch, web
- github.comvllm-project/vllm/pull/59417x_refsource_MISC, Patch, web
- github.comvllm-project/vllm/releases/tag/v0.32.0x_refsource_MISC, Release notes, web
- github.comvllm-project/vllm/security/advisories/GHSA-gq5q-89c7-vhvxx_refsource_CONFIRM, Patch, Vendor advisory, web
- NVD (NIST)vuln/detail/CVE-2026-115914advisory
- github.comvllm-project/vllmpackage
Changes to this CVE
Who changed this CVE and when: NVD (analysis, affected products, CVSS), the CVE publisher (CNA) and additional providers such as CISA (ADP).
Initial analysis by NVDNVD
Newly listed as affected:- vLLM (0.11.1 – before 0.32.0)
Show 5 original changes
Individual changes of this event as published by NVD Type Action Before After Product configuration added —OR *cpe:2.3:a:vllm:vllm:*:*:*:*:*:*:*:* versions from (including) 0.11.1 up to (excluding) 0.32.0Reference type added —GitHub, Inc.: https://github.com/vllm-project/vllm/commit/c15672cfb49152ebf65cfd188e71b84593ec5686 Types: PatchReference type added —GitHub, Inc.: https://github.com/vllm-project/vllm/pull/59417 Types: PatchReference type added —GitHub, Inc.: https://github.com/vllm-project/vllm/releases/tag/v0.32.0 Types: Release NotesReference type added —GitHub, Inc.: https://github.com/vllm-project/vllm/security/advisories/GHSA-gq5q-89c7-vhvx Types: Patch, Vendor AdvisorySource identifier: nvd@nist.gov
ReceivedGitHub
CVSS V3.1: AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Weakness type: CWE-617
Description added or changed
Show 8 original changes
Individual changes of this event as published by NVD Type Action Before After Description added —vLLM is an inference and serving engine for large language models. From 0.11.1 until 0.32.0, the DeepSeek-OCR, DeepSeek-OCR-2, and Unlimited-OCR image path allows a remote client that can reach the OpenAI-compatible API to submit a legal image whose normalized pixels sum to zero when that image is alone in its per-step encoder batch. _parse_and_validate_image_input returns no embedding, embed_multimodal propagates that value, and sanity_check_mm_encoder_outputs triggers a fatal assertion that shuts down the engine for all users. This issue is fixed in version 0.32.0.CVSS added —AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:HWeakness type (CWE) added —CWE-617Affected versions (publisher) added —New affected value received. <a href="https://github.com/CVEProject/cvelistV5/blob/main/cves/2026/115xxx/CVE-2026-115914.json">CVE-2026-115914</a>Reference added —https://github.com/vllm-project/vllm/commit/c15672cfb49152ebf65cfd188e71b84593ec5686Reference added —https://github.com/vllm-project/vllm/pull/59417Reference added —https://github.com/vllm-project/vllm/releases/tag/v0.32.0Reference added —https://github.com/vllm-project/vllm/security/advisories/GHSA-gq5q-89c7-vhvxSource identifier: security-advisories@github.com
Embed badge
Show the severity and KEV status of this vulnerability on your site – with a link back here. The image is updated hourly.
<a href="https://intel.neosec.eu/en/cve/CVE-2026-115914"><img src="https://intel.neosec.eu/badge/cve/CVE-2026-115914.svg?lang=en" alt="CVE-2026-115914 – NEOSEC Intel" height="20"></a>