vLLM: unsafe deserialization
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...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| vllm PyPI | < 0.32.0 | 0.32.0 |
Details and references
## 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
- CVSS 3.1
- CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-502
- Also known as
- CVE-2026-115912
- github.com/vllm-project/vllm/security/advisories/GHSA-6757-hggm-hp2w
- nvd.nist.gov/vuln/detail/CVE-2026-115912
- github.com/vllm-project/vllm/pull/59357
- github.com/vllm-project/vllm/commit/3d5625cc3c2474994107047d7a6ecb8e4008f9b0
- github.com/vllm-project/vllm
- github.com/vllm-project/vllm/releases/tag/v0.32.0