CVE-2026-55646: vLLM STT Upload OOM Fix Is a Partial fix
Summary
The patch replaces unbounded request.file.read() calls in vLLM speech-to-text transcription and translation endpoints with a bounded helper that checks file.size when available and otherwise reads in 64 KiB chunks until the configured limit is exceeded. This materially reduces the denial-of-service risk by preventing full materialization of oversized uploads in application memory. However, the helper still accumulates all accepted chunks into a list and returns a single bytes object, so memory usage remains proportional to the configured maximum for every accepted request and can still be amplified under concurrency. The fix addresses the immediate root cause for oversized-upload OOM, but it does not redesign the endpoint into a streaming pipeline.
Analysis
Vulnerability
CVE-2026-55646 describes a remote denial-of-service condition in vLLM speech-to-text endpoints where uploaded audio was read into RAM before size validation. The vulnerable call sites in both transcription and translation used an unbounded read:
audio_data = await request.file.read()Because validation happened after full materialization, an attacker could submit an oversized multipart upload and force memory allocation proportional to attacker-controlled input size, potentially triggering host OOM and crashing the service. The affected patch references are the upstream pull request and commit at PR #45510 and commit b997071, with vulnerability context also recorded by CVE.
Patch
The patch introduces a shared helper, read_upload_with_limit(), in vllm/entrypoints/speech_to_text/base/utils.py and rewires both API routers to use it instead of directly calling request.file.read(). The helper enforces the configured limit from VLLM_MAX_AUDIO_CLIP_FILESIZE_MB by first checking file.size when present, then falling back to chunked reads of 64 KiB and aborting as soon as the accumulated byte count exceeds the limit.
async def read_upload_with_limit(
file: UploadFile,
max_size_mb: float | None = None,
) -> bytes:
if max_size_mb is None:
max_size_mb = envs.VLLM_MAX_AUDIO_CLIP_FILESIZE_MB
max_bytes = int(max_size_mb * MiB_bytes)
if file.size is not None and file.size > max_bytes:
raise VLLMValidationError(
"Maximum file size exceeded",
parameter="audio_filesize_mb",
value=file.size / MiB_bytes,
)
chunks: list[bytes] = []
total = 0
while True:
chunk = await file.read(_READ_CHUNK_SIZE)
if not chunk:
break
total += len(chunk)
if total > max_bytes:
raise VLLMValidationError(
"Maximum file size exceeded",
parameter="audio_filesize_mb",
value=total / MiB_bytes,
)
chunks.append(chunk)
return b"".join(chunks)The router changes are narrow and targeted:
from vllm.entrypoints.speech_to_text.base.utils import read_upload_with_limit
audio_data = await read_upload_with_limit(request.file)The accompanying regression tests cover early rejection via file.size, rejection during chunked reads when size metadata is absent, exact-boundary acceptance, one-byte-over rejection, environment-default behavior, and a check that oversized chunked uploads are not fully materialized. These changes are documented in PR #45510 and commit b997071.
Review
Pros
- Eliminates the direct root trigger for the reported issue: the endpoints no longer call unbounded
request.file.read()before enforcing limits. - Uses a two-stage guard: metadata-based rejection when
file.sizeis available, and chunked enforcement when it is not. This is important because multipart metadata may be absent or unreliable. - Stops reading as soon as the limit is crossed, which bounds attacker-driven allocation for oversized uploads to roughly the configured maximum plus one chunk.
- Centralizes the logic in a shared utility, reducing the chance that transcription and translation drift into inconsistent behavior.
- Regression coverage is strong for boundary conditions and specifically asserts that oversized inputs are not fully read.
Cons
- The helper still buffers all accepted content in memory by appending chunks and returning
b"".join(chunks). That means memory consumption remains O(limit) per accepted request, which is safer than O(attacker input) but still significant under concurrency. - The patch is scoped only to the two speech-to-text router call sites shown in the diff. It does not demonstrate a broader audit for similar upload patterns elsewhere in the codebase.
- The implementation trusts
file.sizeonly as an optimization, which is correct, but there is no evidence in the patch of upstream request-body limits at the ASGI server, reverse proxy, or multipart parser layer. Application-layer checks alone still allow network and parser work before rejection. - There is no streaming handoff to downstream audio processing. If later stages can operate incrementally, this patch does not exploit that to reduce steady-state memory pressure.
Verdict
Partial fix.
The patch directly addresses the vulnerable behavior identified in NVD by preventing oversized uploads from being fully materialized before validation, so it is effective against the specific OOM vector described in the advisory. However, it does not fully eliminate memory-amplification risk in the endpoint design because accepted files are still accumulated into memory and returned as a single bytes object. For the stated CVE, this is a solid mitigation and likely sufficient to close the reported bug; for defense-in-depth, the next step should be end-to-end streaming or spooled-file processing plus request-size enforcement at the ingress layer. Source-grounded references: PR #45510, commit b997071, NVD, and CVE.
Recommended Labs
Try this vulnerability pattern yourself with hands-on labs.
- Intent.android
Closest defensive match to this CVE’s root cause: insufficient input validation before processing attacker-controlled data. CVE-2026-55646 is a resource-exhaustion/DoS bug triggered because uploaded content is handled before size limits are enforced. This lab helps practise validating untrusted inputs earlier in the request lifecycle.
- Class Pollution.py
Although not a direct DoS lab, this Python hands-on challenge is useful because the CVE affects a Python-serving stack. It reinforces secure request/data handling and validation patterns in Python applications, which are directly relevant when reviewing fixes for upload parsing and pre-allocation logic.
- MCP Poison.ai
Best AI-adjacent defensive lab from the current catalogue. While it is not specifically about memory exhaustion, it is relevant to securing AI application boundaries and validating untrusted inputs/tools. For teams working on vLLM-based endpoints, it provides adjacent hands-on practice in securing AI-facing application surfaces.