NewApp Monitoring is liveSign up free
AgentbeamBeta
AI agent vulnerability · vLLM

CVE-2026-105756: vLLM: Loose `cache_salt` validation lets a single request kill EngineCore on LMCache-MP deployments — uncaught downstream `ValueError` denial of service

mediumCVSS 3.1 6.5GHSA-2823-qmq8-rwvj

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 lmcache

Then 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.0

The 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_salt validator 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 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

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.

Suggested Fix

Two independent fixes:

  1. Tighten admission — add one shared validate_cache_salt() helper (for example in vllm/entrypoints/openai/engine/protocol.py) that matches or exceeds the downstream IPCCacheServerKey rules — reject @, /, \, NUL, and >128-character salts at the HTTP boundary with a 4xx — and route every request model that exposes cache_salt through it: the three check_cache_salt_support validators above, the pooling base request, and the token-in-token-out GenerateRequest (which has no validator today).
  2. Defense in depth — wrap the LMCache-MP lookup call reached from Scheduler.schedule() so a downstream validator ValueError becomes 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 uncaught ValueError takes the engine down for all users.

Does the flaw expose data or run code?

No. The advisory describes availability impact only.

References

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 entries