---
id: "GHSA-vxqj-p4gw-9h4c"
page: "https://infrastack.ai/live/GHSA-vxqj-p4gw-9h4c"
kind: "vulnerability"
aliases: ["GHSA-vxqj-p4gw-9h4c"]
title: "Incomplete CVE-2026-34755 remediation can be bypassed by request-level num_frames"
severity: "medium"
cvss: 6.5
epss: null
exploited: false
status: "open"
category: "ai-tooling"
products: [{"name": "vllm", "fixed": ["0.31.0"], "affected": ">= 0.19.0, < 0.31.0", "ecosystem": "pypi"}]
ecosystems: ["pypi"]
tags: ["ai-tooling", "cwe-770", "llm-serving", "python", "self-hosted", "vllm"]
not_affected_if: ["auth-required", "text-only-models", "trust-request-mm-kwargs-disabled"]
published_at: "2026-10-27T22:22:11+00:00"
first_seen_at: "2026-11-09T22:53:23+00:00"
updated_at: "2026-11-09T22:54:42+00:00"
sources: [{"source": "ghsa-repo", "url": "https://github.com/vllm-project/vllm/security/advisories/GHSA-vxqj-p4gw-9h4c", "first_seen_at": "2026-11-09T22:53:23+00:00"}]
exploit_sources: []
---

# Incomplete CVE-2026-34755 remediation can be bypassed by request-level num_frames

Link for people: https://infrastack.ai/live/GHSA-vxqj-p4gw-9h4c (this .md is for agents)

A client of a vLLM server can send a request-level num_frames of -1 that overrides the server's video frame cap, so every JPEG frame in a video payload is decoded. Memory use then grows with the number of frames supplied, which can exhaust memory on the server.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- pypi:vllm affected >= 0.19.0, < 0.31.0 fixed 0.31.0

## How to check

- Check the installed vLLM version with `pip show vllm` and compare it against the affected range `>= 0.19.0, < 0.31.0`.
- Check whether the server serves video-capable multimodal models to clients via `/v1/chat/completions`.
- Check whether `--api-key` is set; without it, the route has no API-key middleware.
- Check whether `--trust-request-mm-kwargs` is enabled, since the advisory says it restores trusted override behavior.

## Fix

