DoS via unbounded WAV header values in AudioSegment.from_wav()
Author: peteypivotsCreated Apr 9, 2026Updated Apr 9, 2026
Describe the bug
`AudioSegment.from_wav()` and `AudioSegment.from_file()` parse WAV headers and trust `nSamplesPerSec` and `nChannels`
without bounds checking. Malformed WAV files with absurd values load successfully but cause CPU exhaustion or OOM when
downstream operations like `export()`, `len()`, or `frame_rate` calculations trigger `ffmpeg` processing.
This allows a 44-byte file to DoS any service that uses `pydub` to process user-uploaded WAVs.
1. Download `pydub_wav_dos_pocs.zip` attached to this issue
2. Extract and run:
```python
from pydub import AudioSegment
# PoC 1: Samplerate overflow - HANG/OOM on export
a = AudioSegment.from_wav('evil.wav') # loads: frame_rate=100000000
a.export('out.mp3', format='mp3') # hangs/allocates huge memory
# PoC 2: Channel overflow - ffmpeg error/OOM on export
b = AudioSegment.from_wav('evil_channels.wav') # loads: channels=65535
b.export('out.mp3', format='mp3') # ffmpeg: Invalid channel layout- Observe: Files load without error, then exhaust resources during processing.
Expected behavior
pydub should reject WAV files with unreasonable header values before returning an AudioSegment. Similar to how ffmpeg itself rejects data_size=0xFFFFFFFF with "invalid start code", pydub should fail fast on sample_rate > 384000 or channels > 8.
Actual behavior
>>> a = AudioSegment.from_wav('evil.wav')
>>> a.frame_rate
100000000 # accepted
>>> a.channels
1
>>> len(a)
0 # duration calc underflows but doesn't errorEnvironment
- pydub: 0.25.1
- Python: 3.9.16
- ffmpeg: 5.1.4
- OS: Linux x86_64
Impact
Any service accepting user WAV uploads via pydub is vulnerable to DoS. A 44-byte file can cause:
- CPU exhaustion:
ffmpegattempts to resample 100MHz audio - Memory exhaustion:
ffmpegattempts 64k-channel processing - Request thread hangs until killed by timeout
Additional test cases in zip:
evil_bits.wav:bits_per_sample=65535— loads, fails faster on exportevil_data_size.wav:data_size=0xFFFFFFFF— already rejected byffmpeg, included to show where validation exists
Suggested fix
Add bounds checks after parsing fmt chunk in audio_segment.py:
MAX_SAMPLE_RATE = 384000 # DXD/DSD rates
MIN_SAMPLE_RATE = 8000 # telephone quality
MAX_CHANNELS = 8 # 7.1 surround
if not MIN_SAMPLE_RATE <= self.frame_rate <= MAX_SAMPLE_RATE:
raise CouldntDecodeError(f"Sample rate {self.frame_rate} out of bounds")
if not 1 <= self.channels <= MAX_CHANNELS:
raise CouldntDecodeError(f"Channel count {self.channels} out of bounds")
if self.sample_width not in (1, 2, 3, 4): # 8,16,24,32-bit
raise CouldntDecodeError(f"Sample width {self.sample_width} unsupported")Happy to submit a PR if maintainers agree with the approach.
Source: jiaaro/pydub