fix(security): enforce VLLM_MAX_AUDIO_CLIP_FILESIZE_MB on all audio paths (#53561)

Signed-off-by: jperezde <jperezde@redhat.com>
This commit is contained in:
Juan Pérez de Algaba
2026-08-24 21:37:02 +00:00
committed by GitHub
parent 23ab0cfdbc
commit 0d7d5ed0b2
4 changed files with 27 additions and 5 deletions
+1 -1
View File
@@ -95,7 +95,7 @@ denial of service:
| --- | --- | --- |
| `VLLM_MAX_MEDIA_DOWNLOAD_SIZE_MB` | `256` | Maximum size in MB for a single remote media response. Oversized responses are rejected while streaming before the full body is materialized in memory. |
| `VLLM_MAX_IMAGE_PIXELS` | `178956970` (~179M pixels) | Maximum decoded image size in pixels. Images exceeding this are rejected before raster memory is allocated. Default matches PIL's built-in 2x decompression-bomb threshold (~680 MB for RGB). |
| `VLLM_MAX_AUDIO_CLIP_FILESIZE_MB` | `25` | Maximum filesize in MB for a single audio file. |
| `VLLM_MAX_AUDIO_CLIP_FILESIZE_MB` | `25` | Maximum compressed filesize in MB for a single audio file. Enforced on all audio inputs (multimodal chat URLs, speech-to-text uploads, data: URLs, and local file paths) before decoding begins. |
| `VLLM_MAX_AUDIO_DECODE_DURATION_S` | `600` | Maximum decoded audio duration in seconds. Prevents compressed audio from expanding into gigabytes of float32 PCM. |
| `VLLM_MAX_AUDIO_DECODE_BYTES` | `268435456` (256 MiB) | Maximum float32 PCM bytes that audio decoding may allocate. Guards against sample-rate forgery where an inflated header sample rate bypasses the duration guard while the actual frame count causes a multi-GiB allocation. |
+21 -1
View File
@@ -2,6 +2,7 @@
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from collections.abc import AsyncIterator, Iterator
from pathlib import Path
from unittest.mock import patch
import pytest
@@ -18,7 +19,7 @@ pytestmark = [pytest.mark.cpu_test, pytest.mark.skip_global_cleanup]
class _SyncResponse:
def __init__(self, chunks: list[bytes], content_length: int | None = None):
self.headers = (
{} if content_length is None else {"content-length": str(content_length)}
{} if content_length is None else {"Content-Length": str(content_length)}
)
self._chunks = chunks
self.iterated = 0
@@ -79,6 +80,25 @@ def test_audio_base64_rejects_before_decode(monkeypatch: pytest.MonkeyPatch):
decode.assert_not_called()
def test_audio_load_bytes_rejects_oversized(monkeypatch: pytest.MonkeyPatch):
monkeypatch.setattr(envs, "VLLM_MAX_AUDIO_CLIP_FILESIZE_MB", 1)
with pytest.raises(VLLMValidationError, match="Maximum file size exceeded"):
AudioMediaIO().load_bytes(b"\x00" * (MiB_bytes + 1))
def test_audio_load_file_rejects_oversized(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
):
monkeypatch.setattr(envs, "VLLM_MAX_AUDIO_CLIP_FILESIZE_MB", 1)
oversized = tmp_path / "big.wav"
oversized.write_bytes(b"\x00" * (MiB_bytes + 1))
with pytest.raises(VLLMValidationError, match="Maximum file size exceeded"):
AudioMediaIO().load_file(oversized)
def test_sync_http_reader_rejects_from_content_length_before_body_read():
response = _SyncResponse([b"A" * 10], content_length=MiB_bytes + 1)
connection = HTTPConnection()
+4 -3
View File
@@ -987,9 +987,10 @@ environment_variables: dict[str, Callable[[], Any]] = {
"VLLM_MEDIA_LOADING_THREAD_COUNT": lambda: int(
os.getenv("VLLM_MEDIA_LOADING_THREAD_COUNT", "8")
),
# Maximum filesize in MB for a single audio file when processing
# speech-to-text requests. Files larger than this will be rejected.
# Default is 25 MB
# Maximum filesize in MB for a single audio file. Enforced on all
# audio inputs (multimodal chat, speech-to-text uploads, data: URLs,
# and local file:// paths). Files larger than this will be rejected
# before decoding. Default is 25 MB.
"VLLM_MAX_AUDIO_CLIP_FILESIZE_MB": lambda: int(
os.getenv("VLLM_MAX_AUDIO_CLIP_FILESIZE_MB", "25")
),
+1
View File
@@ -312,6 +312,7 @@ class AudioMediaIO(MediaIO[tuple[npt.NDArray, float]]):
return self.load_bytes(pybase64.b64decode(data))
def load_file(self, filepath: Path) -> tuple[npt.NDArray, float]:
self._validate_encoded_size(filepath.stat().st_size)
return load_audio(
filepath,
sr=None,