VLLM · GHSA-6pgw-f3pv-4h8q · AI agents & MCP
FIX → 0.24.0
The vLLM Rust gRPC Generate and GenerateStream endpoints accept more than 128 output candidate token IDs without the validation the Python API enforces. A single request with output logprobs enabled can make the engine core terminate, which is a denial of service.
first public Oct 9, 2026 · 1 source
Could this affect your app?
Free first check from your website. Connect your repo for the full answer.
Teams that run vLLM with the Rust gRPC frontend reachable by clients, where a request can set output logprobs with explicit candidate token IDs. The sources state the attack needs only an unauthenticated request in the default case, though the advisory also models a conservative case where clients must be authorized. Deployments that do not expose the gRPC endpoint, or that restrict it behind a credential layer, are less exposed.
Not affected if default-config, no-grpc-frontend
Who cares grpc, python, self-hosted, vllm
pip show vllm; versions >= 0.22.0, < 0.24.0 are affected.Upgrade to 0.24.0
vllm to 0.24.0.The Rust gRPC Generate and GenerateStream API accepts ResponseOptions.output_candidates.token_ids and lowers the supplied list directly into SamplingParams.logprob_token_ids without enforcing the Python SamplingParams validator.
Python explicitly caps logprob_token_ids at MAX_LOGPROB_TOKEN_IDS = 128, range-checks each token ID against model vocabulary, and requires logprobs == len(logprob_token_ids) when both are set. The V1 GPU logprob sampler allocates a fixed (max_num_reqs, MAX_LOGPROB_TOKEN_IDS) row and raises ValueError if a request carries more than 128 logprob_token_ids.
The Rust gRPC converter bypasses that intended guard. A caller can request output logprobs with 129 or more explicit candidate token IDs. The Rust frontend accepts the request, stores the unbounded list in , and sends it toward the Python engine-core boundary, where the lower sampler path can raise after the request has entered execution. EngineCore treats uncaught worker exceptions as fatal engine errors.
Check it with your coding agent
It reads this page and tells you if your code is affected.
Are you affected?
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
SamplingParamsThe protobuf exposes explicit output candidate token IDs:
optional CandidateTokens output_candidates = 7;
message CandidateTokens {
oneof select {
uint32 top_n = 1;
TokenIds token_ids = 2;
bool all = 3;
}
}
The Rust gRPC converter maps output_candidates.token_ids directly into SamplingParams.logprob_token_ids:
if r.output_logprobs {
let (count, token_ids) = candidate_logprob_spec(r.output_candidates.as_ref());
params.logprobs = Some(count);
params.logprob_token_ids = token_ids;
}
Some(pb::candidate_tokens::Select::TokenIds(ids)) => (1, Some(ids.ids.clone())),
There is no length check against MAX_LOGPROB_TOKEN_IDS, no vocabulary range-check, and no logprobs == len(logprob_token_ids) consistency check.
By contrast, Python SamplingParams explicitly rejects the same unsafe state:
MAX_LOGPROB_TOKEN_IDS = 128
"""Upper bound on `SamplingParams.logprob_token_ids` list length. Must match
the per-request row width allocated by the sampler's `LogprobTokenIdsState`."""
if self.logprob_token_ids is not None:
n = len(self.logprob_token_ids)
if n > MAX_LOGPROB_TOKEN_IDS:
raise VLLMValidationError(...)
The lower GPU logprob sampler confirms this is not only a presentation-layer preference. It allocates a fixed-width row and raises if an oversized list reaches add_request():
self.token_ids = StagedWriteTensor(
(max_num_reqs, MAX_LOGPROB_TOKEN_IDS),
dtype=torch.int32,
device=device,
)
if n > MAX_LOGPROB_TOKEN_IDS:
raise ValueError(
f"Too many logprob_token_ids: {n}. The max is {MAX_LOGPROB_TOKEN_IDS}."
)
The intended behavior is visible in three independent places.
First, the Python API documents and enforces MAX_LOGPROB_TOKEN_IDS = 128, with the comment that the bound must match the sampler row width.
Second, the Rust gRPC converter already recognizes that candidate token ID selectors need special handling. It explicitly rejects prompt_candidates.token_ids with INVALID_ARGUMENT because the shared engine-core protocol cannot safely represent separate prompt candidate IDs:
"prompt_candidates token_ids selector is not supported"
Third, the lower sampler path is fixed-width and has a defensive runtime check. Allowing Rust gRPC to bypass the northbound validator and rely on a late worker exception is the unsafe state; the correct behavior is early request validation.
This is also distinct from the previously submitted Rust gRPC prompt token-ID range issue (this report / GHSA-w2v2-5662-8v5q). That report covers out-of-vocabulary prompt IDs submitted as the request prompt. This report covers the output-logprob candidate selector lowering into SamplingParams.logprob_token_ids with no count, vocabulary, or consistency validation.
An attacker who can reach a vLLM Rust gRPC endpoint can submit a generation request with output_logprobs = true and output_candidates.token_ids containing more than 128 IDs.
Expected impact is a single-request denial of service for affected deployments: the request is accepted by the Rust gRPC frontend, reaches the engine logprob sampler state, and can terminate EngineCore. Fixed behavior should be an immediate INVALID_ARGUMENT response before any engine request is submitted.
This report does not claim code execution, data access, model compromise, persistence, or public internet exposure. It is an availability issue caused by missing Rust gRPC input validation.
Suggested severity: High if the Rust gRPC endpoint is network-reachable without an additional deployment-layer credential requirement.
Suggested CVSS v3.1:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
If maintainers model the affected gRPC surface as reachable only to already-authorized clients, the conservative score is:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
Suggested CWEs:
CWE-20: Improper Input ValidationCWE-400: Uncontrolled Resource ConsumptionCWE-770: Allocation of Resources Without Limits or ThrottlingValidate ResponseOptions.output_candidates.token_ids before assigning params.logprob_token_ids.
Recommended patch shape:
MAX_LOGPROB_TOKEN_IDS = 128;output_candidates.token_ids when ids.len() > MAX_LOGPROB_TOKEN_IDS;params.logprobs consistent with the explicit ID count, or reject inconsistent combinations instead of lowering them;Status::invalid_argument(...) from the gRPC converter before any engine request is submitted; andGenerate and streaming GenerateStream regression tests for 128 accepted IDs, 129 rejected IDs, and out-of-vocabulary candidate IDs.The same fix should preserve the existing prompt_candidates.token_ids rejection unless maintainers add a safe protocol representation for it.
https://github.com/vllm-project/vllm/securityhttps://docs.vllm.ai/en/latest/contributing/vulnerability_management/https://docs.vllm.ai/en/latest/usage/security/SamplingParams API docs: https://docs.vllm.ai/en/latest/api/vllm/sampling_params/https://docs.vllm.ai/en/stable/api/vllm/v1/worker/gpu/sample/logprob/I checked local vLLM submission bundles, private-visible vLLM advisories, and GitHub PR/issue search for logprob_token_ids, CandidateTokens, output_candidates, ResponseOptions, and gRPC logprob variants.
Nearby but distinct:
this report / GHSA-w2v2-5662-8v5q: Rust gRPC accepts out-of-vocabulary prompt.token_ids. That is a prompt-token range-validation bug. It does not package the output_candidates.token_ids length bypass or LogprobTokenIdsState fixed-row failure path.this report / GHSA-mmrv-74wf-6pxm: Rust frontend sampler-control length bounds remain missing after PR #45390. This report is narrower and covers the gRPC response-candidate selector that bypasses the explicit logprob_token_ids Python validator.The GitHub advisory API returned only the prior this report gRPC prompt-token advisory for these terms. Public GitHub PR and issue searches for the exact candidate-selector/logprob-token path returned no matches.
vllm (PyPI).>= 0.22.0, < 0.24.0.0.24.0.Advisory fixed in #45674.