PR #23196 opened by michaelni URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23196 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23196.patch
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. >From 6f53e62340154566ad922b30b13b1c7c0f68425a Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Thu, 21 May 2026 21:59:01 +0200 Subject: [PATCH] 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. --- libavformat/dashdec.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/libavformat/dashdec.c b/libavformat/dashdec.c index cac2250634..bed82fcf45 100644 --- a/libavformat/dashdec.c +++ b/libavformat/dashdec.c @@ -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} }; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
