Skip to content
vLLMGHSA-8wr5-jm2h-8r4f

vLLM has Remote DoS via Invalid Recovered Token Reinjection

High7.5CVE-2026-54234 · Published Jul 17, 2026 · updated Sep 10, 2026

## Summary A frontend-legal multi-request speculative workload can make vLLM produce an out-of-vocabulary recovered token equal to `vocab_size`, convert that value to `-1` when choosing the next live token for a request, and then feed that `-1` back into the next drafter input ids. On Qwen3 GPTQ this reaches the worker-side drafting / attention path and crashes the engine with a GPU `device-side assert`. The same issue is reachable through the public gRPC request surface by sending a specific overlapping `Generate` / `Abort` sequence. ## Impact - A remote client that can send public gRPC generation requests can crash the shared vLLM engine worker - The triggering request sequence aborts concurrent requests and prevents later requests from completing until the worker is restarted - In shared deployments, this is a service-wide denial of service for other clients, not just a failure isolated to the attacking requests - The failure is reproducible, so repeated request sequences can sustain the outage ## Affected version - Confirmed on vLLM `0.17.1` - Earlier and later versions have not been checked yet in this report ## Repro model - Official Hugging Face repo: - [`Q...

GitHub advisory

Affected versions

PackageAffectedFixed in
vllm
PyPI
>= 0.17.1, < 0.24.00.24.0
Details and references

## Summary A frontend-legal multi-request speculative workload can make vLLM produce an out-of-vocabulary recovered token equal to `vocab_size`, convert that value to `-1` when choosing the next live token for a request, and then feed that `-1` back into the next drafter input ids. On Qwen3 GPTQ this reaches the worker-side drafting / attention path and crashes the engine with a GPU `device-side assert`. The same issue is reachable through the public gRPC request surface by sending a specific overlapping `Generate` / `Abort` sequence. ## Impact - A remote client that can send public gRPC generation requests can crash the shared vLLM engine worker - The triggering request sequence aborts concurrent requests and prevents later requests from completing until the worker is restarted - In shared deployments, this is a service-wide denial of service for other clients, not just a failure isolated to the attacking requests - The failure is reproducible, so repeated request sequences can sustain the outage ## Affected version - Confirmed on vLLM `0.17.1` - Earlier and later versions have not been checked yet in this report ## Repro model - Official Hugging Face repo: - [`Qwen/Qwen3-0.6B-GPTQ-Int8`](https://huggingface.co/Qwen/Qwen3-0.6B-GPTQ-Int8) - Anyone wants to reproduce the bug with my PoC scripts should download `Qwen3-0.6B-GPTQ-Int8` first ## Trigger chain 1. A legal multi-request speculative workload keeps structured-output state, speculative decoding, overlap, and request cancellation active in the same live engine. 2. During rejection sampling, vLLM produces a recovered token equal to the model `vocab_size` boundary value. 3. That recovered token appears in position 0 of the sampled speculative row for a live request. The same row also contains trailing padding entries equal to `-1`, but those padding entries are not the key fault by themselves. 4. The next-token preparation step treats the position-0 recovered token as the real next token for that request and converts that out-of-vocabulary value to `-1`. 5. The drafter writes that converted `-1` back into the live next-step input-id row for the request. 6. The drafting / embedding / attention path later consumes that live invalid token and the worker crashes on GPU. ## Details ### Simple example The important distinction is: - trailing `-1` values in a speculative row can be ordinary padding - the bug appears when the first live token for a request becomes `151936 == vocab_size`, and that live token is then converted into `-1` In simplified form, the bad transition looks like this: ```text sampled speculative row: [151936, -1, -1, -1, ...] ``` At this point, the trailing `-1` values are only padding. The critical problem is that the first position holds `151936`, which is out of vocabulary and is being treated as the request's real next token. Then vLLM prepares the next-token buffer: ```text next_token_ids: [-1, ...] ``` Finally, that converted `-1` is written back into the live model input ids: ```text input_ids_after: [-1, 0, 0, 0, ...] ``` The crash happens because the live next token became `-1` and was later consumed by the drafting / embedding / attention path, not merely because the speculative row contained padded `-1` entries. ### Trigger path in code 1. The workload is frontend-legal. The requests use normal `SamplingParams` features such as structured outputs, `stop`, `bad_words`, `min_tokens`, and streaming overlap. No malformed token-id list is required at the request boundary. 2. In speculative decoding, the rejection sampler can generate recovered tokens when drafted tokens are rejected. ```python # vllm/v1/sample/rejection_sampler.py def sample_recovered_tokens(...): recovered_token_ids = torch.empty_like(draft_token_ids) sample_recovered_tokens_kernel[(batch_size, max_spec_len)](...) return recovered_token_ids ``` On the verified Qwen3 run, the

CVSS 3.1
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Severity from
GitHub (reviewed advisory)
Weakness
CWE-20
Also known as
CVE-2026-54234, PYSEC-2026-3542

More vLLM advisories

All vLLM

Critical advisories by email

Wednesdays: the week’s critical and high advisories in the AI and data stack, with the fixed versions. Only in weeks that have some.

Double opt-in. Unsubscribe any time.