Résumé
vLLM: Structured-output request errors escape the request boundary and terminate the shared EngineCore — engine-fatal denial of service (3 sites)
Détails de l’avis
Affected
- Ecosystem / package: pip /
vllm - Affected versions: vLLM ≤ 0.25.1 (confirmed on 0.25.1, commit
752a3a504485). The lower bound predates 0.25.1; maintainers can confirm how far back each path reaches.
Summary
Three structured-output request paths let an ordinary request reach a condition that raises an uncaught, engine-fatal exception instead of a per-request validation error. The failure is not confined to the request that caused it: it escapes into EngineCore's busy loop and triggers a fatal _send_engine_dead(), so one malformed structured-output request denies service to all concurrent and subsequent tenants of that engine. The requests are ordinary API calls; the client does not need any privileged configuration beyond the (often default) structured-output feature.
The shared root cause is the absence of a per-request exception boundary around structured-output grammar/token handling: a value that should fail as a request-scoped validation error instead propagates as an uncaught exception (or bypasses frontend validation entirely) and reaches the scheduler/engine loop, which treats the failure as fatal.
These sites are distinct from the published fixes for GHSA-6qc9-v4r8-22xg and GHSA-8wr5-jm2h-8r4f: GHSA-6qc9 (PR #17623) added frontend json-schema/regex/type validation but the completion check on v0.25.1 still catches only TimeoutError, so Site 1's valid duplicate-root EBNF via the latched xgrammar backend still re-raises and kills EngineCore; GHSA-8wr5 (PR #44744) fixes recovered-token state in the eagle spec-decode path and explicitly does not treat trailing -1 padding as the fault, so Site 2's -1 reaching backend_guidance.validate_tokens() via ngram_gpu survives it.
Affected code
Links pinned to the confirmed commit 752a3a504485 (v0.25.1). The three sites are:
Site 1 — latched-backend compile exception escapes. StructuredOutputManager permanently latches one backend on the first structured-output request and always compiles through it, ignoring the per-request auto backend selection. A grammar that auto accepts only via a fallback backend (e.g. a duplicate-root EBNF) then makes the latched xgrammar compiler raise; the exception is stored in the grammar Future, and the completion check catches only TimeoutError, so Future.result() re-raises during scheduler promotion and kills EngineCore.
- Backend defaults to
"auto":vllm/config/structured_outputs.py#L21. - The
autovalidator tries xgrammar and silently falls back, recording the resolved backend on the request:vllm/sampling_params.py#L1004-L1035(fields at#L85-L88). - The manager latches one backend and always compiles through it:
vllm/v1/structured_output/__init__.py#L127-L159and_create_grammar()at#L173-L184. - The xgrammar sink calls the native compiler unguarded:
vllm/v1/structured_output/backend_xgrammar.py#L78-L110(grammar path at#L90). - The completion check catches only
TimeoutError:vllm/v1/structured_output/request.py#L48-L63(result(timeout=0.0001)at#L55,except TimeoutErrorat#L57). - Scheduler promotion dereferences the grammar with no exception boundary:
vllm/v1/core/sched/scheduler.py#L2441-L2460(reached fromschedule()at #L396). - The uncaught exception is treated as fatal:
vllm/v1/engine/core.py#L1229-L1234(_send_engine_dead()at#L1470).
The escaping-exception sink: the completion check only handles TimeoutError, so any compile exception stored in the Future re-raises out of result() during scheduler promotion.
# vllm/v1/structured_output/request.py Lines 48-59
def _check_grammar_completion(self) -> bool:
# NOTE: We have to lazy import to gate circular imports
from vllm.v1.request import RequestStatus
if isinstance(self._grammar, Future):
try:
# We will check whether the future is ready within 100 us
self._grammar = self._grammar.result(timeout=0.0001)
self.status = RequestStatus.WAITING
except TimeoutError:
return False
return True
Site 2 — guidance treats ngram_gpu -1 padding as a token id. The ngram_gpu proposer pads a fixed-width draft-token row with -1 and separately records num_valid_draft_tokens, but the scheduler forwards the untrimmed padded row into GuidanceGrammar.validate_tokens(), which passes -1 to the native llguidance matcher; the matcher cannot convert a negative value to its unsigned token type and raises an OverflowError, uncaught and fatal.
- The proposer fills with
-1and computesnum_valid_draft_tokens:vllm/v1/spec_decode/ngram_proposer_gpu.py#L189-L209. _get_draft_token_ids_cpu()materializes the full padded row, not trimmed to the valid count:vllm/v1/worker/gpu_model_runner.py#L4835-L4849.- Two scheduler call sites pass the padded row into
validate_tokens(...):vllm/v1/core/sched/scheduler.py#L1967and#L1997. - The sink has no negative-value filter:
vllm/v1/structured_output/backend_guidance.py#L181-L192(validate_tokens(tokens)at [#L192](https://github.com/vllm-project/vllm/blob/752a3a504485790a2e8491c
Références
- https://github.com/advisories/GHSA-85xf-c7hm-whqw
- https://github.com/vllm-project/vllm/security/advisories/GHSA-85xf-c7hm-whqw
- https://github.com/vllm-project/vllm/pull/51450
- https://github.com/vllm-project/vllm/commit/c55e15a44ec4127832d4a86928a356fdd9e68dbd
- https://github.com/vllm-project/vllm/releases/tag/v0.30.0
Vulnérabilités liées
Tout Supply chain →- MEDIUMCVE-2026-100347
kin-openapi: Unauthenticated panic (DoS) in openapi3filter legacy router via non-canonical/unsupported HTTP method on a static path
- MEDIUMCVE-2026-41508
Coraza: Truncated multipart body bypasses MULTIPART_STRICT_ERROR (rule 200003) via silent io.ErrUnexpectedEOF handling
- MEDIUMCVE-2026-105756
vLLM: Loose `cache_salt` validation lets a single request kill EngineCore on LMCache-MP deployments — uncaught downstream `ValueError` denial of service
- HIGHGHSA-x5rw-q4pp-hg5g
devalue: stringifyAsync can cause an unhandled rejection despite a caught returned promise
- MEDIUMCVE-2026-59162
Excelize: Negative shared-string index causes panic in GetCellValue and GetRows
- MEDIUMCVE-2026-54529
SQLAdmin: Unvalidated sortBy parameter in `ModelView` bypasses `column_sortable_list`