HOL LogoGuard

Explore HOL

  • HOL home
  • AI agent registry
  • AI plugins
  • Open standards
  • HOL members
  • Sponsor HOL
  • Advertise with HOL

Guard product

  • Guard overviewLocal security and control for AI agents and the tools they use.
  • FeaturesRuntime protection, policy routing, review, and evidence.

Explore Guard

  • Product previewWalk through Guard surfaces in read-only demo mode.
  • ComparisonCompare Guard with native controls and AI security vendors.

AI tools

  • All AI toolsEvery supported AI tool and how Guard applies policy to it.
  • Codex
  • Claude Code
  • Cursor
  • Antigravity CLI
  • OpenCode
  • Hermes
  • OpenClaw
  • GitHub Copilot CLI
  • Antigravity
  • Kimi
  • Grok
  • Pi / Oh My Pi
  • Zcode

Extensions

  • All extensionsBrowse command and MCP coverage with owners and stated limits.
  • Command coverageShell command protection across clouds, databases, backups, and packages.
  • MCP server coverageSee how Guard maps risk state across MCP tools and servers.
  • Core safetyThe safety floor listings that ship with Guard.
  • Data and resilienceBackup and storage command protection.
  • Cloud and infrastructureAWS, Azure, GCP, Kubernetes, and more.

Security

  • AI security hubSecurity research, advisories, and agent safety coverage.
  • AI tool securitySecurity profiles for each supported coding agent.
  • Safe labsHands-on attack simulations with safe boundaries.
  • Redacted warningsReal blocked actions with sensitive details removed.
  • AdvisoriesCoordinated disclosure reports for AI tooling.
  • Active CVEsSearch active CVEs affecting AI tooling.

Learn

  • Security guidesPractical guides for securing AI agent workflows.
  • DocsInstall, configure, and operate Guard with confidence.
  • ResearchPublished security research, benchmarks, and methodology.

Community

  • ReleasesVersion history, shipped changes and upgrade notes.
  • ContributorsThe people and contributions behind HOL Guard.
  • AffiliatesShare Guard with your audience and earn from referrals.
  • SponsorFund agreed research, implementation and shared testing through the HOL Partner Program.
  • AdvertiseCompare README logo placements, previews and monthly prices, or promote a plugin or extension.
PricingEnterpriseOpen AppInstall Guard
  1. Guard
  2. Security
  3. CVEs
  4. CVE 2026 54234 vllm has remote dos via invalid recovered token
HOL Guard

Public security guidance for teams protecting AI harnesses, MCP servers, skills, prompts, and local tool execution.

Install Guard

AI Security

  • Prompt injection
  • MCP security
  • OWASP MCP mapping
  • Supply chain

Resources

  • Trust packet
  • Harness setup
  • Redacted warnings
  • Safe labs

Product

  • Install Guard
  • Pricing
  • Open dashboard
Guard
  • Guard Overview
  • Releases
  • Contributors
  • Install Guard
  • Pricing
Docs
  • Documentation Index
  • Developer Hub
  • API Reference
  • Root OpenAPI
  • Registry OpenAPI
  • Run in Postman
  • Standards
  • Submit ERC-8004 Contract
  • Feature Your Agent
Best Plugins
  • Browse Plugins
  • Plugin Launches
  • Best Claude Plugins
  • Best Codex Plugins
  • Best Grok Plugins
  • Best Kimi Plugins
  • Best DeepSeek Plugins
  • Best Antigravity Plugins
  • Best MCP Servers
  • Best Cursor Plugins
  • Best OpenCode Plugins
Best Agents
  • Best ERC-8004 Agents
  • Best Virtuals Agents
  • Best MCP Servers
  • Best A2A Agents
  • Best x402 Payable
  • All Categories
Community
  • Telegram
  • X
More
  • About HOL
  • Sponsor
  • Advertise
  • Affiliate program
  • Contact
  • Blog
  • GitHub
  • Privacy
  • Terms of Service
Settings

Copyright ยฉ 2026 HOL DAO LLC. All rights reserved.

Back to active CVEs
High ยท CVSS 7.5CVE-2026-54234GHSA-8WR5-JM2H-8R4F

