fix(security): prevent infinite loop in split_audio with NaN audio sa… (#46463)
Juan Pérez de Algaba committed
Jun 23, 2026 at 12:24 UTC
83fa302ca430592bbf547a20b818cc34c9f40e89
2 files changed
+43
-2
tests/multimodal/test_audio.py
+36
@@ -760,6 +760,42 @@ class TestAudioChunking:
760
assert chunks[0][0] == audio[0]
761
assert chunks[-1][-1] == audio[-1]
762
763
+ def test_find_split_point_nan_input(self):
764
+ """find_split_point must not return 0 for all-NaN input."""
765
+ from vllm.multimodal.audio import find_split_point
766
+
767
+ nan_audio = np.full(32000, float("nan"), dtype=np.float32)
768
+ start_idx = 16000
769
+ end_idx = 32000
770
+
771
+ split_idx = find_split_point(
772
+ wav=nan_audio,
773
+ start_idx=start_idx,
774
+ end_idx=end_idx,
775
+ min_energy_window=1600,
776
+ )
777
+
778
+ # Must return start_idx (the safe fallback), not 0
779
+ assert split_idx == start_idx
780
+
781
+ def test_split_audio_nan_input_terminates(self):
782
+ """split_audio must terminate on all-NaN audio (no infinite loop)."""
783
+ # 31 seconds of NaN at 16kHz — longer than max_clip_duration_s
784
+ nan_audio = np.full(16000 * 31, float("nan"), dtype=np.float32)
785
+
786
+ chunks = split_audio(
787
+ audio_data=nan_audio,
788
+ sample_rate=16000,
789
+ max_clip_duration_s=30.0,
790
+ overlap_duration_s=1.0,
791
+ min_energy_window_size=1600,
792
+ )
793
+
794
+ # Must produce at least 2 chunks and cover all samples
795
+ assert len(chunks) >= 2
796
+ total_samples = sum(c.shape[-1] for c in chunks)
797
+ assert total_samples == nan_audio.shape[-1]
798
+
799
def test_split_audio_with_different_sample_rates(self):
800
"""Test chunking works with different sample rates."""
801
vllm/multimodal/audio.py
+7
-2
@@ -378,6 +378,11 @@ def split_audio(
378
audio_data, search_start, search_end, min_energy_window_size
379
)
380
381
+ # Guarantee forward progress: if split_point didn't advance,
382
+ # fall back to the hard chunk boundary.
383
+ if split_point <= i:
384
+ split_point = min(i + chunk_size, audio_data.shape[-1])
385
+
386
# Extract chunk up to the split point
387
chunks.append(audio_data[..., i:split_point])
388
i = split_point
@@ -423,12 +428,12 @@ def find_split_point(
428
429
# Calculate RMS energy in small windows
430
min_energy = math.inf
426
- quietest_idx = 0
431
+ quietest_idx = start_idx
432
433
for i in range(0, len(segment) - min_energy_window, min_energy_window):
434
window = segment[i : i + min_energy_window]
435
energy = (window**2).mean() ** 0.5
431
- if energy < min_energy:
436
+ if not math.isnan(energy) and energy < min_energy:
437
quietest_idx = i + start_idx
438
min_energy = energy
439