vLLM: Public multimodal SHM handle forgery reaches worker-side `pickle.loads()` with insecure serialization disabled
Affects vllmSource GCVE Database
Not known to be exploited · CVSS 7.5 High : upgrade to 0.32.0 soon.
Data as of 28 Oct 2026, 22:12 UTC
- Patch
- Available – fixed in 0.32.0
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:HExploitation evidence
Disclosed: evidence found.
CVE published 26 Oct 2026
Detectable: no evidence on record.
No public nuclei template indexed
Weaponised: no evidence on record.
No public exploit code indexed in Metasploit or Exploit-DB
Exploited in the wildExploited: no evidence on record.
Not in CISA KEV and no source reports exploitation
Observed by OffSeq MirageSeen by Mirage: no evidence on record.
No attack traffic recorded by OffSeq Mirage sensors
A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.
What to do
Upgrade vllm to 0.32.0.
Affected software
| Product | Affected | Fixed in |
|---|---|---|
| PyPIvllm | Affected
| Fixed in
|
Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.
Description
## Affected - **Ecosystem / package:** pip / `vllm` - **Affected versions:** vLLM < 0.32.0. ## Summary When `--mm-processor-cache-type shm` is enabled, vLLM's multimodal SHM cache represents a cached item with an `address` and `monotonic_id` pair and treats that pair as authority to dereference shared memory in the worker. A public scale-out request can supply its own multimodal `features.kwargs_data`, so an authenticated caller can forge an inner SHM carrier that points at attacker-controlled bytes already staged in the ring buffer. The worker then reaches `pickle.loads()` while deserializing the forged object. This is reachable through ordinary public HTTP routes and does not require `VLLM_ALLOW_INSECURE_SERIALIZATION=1`. The live chain used `/v1/chat/completions/render` to obtain a valid feature envelope, `/v1/chat/completions` to stage attacker-chosen bytes into SHM, and `/inference/v1/generate` to submit the forged inner carrier. ## Affected code Links pinned to the confirmed commit [`752a3a504485`](https://github.com/vllm-project/vllm/tree/752a3a504485790a2e8491cacbb35c137339ad34) (v0.25.1): - [`vllm/distributed/device_communicators/shm_object_storage.py#L375-L400`](https://github.com/vllm-project/vllm/blob/752a3a504485790a2e8491cacbb35c137339ad34/vllm/distributed/device_communicators/shm_object_storage.py#L375-L400) — SHM object deserialization reaches `pickle.loads(serialized_data)` for byte objects. - [`vllm/multimodal/cache.py#L567-L581`](https://github.com/vllm-project/vllm/blob/752a3a504485790a2e8491cacbb35c137339ad34/vllm/multimodal/cache.py#L567-L581) — sender emits an SHM carrier containing only `address` and `monotonic_id`. - [`vllm/multimodal/cache.py#L711-L737`](https://github.com/vllm-project/vllm/blob/752a3a504485790a2e8491cacbb35c137339ad34/vllm/multimodal/cache.py#L711-L737) — worker dereferences the untrusted pair and reads the SHM object. - [`vllm/entrypoints/scale_out/token_in_token_out/serving.py#L146-L173`](https://github.com/vllm-project/vllm/blob/752a3a504485790a2e8491cacbb35c137339ad34/vllm/entrypoints/scale_out/token_in_token_out/serving.py#L146-L173) — public `features.kwargs_data` is decoded into engine input without authenticating SHM handles before handoff. The public scale-out route decodes attacker-controlled `features.kwargs_data` into `MultiModalKwargsItem`s and hands them straight to the engine — no authentication of the SHM handle: ```python # vllm/entrypoints/scale_out/token_in_token_out/serving.py Lines 155-165 # Deserialize tensor data when present; None → cache hit. mm_kwargs: dict[str, list[MultiModalKwargsItem | None]] = {} if features.kwargs_data is not None: for modality, items in features.kwargs_data.items(): mm_kwargs[modality] = [ decode_mm_kwargs_item(item) if item is not None else None for item in items ] else: for modality, hashes in features.mm_hashes.items(): mm_kwargs[modality] = [None] * len(hashes) ``` In the worker, a decoded item that carries an `address` is treated as authority to dereference shared memory: the `(address, monotonic_id)` pair alone drives the read, with no binding to the cache key or to who issued the handle: ```python # vllm/multimodal/cache.py Lines 711-723 @override def get_and_update_item( self, mm_item: MultiModalKwargsItem | None, mm_hash: str, ) -> MultiModalKwargsItem: assert mm_item is not None, f"Expected an address item for {mm_hash=}" if "address" in mm_item: address = cast(int, mm_item["address"].data) monotonic_id = cast(int, mm_item["monotonic_id"].data) return self._shm_cache.get(address, monotonic_id) return mm_item ``` The dereference lands in the SHM deserializer, which `pickle.loads()` any chunk tagged as `bytes` — so attacker bytes staged in the ring buffer are unpickled in the worker process: ```python # vllm/distributed/device_communicators/shm_object_storage.py Lines 375-400 def deserialize(self, data_view: memoryview) -> Any: # pickle.loads do not read past the end of a pickled object # within a large buffer, so we can skip storing the metadata size type_name, nbytes, len_arr = pickle.loads(data_view) serialized_data = data_view[-nbytes:] if type_name == torch.Tensor.__name__: obj = [] start_idx = 0 for length in len_arr: item_bytes = serialized_data[start_idx : start_idx + length] obj.append(item_bytes) start_idx += length obj = self.tensor_decoder.decode(obj) elif type_name == self._mm_kwargs_item_cls.__name__: obj = [] start_idx = 0 for length in len_arr: item_bytes = serialized_data[start_idx : start_idx + lengt
References
- github.com/vllm-project/vllm/security/advisories/GHSA-6757-hggm-hp2w (opens in a new tab)
- nvd.nist.gov/vuln/detail/CVE-2026-115912 (opens in a new tab)
- github.com/vllm-project/vllm/pull/59357 (opens in a new tab)
- github.com/vllm-project/vllm/commit/3d5625cc3c2474994107047d7a6ecb8e4008f9b0 (opens in a new tab)
- github.com/vllm-project/vllm (opens in a new tab)
- github.com/vllm-project/vllm/releases/tag/v0.32.0 (opens in a new tab)