vLLM has Remote DoS via Invalid Recovered Token ReinjectionCVE-2026-54234

Answer in brief

CVE-2026-54234 records a High severity (CVSS 7.5) vulnerability in vLLM has Remote DoS via Invalid Recovered Token Reinjection. The current sources do not mark it as known exploited. The current feed maps vllm (pip), vllm (pypi). Check affected ranges and fixed versions before updating.

Analysis pending evidence review

HOL Guard separates source facts from reviewed analysis. See the methodology.

Published Jul 6, 2026Updated Oct 25, 2026Source checked Nov 1, 2026First seen by HOL Jul 6, 2026Material review Oct 25, 2026
Upstream Advisory

Key facts

Risk
High ยท CVSS 7.5
Exploitation
Not marked as known exploited
Affected software
3 mapped packages or products
Fix availability
Available

Why this deserves its current priority

CVSS is 7.5. The current sources do not mark it as known exploited. Treat this as a source-backed prioritization signal, not a statement about your environment.

Analysis status

Analysis pending evidence review

Factual feed record only; HOL analysis is not approved for indexing. Read the methodology.

Affected scope and exposure questions

The current feed maps vllm (pip), vllm (pypi). Check affected ranges and fixed versions before updating.

Affected products and reported versions
ProductAffected versionsFixed versions
cpe:2.3:a:vllm:vllm:*:*:*:*:*:*:*:*Not reportedNot reported
Mapped affected packages and fixed versions
PackageAffected rangeFixed version
vllmpip>=0.17.1,<0.24.00.24.0
vllmpypi>=0.17.1 <0.24.00.24.0

Recommended response

  1. 1Check inventory. Check lockfiles and deployed manifests for vllm, vllm.
  2. 2Review the reported fix. Update vllm to 0.24.0; vllm to 0.24.0 if you use the affected versions. Test the change in a non-production environment first.

Evidence timeline and material changes

  1. Published upstream

    Jul 6, 2026

    Evidence: source:nvd:source_dates:source-dates:record
  2. Source modified

    Oct 25, 2026

    Evidence: source:nvd:source_dates:source-dates:record
  3. First seen by HOL

    Jul 6, 2026

Sources and claim methodology

  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • GitHub security advisorygithub.com
  • NVD vulnerability recordnvd.nist.gov
  • GitHub security advisorygithub.com
Upstream source description