- vllm: upgrade to `0.31.0`
- Keep `--trust-request-mm-kwargs` disabled for untrusted clients (per the advisory, PR #58830 blocks untrusted per-request media kwargs by default)

## Timeline

- 2026-11-09T22:53:23+00:00 ghsa-repo https://github.com/vllm-project/vllm/security/advisories/GHSA-vxqj-p4gw-9h4c

## Documents

- [advisory] Maintainer's advisory: github.com https://github.com/vllm-project/vllm/security/advisories/GHSA-vxqj-p4gw-9h4c

## From the sources

### Summary

The frame-count protection added by PR #38636 for GHSA-pq5c-rjhq-qp7p / CVE-2026-34755 is incomplete in vLLM v0.19.0 through v0.27.1 and current `main` at commit `6c95a641e95c0faa6f3aa802d1fd3cce3f3bc3ce`.

An inference client can set `media_io_kwargs.video.num_frames=-1` in a stock `ChatCompletionRequest`. The production merge lets that request value replace the engine-level frame setting. `VideoMediaIO.load_base64("video/jpeg", ...)` treats a negative value as unlimited and decodes every comma-separated JPEG frame before combining them with `np.stack`.

In a safe exact-path reproduction, the same 256-frame payload loaded 8 frames when the server value was 8, but all 256 frames when the request supplied `-1`. This restores request-controlled, linearly scalable memory allocation at the same decoder addressed by the parent advisory. I assess the issue as Moderate (CVSS 3.1: 6.5). The safe run demonstrates the limit bypass and allocation growth; it does not claim a reproduced service crash or RCE.

### Details

The request field is exposed without a numeric security bound in [`ChatCompletionRequest`](https://github.com/vllm-project/vllm/blob/6c95a641e95c0faa6f3aa802d1fd3cce3f3bc3ce/vllm/entrypoints/openai/chat_completion/protocol.py#L364-L370). `ChatParams.with_defaults()` performs the engine/request merge ([`renderers/params.py`](https://github.com/vllm-project/vllm/blob/6c95a641e95c0faa6f3aa802d1fd3cce3f3bc3ce/vllm/renderers/params.py#L99-L122)), and the base merge gives runtime values precedence ([`media/base.py`](https://github.com/vllm-project/vllm/blob/6c95a641e95c0faa6f3aa802d1fd3cce3f3bc3ce/vllm/multimodal/media/base.py#L65-L80)).

`VideoMediaIO.merge_kwargs()` removes unsafe request-level GPU backend settings but does not reject or clamp `num_frames` ([`media/video.py`](https://github.com/vllm-project/vllm/blob/6c95a641e95c0faa6f3aa802d1fd3cce3f3bc3ce/vllm/multimodal/media/video.py#L28-L66)). The resulting value reaches the following decoder branch ([`media/video.py`](https://github.com/vllm-project/vllm/blob/6c95a641e95c0faa6f3aa802d1fd3cce3f3bc3ce/vllm/multimodal/media/video.py#L103-L121)):

```python
if self.num_frames > 0:
    frame_parts = data.split(",", self.num_frames)[: self.num_frames]
elif self.num_frames == 0:
    raise ValueError("num_frames must be greater than 0 or -1")
else:
    frame_parts = data.split(",")

frames = np.stack(
    [np.asarray(load_frame(frame_data)) for frame_data in frame_parts]
)
```

The effective policy is therefore:

```text
server value:  {"video": {"num_frames": 8}}
request value: {"video": {"num_frames": -1}}
effective:     {"video": {"num_frames": -1}}
```

The source-traced actor-to-sink chain is:

```text
POST /v1/chat/completions
  -> ChatCompletionRequest.media_io_kwargs
  -> ChatCompletionRequest.build_chat_params()
  -> ChatParams.with_defaults()
  -> merge_media_io_kwargs() / VideoMediaIO.merge_kwargs()
  -> MediaConnector data-URL handling
  -> VideoMediaIO.load_base64("video/jpeg", ...)
  -> all-frame JPEG decode and np.stack
```

The `-1` sentinel is an intentional “all frames” sampling preference and was shown in the runtime API example for PR #34778. PR #38636 also retained a negative-value unlimited test. This report does not claim that the sentinel itself is new. The security issue is the absence of a trust separation between an operator preference and the server-side ceiling introduced to remediate GHSA-pq5c. A low-privilege request can disable that ceiling. A very large positive request value has the same policy problem because the client selects the effective maximum.

`VLLM_MAX_IMAGE_PIXELS` applies independently to each JPEG and does not bound aggregate frames or decoded pixels. Downstream model limits run after media loading and cannot prevent the split, per-frame allocations, and combined stack.

The report confirmed this behavior in v0.27.1 (`6e448d0ea9bf3d88d898b65449ca6dc2aec170ac`) and the then-current main revision. The online request-boundary fix is available in v0.31.0.

The recommended fix is a positive server-enforced maximum frame or aggregate decoded-work budget that is independent of request and startup sampling preferences. On the online API, request `num_frames <= 0` and values above the maximum should be rejected or mapped to the server limit.

### Impact

This is an availability/resource-exhaustion issue affecting deployments that expose a video-capable multimodal model to inference clients. An actor who can submit a video request can replace the operator's frame setting with `-1` or an attacker-selected large value. Memory consumption then scales with every supplied frame, including individual decoded frame arrays and the combined `np.stack` allocation.

If `--api-key` is configured, the actor needs a valid inference API key. Without a configured key, the stock `/v1/chat/completions` route has no API-key middleware. I retain the parent advisory's conservative `PR:L` vector.

In a memory-constrained deployment, a sufficiently large request can cause allocation failure or API/renderer process termination through container or host OOM handling. The retained safe run does not itself demonstrate a service crash. No confidentiality, integrity, or code-execution impact is claimed.

## Affected versions

- Package: `vllm` (PyPI).
- Affected versions: `>= 0.19.0, < 0.31.0`.
- First stable patched version: `0.31.0`.

PR #58830 blocks untrusted per-request `mm_processor_kwargs` and `media_io_kwargs` by default before preprocessing. Keep `--trust-request-mm-kwargs` disabled for untrusted clients. Explicitly enabling it restores trusted override behavior; the underlying processor/sampler is not independently bounded by this fix.

Advisory fixed in [#58830](https://github.com/vllm-project/vllm/pull/58830).

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
