CVE-2026-73559: vLLM: Completion prompt lists fan out into unbounded engine requests
vLLM's `/v1/completions` endpoint does not limit how many prompts a request may carry. One authenticated request with a huge `prompt` list creates one engine request per entry and can exhaust CPU and memory, starving other users of the server. Versions 0.19.0 up to 0.26.0 are affected. Upgrade to 0.26.0.
Published Aug 13, 2026 · Analysis Oct 25, 2026
What an attacker can do
CompletionRequest.prompt accepts list[str] or list[list[int]] with no cap on the outer list. prompt_to_seq() returns such a list unchanged. OnlineRenderer.preprocess_completion() parses every element. The serving code in vllm/entrypoints/openai/completion/serving.py then creates one engine generator per prompt, merges them with merge_async_iterators, and allocates a result slot per prompt.
The per-prompt token and sampling limits still apply, so the attacker keeps each entry tiny. A body like {"prompt": ["x", "x", ...], "max_tokens": 1, "n": 1} scaled to tens or hundreds of thousands of entries makes the server allocate, tokenize, schedule, merge and buffer that many subrequests. A scalar prompt is wrapped as one and is fine.
The client needs API access (low privileges in the CVSS vector). On a vLLM server shared between teams or customers, a single user can slow or take down service for everyone else. The report claims no unauthenticated access, data exposure or code execution. If /v1/completions is reachable only by trusted callers, the risk is small.
It is distinct from the earlier unbounded-n advisory, GHSA-3mwp-wvh9-7528, because n stays at 1.
Who is affected
PyPI package vllm, versions >= 0.19.0, < 0.26.0. Only servers that expose the OpenAI-compatible /v1/completions endpoint to untrusted or semi-trusted clients matter in practice. Check the version:
pip show vllmvllm
- Ecosystem
- PyPI
- Affected
- >= 0.19.0, < 0.26.0
- Fixed in
- 0.26.0
- Check
pip show vllm
How to fix it
Upgrade to 0.26.0, the patched version:
pip install --upgrade "vllm>=0.26.0"The fix is in pull request 47845 and commit 675f4295cdfe0d870471c2b51bfeca3a68a9569e. The reporter's suggested approach is an outer prompt-count limit checked before preprocessing expands the list. The sources give no workaround for older versions. Until you upgrade, put a request body size limit and a per-client rate limit on the reverse proxy in front of vLLM, and do not expose /v1/completions to untrusted clients.
How to tell if you were hit
Look in the proxy or server access logs for /v1/completions requests with unusually large bodies, and for a sudden surge in the number of engine requests from a single API key. On the host, watch for CPU and memory growth, a long queue of waiting requests, and OOM kills of the vLLM process shortly after a completions call. Inspect request payloads for prompt arrays with thousands of short entries.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
Summary
The /v1/completions request model accepts prompt as a list of text prompts or a list of token-id prompts without any outer prompt-count bound. The serving path turns each element into a separate engine input, creates one engine generator per element, merges all generators, and allocates a response slot per prompt. An authenticated API client can therefore turn one request into an attacker-chosen number of backend subrequests before any aggregate request-count budget is enforced.
Technical Details
CompletionRequest.prompt allows both list-shaped prompt inputs and scalar prompts:
# vllm/entrypoints/openai/completion/protocol.py
prompt: (
list[Annotated[int, Field(ge=0)]]
| list[list[Annotated[int, Field(ge=0)]]]
| str
| list[str]
| None
) = NoneThe validator only requires some prompt-like input to be present:
def validate_prompt_and_prompt_embeds(cls, data):
prompt = data.get("prompt")
prompt_embeds = data.get("prompt_embeds")
...
if prompt_is_empty and embeds_is_empty:
raise VLLMValidationError(...)The renderer then expands list-shaped prompts as a sequence. prompt_to_seq() wraps a scalar string or a single token-id list, but returns a list[str] or list[list[int]] unchanged:
# vllm/renderers/inputs/preprocess.py
def prompt_to_seq(prompt_or_prompts):
if isinstance(prompt_or_prompts, (dict, str, bytes)) or (
len(prompt_or_prompts) > 0 and is_list_of(prompt_or_prompts, int)
):
return [prompt_or_prompts]
return prompt_or_promptsOnlineRenderer.preprocess_completion() appends that whole sequence, and the renderer processes every element:
# vllm/renderers/online_renderer.py
prompts = list[SingletonPrompt | bytes]()
if prompt_input is not None:
prompts.extend(prompt_to_seq(prompt_input))
...
parsed_prompts = [
prompt if isinstance(prompt, bytes) else parse_model_prompt(model_config, prompt)
for prompt in prompts
]
return await renderer.render_cmpl_async(parsed_prompts, tok_params, ...)Finally, completion serving creates one backend generator and one response slot per rendered prompt:
# vllm/entrypoints/openai/completion/serving.py
generators: list[AsyncGenerator[RequestOutput, None]] = []
for i, engine_input in enumerate(engine_inputs):
...
generator = self.engine_client.generate(...)
generators.append(generator)
result_generator = merge_async_iterators(*generators)
num_prompts = len(engine_inputs)
...
final_res_batch: list[RequestOutput | None] = [None] * num_promptsThe violated invariant is that one HTTP request should have a bounded backend request count. Current code enforces per-prompt token and sampling limits, but not the number of prompts in the outer completion request.
PoV
A minimal oversized request keeps normal generation parameters small but supplies a large outer prompt list:
{
"model": "served-model",
"prompt": ["x", "x", "x"],
"max_tokens": 1,
"n": 1
}Scaling the prompt array to tens or hundreds of thousands of short entries makes the server allocate, preprocess, schedule, merge, and buffer one subrequest per entry. The same applies to token-id prompt lists:
{
"model": "served-model",
"prompt": [[1], [1], [1]],
"max_tokens": 1,
"n": 1
}The intended negative control is a scalar prompt:
{
"model": "served-model",
"prompt": "x",
"max_tokens": 1,
"n": 1
}The scalar string is wrapped as one prompt; the list form is not bounded and fans out by list length.
Impact
An authenticated API client can make one /v1/completions request consume CPU, memory, async task scheduling, engine request slots, and response buffering proportional to an attacker-chosen outer prompt list. This can starve or disrupt other tenants sharing the same vLLM server. The report does not claim unauthenticated access, confidentiality impact, integrity impact, code execution, or impact where /v1/completions is not reachable by untrusted or semi-trusted clients.
Suggested Fix
Reject oversized prompt lists before renderer preprocessing. Add an outer prompt-count limit to CompletionRequest.prompt when the prompt is list[str] or list[list[int]], and consider making the limit configurable in the same style as the batch-chat and sampling-list bounds. The check should run before OnlineRenderer.preprocess_completion() expands the prompt sequence, so oversized requests do not allocate parsed prompt lists, async render/tokenization tasks, engine generators, or response result slots.
Regression coverage should include a scalar prompt, a bounded prompt list, an oversized list[str], and an oversized list[list[int]]. The oversized requests should fail with a controlled validation error before any backend generator is created.
Affected Package/Versions
Package ecosystem: pip
Package name: vllm
Affected range confirmed by source proof: >=0.19.0, <=0.24.0; current main at cbe9c40f998f13975b967773ac7e7920e115387f remains affected.
Patched versions: unknown.
Latest release checked: v0.24.0, published on 2026-06-29.
GitHub Advisory Metadata
Package ecosystem: pip
Package name: vllm
Vulnerable version range: >=0.19.0, <=0.24.0
Patched versions: unknown
Advisory History
Public issue and PR searches for CompletionRequest prompt list, "prompt" "list[str]" "completion", and "CompletionRequest" "max_length" did not find an existing report or fix for this exact path.
The closest published advisory is GHSA-3mwp-wvh9-7528, "OOM Denial of Service via Unbounded n Parameter in OpenAI API Server", patched in 0.19.0. This report is distinct because it keeps n=1 and uses the /v1/completions prompt outer list to create one engine request per prompt. The fix invariant is an outer prompt-count and aggregate request budget, not only a generated-sequence-count cap.
The closest public PR is vllm-project/vllm#45390, which covers multiple DoS fixes including GHSA-83mh-6mwq-3hg9 for BatchChatCompletionRequest.messages. That PR adds an outer bound to batch chat conversations, but its diff does not touch vllm/entrypoints/openai/completion/protocol.py or vllm/entrypoints/openai/completion/serving.py.
Prior local/private report families checked included pooling/rerank batch fanout, derender token-id postprocessing, explicit truncation_side tokenizer-limit bypass, Python disaggregated generate prompt-length bypass, and priority scheduling. Those reports differ by endpoint, attacker-controlled field, sink, and fix surface.
Credited to rexpository, jperezdealgaba, DarkLight1337.
Questions
- Is authentication required to exploit this?
Yes. The report describes an authenticated API client and does not claim unauthenticated access. If the server runs with no API key, anyone who can reach it counts as a client.
- How is it different from the unbounded n parameter issue?
That one used
nto create many sequences. This one keepsn=1and uses the outerpromptlist to create one engine request per entry.- Does it allow code execution or data theft?
No. The advisory says the impact is availability only: CPU, memory, task scheduling and response buffering.
- Which vLLM version fixes it?
0.26.0. The reporter confirmed the flaw from 0.19.0 through 0.24.0 and on main at the time.
References
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology
More vLLM vulnerabilities
All vLLM entriesCVE-2026-114050
vLLM: Forged multimodal UUID + P0/P1 processor-cache eviction drift: unauthenticated remote crash of the whole vLLM engine
medium6.5Oct 23, 2026CVE-2026-114055
vLLM : DoS via unvalidated mm_processor_kwargs in multimodal image processors (Isaac, InternVL, H2OVL, etc.)
high7.5Oct 23, 2026CVE-2026-105752
vLLM: Harmony tool continuations drop `cache_salt` — restoring a cross-tenant prefix-cache membership oracle
low3.1Oct 6, 2026CVE-2026-105753
vLLM: Mirrored multimodal IPC caches desync after a rejected request — a later request reusing the same media hash trips a receiver assertion in the engine core
medium6.5Oct 6, 2026CVE-2026-105754
vLLM: Scale-out disaggregated multimodal transport trusts caller-supplied features
medium6.5Oct 5, 2026CVE-2026-105755
vLLM: Flash late-interaction scoring caches query embeddings under a caller-controlled request id — cross-request integrity break and induced errors on `/score` and `/rerank`
medium4.2Oct 5, 2026
