CVE-2026-34755: vLLM Affected by Denial of Service via Unbounded Frame Count in video/jpeg Base64 Processing
Summary
The VideoMediaIO.loadbase64() method at vllm/multimodal/media/video.py:51-62 splits video/jpeg data URLs by comma to extract individual JPEG frames, but does not enforce a frame count limit. The numframes parameter (default: 32), which is enforced by the loadbytes() code path at line 47-48, is completely bypassed in the video/jpeg base64 path. An attacker can send a single API request containing thousands of comma-separated base64-encoded JPEG frames, causing the server to decode all frames into memory and crash with OOM.
Details
Vulnerable code
python video.py:51-62 def loadbase64(self, mediatype: str, data: str) -> tuple[npt.NDArray, dict[str, Any]]: if mediatype.lower() == "video/jpeg": loadframe = partial(self.imageio.loadbase64, "image/jpeg") return np.stack( [np.asarray(loadframe(framedata)) for framedata in data.split(",")] # ^^^^^^^^^^ # Unbounded split — no frame count limit ), {} return self.loadbytes(base64.b64decode(data))
The loadbytes() path (line 47-48) properly delegates to a video loader that respects self.numframes (default 32). The loadbase64("video/jpeg", ...) path bypasses this limit entirely — data.split(",") produces an unbounded list and every frame is decoded into a numpy array.
video/jpeg is part of vLLM's public API
video/jpeg is a vLLM-specific MIME type, not IANA-registered. However it is part of the public API surface:
- encodevideourl() at vllm/multimodal/utils.py:96-108 generates data:video/jpeg;base64,... URLs - Official test suites at tests/entrypoints/openai/testvideo.py:62 and tests/entrypoints/testchatutils.py:153 both use this format
Memory amplification
Each JPEG frame decodes to a full numpy array. For 640x480 RGB images, each frame is ~921 KB decoded. 5000 frames = ~4.6 GB. np.stack() then creates an additional copy. The compressed JPEG payload is small (~100 KB for 5000 frames) but decompresses to gigabytes.
Data flow
POST /v1/chat/completions → chatutils.py:1434 videourl type → mmparser.parsevideo() → chatutils.py:872 parsevideo() → self.connector.fetchvideo() → connector.py:295 fetchvideo() → loadfromurl(url, self.videoio) → connector.py:91 loaddataurl(): urlspec.path.split(",", 1) → mediatype = "video/jpeg" → data = "<frame1>,<frame2>,...,<frame10000>" → connector.py:100 mediaio.loadbase64("video/jpeg", data) → video.py:54 data.split(",") ← UNBOUNDED → video.py:55-57 all frames decoded into numpy arrays → video.py:56 np.stack([...]) ← massive combined array → OOM
connector.py:91 uses split(",", 1) which splits on only the first comma. All remaining commas stay in data and are later split by video.py:54.
Comparison with existing protections
| Code Path | Frame Limit | File | |-----------|-------------|------| | loadbytes() (binary video) | Yes — numframes (default 32) | video.py:46-49 | | loadbase64("video/jpeg", ...) | No — unlimited data.split(",") | video.py:51-62 |
Other sources
vLLM is an inference and serving engine for large language models (LLMs). From 0.7.0 to before 0.19.0, the VideoMediaIO.loadbase64() method at vllm/multimodal/media/video.py splits video/jpeg data URLs by comma to extract individual JPEG frames, but does not enforce a frame count limit. The numframes parameter (default: 32), which is enforced by the loadbytes() code path, is completely bypassed in the video/jpeg base64 path. An attacker can send a single API request containing thousands of comma-separated base64-encoded JPEG frames, causing the server to decode all frames into memory and crash with OOM. This vulnerability is fixed in 0.19.0.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/vllmto a version that resolves this vulnerability.Fixed in 0.19.0 - Upgrade
Upgrade
vLLMto a version that resolves this vulnerability.Fixed in 0.19.0 - Compensating control
Limit the maximum size and/or length of user-supplied `data:video/jpeg;base64,...` payloads (e.g., reject requests whose `data` field contains more than a small allowed number of comma-separated base64 frames) before they reach `vllm/multimodal/media/video.py`.