Summary
vLLM: Loose cache_salt validation lets a single request kill EngineCore on LMCache-MP deployments, uncaught downstream ValueError denial of service
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 loosecache_saltvalidator and unguarded scheduling-path lookup reach.
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), andvllm/entrypoints/openai/responses/protocol.py#L459-L465(field at#L235). The loose test itself is atcompletion #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-L85carries 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(...), insideschedule()at #L396). - The generic top-level handler treats the exception as fatal:
vllm/v1/engine/core.py#L1229-L1235, theexcept 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 e
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
Impact
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.
The application does not adequately validate input before processing it, allowing unexpected values to reach sensitive code paths. Typical impact: varies by context: data corruption, logic bypass, or denial of service.
CVE-2026-105756 has a CVSS score of 6.5 (Medium). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (0.30.0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
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: 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.
Frequently Asked Questions
- What is CVE-2026-105756? CVE-2026-105756 is a medium-severity improper input validation vulnerability in vllm (pip), affecting versions < 0.30.0. It is fixed in 0.30.0. The application does not adequately validate input before processing it, allowing unexpected values to reach sensitive code paths.
- How severe is CVE-2026-105756? CVE-2026-105756 has a CVSS score of 6.5 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of vllm are affected by CVE-2026-105756? vllm (pip) versions < 0.30.0 is affected.
- Is there a fix for CVE-2026-105756? Yes. CVE-2026-105756 is fixed in 0.30.0. Upgrade to this version or later.
- Is CVE-2026-105756 exploitable, and should I be worried? Whether CVE-2026-105756 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-105756 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-105756? Upgrade
vllmto 0.30.0 or later.