The Cache Speaks Pickle: LMCache CVE-2026-105192 Executes Before a Handler Ever Runs
LMCache has now collected three critical CVEs in two days, and the third is the one that should worry operators most — because unlike the other two, it is not a bolted-on debug endpoint. It is the serialisation format the cache uses to do its job.
JFrog published CVE-2026-105192 on 7 October 2026 at 09:40 UTC, scored CVSS 3.1 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), carrying both CWE-502 (deserialization of untrusted data) and CWE-306 (missing authentication for critical function). It affects LMCache from 0.3.9 onwards, with no upper bound — the CVE record’s affected range is 0.3.9 with lessThanOrEqual: *. There is no fixed version because there is no fix.
We covered CVE-2026-107204 and 107206 yesterday: an unauthenticated /run_script endpoint with a sandbox that does not hold, and an unauthenticated multiprocess management interface that leaks environment variables. 105192 is a different mechanism in the same subsystem, and it is architecturally harder to remove.
The bug is in the wire format, not an endpoint
The CVE description is unusually precise, and the referenced source files confirm it. We read both at the tags the record cites.
LMCache’s multiprocess mode — also called distributed mode — opens a ZeroMQ ROUTER socket so worker processes can register and share KV cache blocks. Messages on that socket are msgpack. msgpack supports extension types, and LMCache registers extension code 1 for its device-IPC wrapper. In lmcache/v1/platform/base/ipc_wrapper.py at tag v0.5.5, the handling is explicit: line 17 imports pickle, line 156 is return pickle.dumps(obj) inside Serialize, and line 169 is return pickle.loads(data) inside Deserialize. The module’s own docstring states the design intent — “the single msgspec ext code (1) — pickle preserves the concrete” type across the process boundary.
At tag v0.3.9, where the affected range begins, lmcache/v1/multiprocess/custom_types.py shows the wiring: a decoder config registers CudaIPCWrapper.Deserialize against code=1, and get_customized_decoder installs an ext_hook that dispatches on that code and raises TypeError for anything else. The hook is invoked by the msgpack decoder itself.
That last point is the whole vulnerability. Per the CVE record, extension code 1 reaches pickle.loads while the server is still decoding request arguments and before the handler runs. There is no handler-level check to add, because no handler has executed yet. A single unauthenticated ZMQ DEALER message to the transport port — default 5555 — executes code as the user running the LMCache process. JFrog notes that official container images run that process as root.
The exposure condition is the one multi-node deployments must satisfy
There is a real mitigating factor, and it deserves to be stated honestly rather than minimised. The transport binds to localhost unless an operator sets a routable address with --host. A single-node deployment on default settings is not remotely exploitable through this path.
But the CVE record also states why that address gets changed: it “is how multi-node deployments let peers connect.” The exposure condition is not operator error or a careless debug flag. It is the documented, necessary configuration for the feature the subsystem exists to provide. Anyone running LMCache across more than one node has, by construction, satisfied the precondition.
That is a meaningfully different risk profile from “9.8, but only if misconfigured.” Here the vulnerable configuration is the configuration. The practical control is not a setting but a network boundary: the ZeroMQ transport must sit on a segment where only cache peers can reach it, with authentication provided by the network because the protocol provides none.
Three criticals, no stable release, pre-release tags that are not remediation
The remediation picture across all three CVEs is the part operators keep getting wrong, and we checked it directly against PyPI and the GitHub releases API rather than inferring it.
PyPI’s latest stable lmcache release is 0.5.5, uploaded 12 September 2026. Everything published since is a release candidate: 0.5.6rc1 (26 September), 0.5.6rc2 (28 September), 0.5.6rc3 (6 October). The GitHub releases list shows the same — v0.5.6rc3 and its platform variants, all cut on 6–7 October and marked pre-release, plus nightly tags. Every one of those predates or coincides with the CVE publications on 7 October.
So an operator cannot currently patch LMCache against any of these three findings by upgrading to a stable release. Installing a release candidate is not remediation either, since nothing in the public record ties 0.5.6rc3 to a fix for 105192, and the CVE record’s affected range has no upper bound at all. We searched the LMCache repository for issues referencing 105192 and found none — there is no public upstream tracking issue for this CVE as of writing.
One further note on the record itself: NVD lists CVE-2026-105192 with status Deferred. That is NVD’s backlog state, not a judgement on severity — the CVSS 9.8 and the technical detail come from JFrog as the assigning CNA, and the referenced source files substantiate the mechanism independently. Do not read “Deferred” as “disputed.”
Why pickle keeps winning in inference infrastructure
It is worth naming the pattern, because this will not be the last instance. Inference-serving components move large, structurally complex Python objects between processes under hard latency budgets. Pickle is the path of least resistance: it round-trips arbitrary objects, preserves concrete types, and requires no schema. Every property that makes it attractive for a trusted IPC channel makes it fatal the moment that channel becomes reachable.
The design assumption embedded in ipc_wrapper.py is that the ZeroMQ socket carries only messages from sibling workers. That assumption is enforced by nothing — not a shared secret, not a signature, not a peer allowlist — and the feature that makes the subsystem useful is precisely the one that removes the network boundary doing the enforcing in the single-node case.
The fix is not a patch to a handler. It is either a schema-constrained serialiser for the ext-1 path, or authentication on the transport before any decoding occurs. Both are real engineering work, which is consistent with no fix having shipped.
What to do
- Find port 5555 before you find the version. The decisive question is whether any LMCache transport socket is bound to a routable address. Inventory
--hostsettings across every deployment; a multi-node cluster will have them set by necessity. - Treat the ZeroMQ segment as a trust boundary with no authentication. Firewall it to cache peers only. It must never traverse a path reachable from workload networks, and it must never be exposed through the same ingress as the OpenAI-compatible serving port.
- Do not run the container as root if you can avoid it. JFrog explicitly notes official images do. Dropping to an unprivileged user does not stop code execution, but it changes what the execution reaches.
- Do not treat
0.5.6rc3as a patch. There is no stable release addressing these findings and no upper bound on the affected range. Pin remediation to a confirmed upstream fix commit, not to a version number that merely sounds newer. - Assume credential disclosure on any exposed host. Between 105192 (code execution as the process user) and CVE-2026-107206 (unauthenticated environment reads), anything in an LMCache process environment on a reachable host should be rotated.
- Hunt for execution, not connection attempts. ZeroMQ traffic to 5555 is normal in a cluster. Unexpected child processes of the LMCache process are not. GPU serving hosts have highly regular process trees, which makes anomalies unusually legible.
Verification note: we read the CVE Program record for CVE-2026-105192 directly via the CVE Services API — assigner JFrog, published 2026-10-07T09:40:11Z, CVSS 3.1 9.8 with vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H, CWE-502 and CWE-306, affected LMCache 0.3.9 with lessThanOrEqual “*”, and its four references. We retrieved the two referenced source files from GitHub at the tags the record cites and confirmed the pickle calls in lmcache/v1/platform/base/ipc_wrapper.py at v0.5.5 (import at line 17, pickle.dumps at 156, pickle.loads at 169) and the ext-code-1 decoder registration in lmcache/v1/multiprocess/custom_types.py at v0.3.9. We queried the PyPI JSON API (latest stable 0.5.5, uploaded 2026-09-12; 0.5.6rc1/rc2/rc3 thereafter) and the GitHub releases API for LMCache (newest tags v0.5.6rc3 and variants, 6–7 October, pre-release), and searched repository issues for 105192 with zero results. NVD reports the record as status “Deferred” with last modification 2026-10-07T18:17:15. OSV currently returns only the earlier GHSA-3hh9-752g-5g22 / CVE-2026-10813 for the PyPI package and has no record for 105192. The root-in-official-images claim is JFrog’s, stated in the CVE description. We did not test any LMCache instance and did not attempt exploitation.
Sources:
- CVE Program — CVE-2026-105192, LMCache unauthenticated RCE in multiprocess mode via pickle deserialization (JFrog, CVSS 3.1 9.8, published 7 October 2026)
- NVD — CVE-2026-105192 record and status
- LMCache v0.5.5 —
ipc_wrapper.py, the pickle Serialize/Deserialize pair behind msgpack ext code 1 - LMCache v0.3.9 —
custom_types.py, ext-code-1 decoder registration andext_hookdispatch - PyPI — lmcache release history (latest stable 0.5.5, 12 September 2026)