CVE-2026-105756: vLLM: Loose `cache_salt` validation lets a single request kill EngineCore on LMCache-MP deployments — uncaught downstream `ValueError` denial of service
On vLLM servers with the built-in LMCache-MP KV connector enabled, one API request with a bad `cache_salt` such as `"/"` raises an uncaught `ValueError` in the scheduler and kills EngineCore, taking the server down for every user. vLLM before 0.30.0 is affected. Upgrade to 0.30.0.
Published Oct 5, 2026 · Analysis Oct 22, 2026
What an attacker can do
The Completions, Chat Completions and Responses request models check cache_salt only as a non-empty string (check_cache_salt_support). With LMCache-MP enabled, the value is stored on LMCacheMPRequestTracker and passed to the LMCache lookup from get_num_new_matched_tokens. The LMCache library is stricter: IPCCacheServerKey.__post_init__ raises ValueError for salts containing @, /, a backslash or NUL, or longer than 128 characters.
Nothing between Scheduler.schedule() and the connector catches that error. It reaches the except Exception in run_engine_core (vllm/v1/engine/core.py), which logs EngineCore encountered a fatal error., calls _send_engine_dead() and re-raises. The engine process dies, in-flight requests fail and /health stops answering.
An attacker needs only a request-capable API account, since the CVSS vector requires low privileges, and one request is enough. The impact is availability only. The pooling base request and the token-in-token-out GenerateRequest also accept cache_salt, the latter with no validator at all. Deployments without the LMCache-MP connector (lmcache >= 0.4.4) are not exposed to this crash.
Who is affected
vllm before 0.30.0, confirmed on 0.25.1, running with the built-in LMCache-MP KV connector enabled and lmcache >= 0.4.4. Without that connector the downstream check never runs. Check both:
pip show vllm lmcacheThen check your serve command or config for the LMCache-MP KV connector setting.
vllm
- Ecosystem
- PyPI
- Affected
- < 0.30.0
- Fixed in
- 0.30.0
- Check
pip show vllm
How to fix it
Upgrade to vLLM 0.30.0:
pip install -U vllm==0.30.0The proposed fix adds one shared validate_cache_salt() helper that rejects @, /, backslash, NUL and salts over 128 characters with a 4xx, and routes every request model with cache_salt through it. A second change should make the lookup failure request-scoped. The advisory lists no workaround. Until you upgrade, a gateway can reject or strip cache_salt from client requests, or you can disable the LMCache-MP connector.
How to tell if you were hit
Look in vLLM logs for EngineCore encountered a fatal error. followed by a ValueError traceback that mentions IPCCacheServerKey or the salt rules, and for /health failing right after a request. Search request logs for cache_salt values containing /, @, a backslash or NUL, or longer than 128 characters. Repeated engine restarts tied to one client point to deliberate abuse.
Original advisory
From the GitHub Advisory Database (CC BY 4.0).
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 the loose
cache_saltvalidator and unguarded scheduling-path lookup reach.
Summary
vLLM's OpenAI-compatible request models (Completions, Chat Completions, Responses) accept a client-supplied cache_salt field and validate it only as "must be a non-empty string" — no character or length restrictions. On a deployment with the built-in LMCache-MP KV connector enabled, that value is stored verbatim on the request tracker and forwarded unguarded as a keyword argument into the scheduler's per-step cache lookup. The downstream LMCache library applies a *stricter* check in IPCCacheServerKey.__post_init__, which raises ValueError for any cache_salt containing @, /, \, or NUL (or longer than 128 characters).
Neither the LMCache-MP connector lookup call site nor Scheduler.schedule() wraps that call in a request-scoped try/except, so the ValueError propagates uncaught into EngineCore's top-level handler, which treats any uncaught exception as fatal and kills the whole engine process. A single publicly reachable request with, for example, cache_salt="/" therefore takes down the engine for all concurrent users. vLLM's boundary validator is looser than the downstream consumer's, and the gap is never converted into a request-scoped failure on the scheduling path.
Affected code
Links pinned to the confirmed commit `752a3a504485` (v0.25.1):
- The three
check_cache_salt_supportvalidators require only a non-empty string — no character or length bound: `vllm/entrypoints/openai/completion/protocol.py#L502-L508` (field at `#L172`), `vllm/entrypoints/openai/chat_completion/protocol.py#L913-L919` (field at `#L425`), and `vllm/entrypoints/openai/responses/protocol.py#L459-L465` (field at `#L235`). The loose test itself is at `completion #L503-L505`, `chat_completion #L914-L916`, `responses #L460-L462`. - Two further request models accept
cache_saltwith the same or weaker checking, and should be hardened at the same time: `vllm/entrypoints/pooling/base/protocol.py#L74-L85` carries the identical non-empty-string-only validator (field at `#L59`), and the token-in-token-out scale-outGenerateRequestexposes the field with nocache_saltvalidator at all: `vllm/entrypoints/scale_out/token_in_token_out/protocol.py#L110`. - The LMCache-MP request tracker stores the salt verbatim: `vllm/distributed/kv_transfer/kv_connector/v1/lmcache_mp_connector.py#L189` (
LMCacheMPRequestTracker), assignment at `#L223`. - The lookup call forwards
cache_saltwith no surroundingtry: `vllm/distributed/kv_transfer/kv_connector/v1/lmcache_mp_connector.py#L733-L774` (get_num_new_matched_tokens→maybe_submit_lookup_request(...)at `#L770`). - The scheduler invokes the connector unguarded: `vllm/v1/core/sched/scheduler.py#L739` (
self.connector.get_num_new_matched_tokens(...), inside `schedule()` at #L396). - The generic top-level handler treats the exception as fatal: `vllm/v1/engine/core.py#L1229-L1235` — the
except Exceptioninrun_engine_core(`#L1154`) logsEngineCore encountered a fatal error., calls_send_engine_dead(), and re-raises. - Downstream strict validator (external LMCache library, not vLLM):
IPCCacheServerKey.__post_init__inlmcache/v1/multiprocess/custom_types.pyrejects@ / \NUL and >128-char salts (_SALT_FORBIDDEN_CHARS = frozenset("@/\\\x00")) by raisingValueError.
The three OpenAI check_cache_salt_support validators are identical; the completion one is representative — a non-empty-string test with no character or length bound:
# vllm/entrypoints/openai/completion/protocol.py Lines 503-509
if data.get("cache_salt") is not None and (
not isinstance(data["cache_salt"], str) or not data["cache_salt"]
):
raise VLLMValidationError(
"Parameter 'cache_salt' must be a non-empty string if provided.",
parameter="cache_salt",
)On the scheduling path the connector is invoked with no surrounding try — a ValueError from the downstream salt check propagates straight out of schedule():
# vllm/v1/core/sched/scheduler.py Lines 736-742
# Get externally-cached tokens if using a KVConnector.
if self.connector is not None:
ext_tokens, load_kv_async = (
self.connector.get_num_new_matched_tokens(
request, num_new_local_computed_tokens
)
)run_engine_core's generic handler — the next except up the stack — treats that as fatal, marks the engine dead, and re-raises:
# vllm/v1/engine/core.py Lines 1229-1235
except Exception as e:
if engine_core is None:
logger.exception("EngineCore failed to start.")
else:
logger.exception("EngineCore encountered a fatal error.")
engine_core._send_engine_dead()
raise eImpact
Availability only. cache_salt is an attacker-controlled, publicly reachable request field that vLLM validates too loosely. A value such as "/" passes vLLM's check, reaches the stricter downstream validator, and its ValueError is never converted into a request-scoped failure — instead it kills the EngineCore process, a denial of service for every concurrent request on that server (HTTP failures, then /health failing).
Applicability: the built-in LMCache-MP KV connector must be enabled (lmcache >= 0.4.4), which is itself an opt-in KV-connector boundary. On such deployments no other special configuration is required, and the crash is a resource-availability failure rather than expected behavior of the opt-in feature.
Suggested Fix
Two independent fixes:
- Tighten admission — add one shared
validate_cache_salt()helper (for example invllm/entrypoints/openai/engine/protocol.py) that matches or exceeds the downstreamIPCCacheServerKeyrules — reject@,/,\, NUL, and >128-character salts at the HTTP boundary with a 4xx — and route every request model that exposescache_saltthrough it: the threecheck_cache_salt_supportvalidators above, the pooling base request, and the token-in-token-outGenerateRequest(which has no validator today). - Defense in depth — wrap the LMCache-MP lookup call reached from
Scheduler.schedule()so a downstream validatorValueErrorbecomes a request-scoped failure instead of an EngineCore-fatal exception. This is the fix that also covers future divergence between vLLM's and LMCache's salt rules; the right failure semantics (fail the request vs. fall back to a cold lookup) is a maintainer design call.
The core of fix 1 is a single shared helper; each check_cache_salt_support body then becomes validate_cache_salt(data.get("cache_salt")), and GenerateRequest gains an equivalent mode="before" validator:
# vllm/entrypoints/openai/engine/protocol.py — new shared helper
_CACHE_SALT_FORBIDDEN_CHARS = frozenset("@/\\\x00")
_MAX_CACHE_SALT_LENGTH = 128
def validate_cache_salt(cache_salt: object) -> None:
"""Validate cache salts before they reach downstream cache backends."""
if cache_salt is None:
return
if not isinstance(cache_salt, str) or not cache_salt:
raise VLLMValidationError(
"Parameter 'cache_salt' must be a non-empty string if provided.",
parameter="cache_salt",
)
if len(cache_salt) > _MAX_CACHE_SALT_LENGTH or any(
char in _CACHE_SALT_FORBIDDEN_CHARS for char in cache_salt
):
raise VLLMValidationError(
"Parameter 'cache_salt' must be at most 128 characters and must "
"not contain '@', '/', '\\\\', or NUL.",
parameter="cache_salt",
)This distinguishes the finding from [GHSA-6qc9-v4r8-22xg](https://github.com/vllm-project/vllm/security/advisories/GHSA-6qc9-v4r8-22xg): that advisory fixed only the guided_json/xgrammar trigger of the same schedule()-into-run_engine_core fatal-handler family, so the cache_salt admission gap and the schedule()-level defense-in-depth (fix 2) survive its published fix. A patch implementing fix 1 across all five request models, with a regression test covering the rejected ("/", 129-char) and accepted salt shapes, applies to v0.25.1 with line offsets and no fuzz.
Credit
Reported by: Patch the Planet (Trail of Bits + OpenAI collaboration)
This vulnerability was discovered using GPT-5.5-Cyber as part of the Patch the Planet security initiative.
---
Proposed fix: a fix for this issue is proposed in a public pull request: https://github.com/vllm-project/vllm/pull/51444
Credited to KernelClint, dhalf, jperezdealgaba.
Questions
- Does every vLLM server crash on a bad cache_salt?
No. The crash needs the built-in LMCache-MP KV connector, because the strict check that raises the error lives in the LMCache library. Other deployments accept the value without failing.
- How many requests does the attack take?
One. A single request with a salt such as
"/"passes vLLM's non-empty-string check, hits the LMCache check, and the uncaughtValueErrortakes the engine down for all users.- Does the flaw expose data or run code?
No. The advisory describes availability impact only.
References
Read for this analysis:
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
