1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2026-06-19 19:03:00 +02:00

avformat/dashdec: bound manifest reloads and fragment-open retries

Mirror the HLS demuxer's max_reload pattern

Reported as a DoS finding by Xueqing. Verified with the supplied PoC
server: a 30s ffmpeg run that previously generated ~10000 requests
without termination now exits in ~100ms after exactly max_reload
fragment-open failures.
This commit is contained in:
Michael Niedermayer
2026-05-21 21:59:01 +02:00
committed by michaelni
parent 461fb22053
commit 4945e33e98
+19
View File
@@ -113,6 +113,7 @@ struct representation {
int64_t cur_seg_offset;
int64_t cur_seg_size;
struct fragment *cur_seg;
int n_open_failures; /* consecutive open_input failures since last good read */
/* Currently active Media Initialization Section */
struct fragment *init_section;
@@ -157,6 +158,7 @@ typedef struct DASHContext {
char *allowed_extensions;
AVDictionary *avio_opts;
int max_url_size;
int max_reload;
char *cenc_decryption_key;
char *cenc_decryption_keys;
@@ -1649,6 +1651,7 @@ static struct fragment *get_current_fragment(struct representation *pls)
struct fragment *seg = NULL;
struct fragment *seg_ptr = NULL;
DASHContext *c = pls->parent->priv_data;
int reload_count = 0;
while (( !ff_check_interrupt(c->interrupt_callback)&& pls->n_fragments > 0)) {
if (pls->cur_seq_no < pls->n_fragments) {
@@ -1666,6 +1669,12 @@ static struct fragment *get_current_fragment(struct representation *pls)
seg->url_offset = seg_ptr->url_offset;
return seg;
} else if (c->is_live) {
if (reload_count++ >= c->max_reload) {
av_log(pls->parent, AV_LOG_ERROR,
"Reached max manifest reloads (%d) at seq %"PRId64"\n",
c->max_reload, pls->cur_seq_no);
return NULL;
}
refresh_manifest(pls->parent);
} else {
break;
@@ -1856,9 +1865,17 @@ restart:
goto end;
}
av_log(v->parent, AV_LOG_WARNING, "Failed to open fragment of playlist\n");
if (++v->n_open_failures > c->max_reload) {
av_log(v->parent, AV_LOG_ERROR,
"Reached max consecutive fragment open failures (%d), giving up\n",
c->max_reload);
ret = AVERROR_EOF;
goto end;
}
v->cur_seq_no++;
goto restart;
}
v->n_open_failures = 0;
}
if (v->init_sec_buf_read_offset < v->init_sec_data_len) {
@@ -2505,6 +2522,8 @@ static const AVOption dash_options[] = {
INT_MIN, INT_MAX, FLAGS},
{ "cenc_decryption_key", "Media default decryption key (hex)", OFFSET(cenc_decryption_key), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
{ "cenc_decryption_keys", "Media decryption keys by KID (hex)", OFFSET(cenc_decryption_keys), AV_OPT_TYPE_STRING, {.str = NULL}, INT_MIN, INT_MAX, .flags = FLAGS },
{ "max_reload", "Maximum number of manifest reloads in get_current_fragment() before giving up",
OFFSET(max_reload), AV_OPT_TYPE_INT, { .i64 = 100 }, 0, INT_MAX, FLAGS },
{NULL}
};