## 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 recovered-token trace shows `recovered_token_ids[0] = 151936`, which is exactly `vocab_size` for this checkpoint. 3. The speculative proposer then prepares the next-token row from the sampled speculative row. ```python # vllm/v1/spec_decode/eagle.py def prepare_next_token_ids_padded(...): ... eagle_prepare_next_token_padded_kernel[grid]( sampled_token_ids, discard_request_mask, backup_tokens_gpu, next_token_ids, valid_sampled_tokens_count, gpu_input_batch.vocab_size, ... ) return next_token_ids, valid_sampled_tokens_count ``` In the verified trace, this step receives a sampled row beginning with `151936`, followed by `-1` padding. The important point is that `151936` occupies the first live token position for the request. This step then produces `next_token_ids[0] = -1`, meaning the live next token for the request has been converted to `-1`. 4. The drafter then rotates the draft input ids and inserts those `next_token_ids` back into the live input-id buffer. ```python # vllm/v1/spec_decode/eagle.py def set_inputs_first_pass(...): ... self.input_ids[token_indices_to_sample] = next_token_ids ``` In the verified trace, this produces `input_ids_after[0] = -1`. 5. The model-side embed path later consumes those input ids. ```python # vllm/model_executor/models/qwen2.py def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor: return self.embed_tokens(input_ids) ``` In the verified trace, this is the first point where the converted `-1` becomes visible as a real model input. The bug is not merely that the sampled speculative row contained padding `-1`; the bug is that the live next token for the request became `-1` and was written back into input ids. 6. After that point, the visible sink depends on timing and backend state. On the attached Qwen3 reproducer, the engine commonly dies later in the drafting / attention path with `CUDA error: device-side assert triggered`, for example under `flash_attn_varlen_func(...)`. ### Local script breakdown `repro_g4_recovered_minus1_local.py` is a standalone local reproducer. - It reads the Qwen3 checkpoint path from `VLLM_POC_G4_MODEL` or the built-in `/path/to/qwen3` placeholder - It creates `EngineCore` directly without any external helper dependency - It submits one fixed multi-request workload that preserves the same overlap and speculative-decoding state needed for the bug - It writes: - `request_payloads.json` - `repro_config.json` - `timeline.json` - `responses.json` - `error.txt` - `recovered_chain_trace.jsonl` - `recovered_chain_trace.jsonl` is the key attribution artifact. It records the recovered-token chain directly from the standalone reproducer ### gRPC script breakdown `repro_g4_recovered_minus1_grpc.py` is a standalone public gRPC reproducer. - It reads the Qwen3 checkpoint path from `VLLM_POC_G4_MODEL` or the built-in `/path/to/qwen3` placeholder - It starts a temporary `vllm.entrypoints.grpc_server` process - It sends only public `Generate` and `Abort` RPCs - It submits one fixed overlapping request sequence that preserves the same speculative-decoding state needed for the bug - After the crash window, it sends one more public `Generate` probe request to confirm that later gRPC requests also fail after the worker dies - It writes: - `request_payloads.json` - `timeline.json` - `server_command.json` - `responses.json` - `post_crash_probe.json` - `server.stdout.log` - `server.stderr.log` ## Observed result Local repro typically ends with: - a recovered-token trace showing: - `sample_recovered_tokens_return -> recovered_token_ids[0] = 151936` - `prepare_next_token_ids_padded -> next_token_ids[0] = -1` - `set_inputs_first_pass -> input_ids_after[0] = -1` - `embed_input_ids_out_of_range -> input_ids[0] = -1` - `CUDA error: device-side assert triggered` - a fatal engine-side failure gRPC repro typically ends with: - the triggering gRPC requests failing with `INTERNAL: EngineCore encountered an issue. See stack trace (above) for the root cause.` - server logs showing the worker dies with `CUDA error: device-side assert triggered` - a later public probe request also failing after the worker is dead This demonstrates that the issue is reachable through the public gRPC request surface, not only through a local reproducer. ## Log snippets ### Local recovered-chain trace ```text sample_recovered_tokens_return: recovered_token_ids = [151936, ...] vocab_size = 151936 prepare_next_token_ids_padded: sampled_token_ids_head = [[151936, -1, -1, ...], ...] next_token_ids = [-1, ...] set_inputs_first_pass: input_ids_after = [-1, 0, 0, 0, ...] embed_input_ids_out_of_range: input_ids = [-1, 0, 0, 0, ...] ``` ### gRPC server log ```text torch.AcceleratorError: CUDA error: device-side assert triggered ... vllm.v1.engine.exceptions.EngineDeadError: EngineCore encountered an issue. See stack trace (above) for the root cause. ... Error in Generate for request post_crash_probe vllm.v1.engine.exceptions.EngineDeadError: EngineCore encountered an issue. See stack trace (above) for the root cause. ``` ## Root cause This is a speculative-decoding state-handling bug, not an invalid frontend token-id input bug. The root cause is that a recovered speculative token can become equal to `vocab_size`, then be selected as the live next token for a request, then be converted to `-1`, and that converted `-1` is still written back into live drafter input ids and later consumed by the drafting / embedding / attention path. For the Qwen3 checkpoint used here: - `151936 == vocab_size` This value should be described as the model `vocab_size` boundary value, not as a legal token id. ## Attachments The attached bundle for this report should contain: - `repro_g4_recovered_minus1_local.py` - `repro_g4_recovered_minus1_grpc.py` These two standalone scripts are sufficient to reproduce the issue and its public gRPC reachability. ## Fix A fix for this vulnerability has been merged in: https://github.com/vllm-project/vllm/pull/44744

Quoted source text, attributed separately from HOL analysis.

Record context

Vulnerability class
Secret Exfiltration
EPSS
Not reported
CWE IDs
CWE-20, CWE-1284
Source
GitHub Security Advisories
Source checked
Nov 1, 2026
References
6 linked sources
Open source record