All vulnerabilities
GHSA-6cxc-2vcg-w5qc

vllm: V1 InputBatch.condense leaves a stale allowed_token_ids mask on recycled batch rows

vllm
CVSS
3.7Low
ExploitationNo known exploitationNot in CISA KEV, which does not prove it is unexploitedCISA KEV
Exploit odds (EPSS)No scoreNo FIRST EPSS score in this snapshot yet
Fixed inNone listedNo source names a fixed version yet
Published6 Oct 2026Updated 6 Oct 2026GitHub

What fixes it

No source in this snapshot names a fixed version.

Description

Severity

  • Severity: Low
  • CVSS v3.1: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N (3.7, Low)
  • CWE: CWE-440 (Expected Behavior Violation), CWE-668 (Exposure of Resource to Wrong Sphere)

Summary

vLLM V1 InputBatch.condense() can leave a stale allowed_token_ids_mask_cpu_tensor row after moving a constrained request. A later unrestricted request that reuses that batch slot inherits the previous request's token allowlist, so sampling for that other client is constrained by a mask it did not set (cross-request output-integrity issue). The victim request still returns HTTP 200 with no error indication.

Public report: https://github.com/vllm-project/vllm/issues/43894

Affected

  • Ecosystem / package: pip / vllm
  • Affected versions: vLLM ≤ 0.30.0 (V1 worker InputBatch path). Confirmed via public issue #43894.
  • Fixed versions: Fix merged in https://github.com/vllm-project/vllm/pull/43931 (not yet in a tagged release as of v0.30.0; expected in the next release).

Details

InputBatch.condense() copies allowed_token_ids_mask_cpu_tensor from a moved request's row into the freed index but never clears the source row. add_request() only writes that row when the new request sets sampling_params.allowed_token_ids. When another active request still has allowed_token_ids, _make_sampling_metadata() copies the active prefix of the CPU mask to GPU and Sampler.apply_logits_processors() applies the stale whitelist to the unrestricted request.

An attacker who can submit generate traffic (including unauthenticated /invocations, which is outside --api-key guards, or any valid API key on /v1) plants a chosen allowed_token_ids mask. If batching/condense recycles that row onto another client's unrestricted request while the constrained request is still active, the victim's tokens are limited to the attacker's allowlist (for example only a single token, only digit tokens, or only tokens that spell a short phrase). This is vocabulary steering / wrong allowlist application, not free-form substitution of arbitrary attacker text.

A related but distinct bug in InputBatch.swap_states() (row-view aliasing of the same mask tensor) was fixed in https://github.com/vllm-project/vllm/pull/48419.

Impact

Limited cross-request sampling integrity on a shared engine: one client's allowed_token_ids can constrain another client's outputs under concurrent batching. Confidentiality and availability are unaffected. Attack complexity is High because exploitation needs concurrent requests plus a condense slot-reuse race while the attacker's constrained request remains in the batch.

Patches

  • https://github.com/vllm-project/vllm/pull/43931 — clear vacated allowed_token_ids mask rows in condense()
  • https://github.com/vllm-project/vllm/pull/48419 — related swap_states mask aliasing fix

References

  • https://github.com/vllm-project/vllm/issues/43894
  • https://github.com/vllm-project/vllm/pull/43931
  • https://github.com/vllm-project/vllm/pull/48419
  • https://docs.vllm.ai/en/latest/usage/security.html (API key does not cover /invocations)

Credit

Reported publicly by @glaziermag in issue #43894.

Text from GitHub Advisory Database

Affected versions 1

  • vllm, pip< 0.31.0GitHub