PR #25015 opened by michaelni URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25015 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25015.patch
This is the first batch of fixes for (pre existing) issues found in reviews >From 8c72121b3e4d89c3817c99693b53eb3f516789ac Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:15:19 +0200 Subject: [PATCH 01/13] avcodec/hevc: keep the active VPS when a layer SPS is cleared set_sps(s, l, NULL) drops the SPS of a non-base layer whose VPS does not match the active one. It also dropped the active VPS of the base layer, the next base layer frame then dereferenced the NULL VPS in ff_hevc_is_alpha_video(). Fixes: NULL pointer dereference Fixes: 4WKa0bETBVfE Regression since: c7f0753a6b Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24895 NULL pointer dereference Replicated through UnModified FFmpeg with ASAN --- libavcodec/hevc/hevcdec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libavcodec/hevc/hevcdec.c b/libavcodec/hevc/hevcdec.c index a36ea57bc2..d5e0fa06d3 100644 --- a/libavcodec/hevc/hevcdec.c +++ b/libavcodec/hevc/hevcdec.c @@ -764,11 +764,12 @@ static int set_sps(HEVCContext *s, HEVCLayerContext *l, const HEVCSPS *sps) pic_arrays_free(l); av_refstruct_unref(&l->sps); - av_refstruct_unref(&s->vps); if (!sps) return 0; + av_refstruct_unref(&s->vps); + ret = pic_arrays_init(l, sps); if (ret < 0) goto fail; -- 2.52.0 >From 15d4e20f2387547e975c7ca0b8128ce4cbf81100 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:19:20 +0200 Subject: [PATCH 02/13] avcodec/aac/aacdec_usac: do not store the custom channel layout in the output configuration OutputConfiguration is copied by value between oc[0] and oc[1]. A custom channel layout built from bsOutputChannelPos that cannot be retyped to a native layout owns a heap allocated map, the copies alias it and it is freed twice. The layout only reaches avctx->ch_layout and is replaced by the one derived from the elements when the configuration is parsed completely. Fixes: double free Fixes: Qn47KDJBCeGk Regression since: eee5fa0808 Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24677 Double free Replicated through UnModified FFmpeg with ASAN --- libavcodec/aac/aacdec_usac.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libavcodec/aac/aacdec_usac.c b/libavcodec/aac/aacdec_usac.c index 7a34cf63d3..3587c76c07 100644 --- a/libavcodec/aac/aacdec_usac.c +++ b/libavcodec/aac/aacdec_usac.c @@ -625,24 +625,22 @@ int ff_aac_usac_config_decode(AACDecContext *ac, AVCodecContext *avctx, if (nb_channels > 64) return AVERROR(EINVAL); - av_channel_layout_uninit(&ac->oc[1].ch_layout); + AVChannelLayout ch_layout = { 0 }; - ret = av_channel_layout_custom_init(&ac->oc[1].ch_layout, nb_channels); + ret = av_channel_layout_custom_init(&ch_layout, nb_channels); if (ret < 0) return ret; for (int i = 0; i < nb_channels; i++) { - AVChannelCustom *cm = &ac->oc[1].ch_layout.u.map[i]; + AVChannelCustom *cm = &ch_layout.u.map[i]; cm->id = usac_ch_pos_to_av[get_bits(gb, 5)]; /* bsOutputChannelPos */ } - ret = av_channel_layout_retype(&ac->oc[1].ch_layout, - AV_CHANNEL_ORDER_NATIVE, + ret = av_channel_layout_retype(&ch_layout, AV_CHANNEL_ORDER_NATIVE, AV_CHANNEL_LAYOUT_RETYPE_FLAG_CANONICAL); - if (ret < 0) - return ret; - - ret = av_channel_layout_copy(&avctx->ch_layout, &ac->oc[1].ch_layout); + if (ret >= 0) + ret = av_channel_layout_copy(&avctx->ch_layout, &ch_layout); + av_channel_layout_uninit(&ch_layout); if (ret < 0) return ret; } else { -- 2.52.0 >From 8b4538075cfb131be56cecdd9f3ca882b4e8c9c3 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:19:20 +0200 Subject: [PATCH 03/13] avcodec/aac: do not overwrite avctx->ch_layout when restoring the output configuration ff_aac_output_configure() copies the restored layout into avctx->ch_layout, the plain assignment before it dropped whatever avctx->ch_layout owned. This is hardening, no leak was replicated. Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24677 --- libavcodec/aac/aacdec.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libavcodec/aac/aacdec.c b/libavcodec/aac/aacdec.c index fdebe139c3..66ab648016 100644 --- a/libavcodec/aac/aacdec.c +++ b/libavcodec/aac/aacdec.c @@ -472,7 +472,6 @@ static void pop_output_configuration(AACDecContext *ac) if (ac->oc[1].status != OC_LOCKED && ac->oc[0].status != OC_NONE) { copy_oc(&ac->oc[1], &ac->oc[0]); - ac->avctx->ch_layout = ac->oc[1].ch_layout; ff_aac_output_configure(ac, ac->oc[1].layout_map, ac->oc[1].layout_map_tags, ac->oc[1].status, 0); } -- 2.52.0 >From c5d4d4d09883d08d15f3639ca9a271222194dbd0 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:19:21 +0200 Subject: [PATCH 04/13] avcodec/aac/aacdec_usac: release the extension payload buffers before clearing the configuration The buffers of fragmented extension payloads were lost when a new configuration was parsed. Fixes: memleak Fixes: yxoDgZkJaJ4D Regression since: eee5fa0808 Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24677 Memleak Replicated through UnModified FFmpeg with ASAN --- libavcodec/aac/aacdec_usac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavcodec/aac/aacdec_usac.c b/libavcodec/aac/aacdec_usac.c index 3587c76c07..5c6d04226d 100644 --- a/libavcodec/aac/aacdec_usac.c +++ b/libavcodec/aac/aacdec_usac.c @@ -571,6 +571,8 @@ int ff_aac_usac_config_decode(AACDecContext *ac, AVCodecContext *avctx, if (!ac) return AVERROR_PATCHWELCOME; + for (int i = 0; i < usac->nb_elems; i++) + av_refstruct_unref(&usac->elems[i].ext.pl_buf); memset(usac, 0, sizeof(*usac)); usac->loudness.input_method_val = -1; -- 2.52.0 >From aee421e722a8a3d3727b466c087556114e602c93 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:22:27 +0200 Subject: [PATCH 05/13] avcodec/pthread_frame: give each worker context its own copy of the channel layout The worker contexts are created by av_memdup() of the user context, a custom channel layout map was shared with all of them. Copying the layout into the first worker in update_context_from_thread() freed the shared map and read it afterwards, decoders which uninit the layout in init freed it twice. Fixes: heap use after free Fixes: double free Fixes: l08oZE4M5Rmz Regression since: 548aeb9383 Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24577 Heap use after free Replicated through UnModified FFmpeg with ASAN --- libavcodec/pthread_frame.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c index 5ec9a8a974..44b48875da 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c @@ -790,6 +790,7 @@ av_cold void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) ff_pthread_free(p, per_thread_offsets); av_packet_free(&p->avpkt); + av_channel_layout_uninit(&ctx->ch_layout); av_freep(&p->avctx); } @@ -824,6 +825,7 @@ static av_cold int init_thread(PerThreadContext *p, int *threads_to_free, copy->priv_data = NULL; copy->decoded_side_data = NULL; copy->nb_decoded_side_data = 0; + copy->ch_layout = (AVChannelLayout){ 0 }; /* From now on, this PerThreadContext will be cleaned up by * ff_frame_thread_free in case of errors. */ @@ -832,6 +834,10 @@ static av_cold int init_thread(PerThreadContext *p, int *threads_to_free, p->parent = fctx; p->avctx = copy; + err = av_channel_layout_copy(©->ch_layout, &avctx->ch_layout); + if (err < 0) + return err; + copy->internal = ff_decode_internal_alloc(); if (!copy->internal) return AVERROR(ENOMEM); -- 2.52.0 >From 0365a2976b2be52bd95671244fbbb920ea6cd82d Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:24:05 +0200 Subject: [PATCH 06/13] avformat/libmodplug: return the error of a failed read instead of passing it to ModPlug_Load() The negative result was used as the length of the never written buffer, libmodplug converted it to an unsigned length and parsed the buffer with that bound. Fixes: use of uninitialized memory Fixes: cIsMCH26EnE2 Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24914 Use of uninitialized memory Replicated through UnModified FFmpeg with Valgrind --- libavformat/libmodplug.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libavformat/libmodplug.c b/libavformat/libmodplug.c index d9ab14b3bc..56a2c8b386 100644 --- a/libavformat/libmodplug.c +++ b/libavformat/libmodplug.c @@ -201,6 +201,8 @@ static int modplug_read_header(AVFormatContext *s) if (!modplug->buf) return AVERROR(ENOMEM); sz = avio_read(pb, modplug->buf, sz); + if (sz < 0) + return sz; ModPlug_GetSettings(&settings); settings.mChannels = 2; -- 2.52.0 >From 0bc0d4f8d0bc528d8a210dd49561595c880d9498 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:31:13 +0200 Subject: [PATCH 07/13] avcodec/tdsc: reject unknown cursor formats before replacing the cursor sprite The sprite buffer was reallocated and the new geometry stored before the format was checked, the uninitialized sprite was then painted onto every following frame. Fixes: use of uninitialized memory Fixes: JJihVQxE36RL Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24682 Use of uninitialized memory Replicated through UnModified FFmpeg with Valgrind --- libavcodec/tdsc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libavcodec/tdsc.c b/libavcodec/tdsc.c index f31655ce3d..976b67b8f9 100644 --- a/libavcodec/tdsc.c +++ b/libavcodec/tdsc.c @@ -209,6 +209,11 @@ static int tdsc_load_cursor(AVCodecContext *avctx) cursor_fmt = bytestream2_get_le32(&ctx->gbc); + if (cursor_fmt != CUR_FMT_MONO && cursor_fmt != CUR_FMT_BGRA && + cursor_fmt != CUR_FMT_RGBA) { + avpriv_request_sample(avctx, "Cursor format %08x", cursor_fmt); + return AVERROR_PATCHWELCOME; + } if (w < 1 || w > 256 || h < 1 || h > 256) { av_log(avctx, AV_LOG_ERROR, "Invalid cursor dimensions %dx%d.\n", w, h); return AVERROR_INVALIDDATA; @@ -305,9 +310,6 @@ static int tdsc_load_cursor(AVCodecContext *avctx) } } break; - default: - avpriv_request_sample(avctx, "Cursor format %08x", cursor_fmt); - return AVERROR_PATCHWELCOME; } return 0; -- 2.52.0 >From f23e57d259de4a379efabb9af384a7a1df88e3ff Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:31:13 +0200 Subject: [PATCH 08/13] avcodec/sunrast: expand only the bytes of a packed line that were decoded The 1 and 4 bit per pixel expansion read (w + 7 >> 3) * depth bytes per line while only (depth * w + 7) >> 3 were written, for 4 bits per pixel and widths that are not a multiple of 8 up to 3 uninitialized bytes per line were expanded into the padding of the line. Fixes: use of uninitialized memory Fixes: mGcNxX6EOTeJ Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24877 Use of uninitialized memory Replicated through API with Valgrind --- libavcodec/sunrast.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavcodec/sunrast.c b/libavcodec/sunrast.c index 1cd51f4637..fcab3de3b5 100644 --- a/libavcodec/sunrast.c +++ b/libavcodec/sunrast.c @@ -199,7 +199,7 @@ static int sunrast_decode_frame(AVCodecContext *avctx, AVFrame *p, uint8_t *ptr_free = ptr2; ptr = p->data[0]; for (y=0; y<h; y++) { - for (x = 0; x < (w + 7 >> 3) * depth; x++) { + for (x = 0; x < len; x++) { if (depth == 1) { ptr[8*x] = ptr2[x] >> 7; ptr[8*x+1] = ptr2[x] >> 6 & 1; -- 2.52.0 >From c074239cdc0cda180a05488c12e113f8c93ff7ca Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:31:13 +0200 Subject: [PATCH 09/13] swscale: reject interlaced frames whose fields have no lines A one line interlaced frame has an empty bottom field, when source and destination format were equal the graph was still built for it and divided by the zero slice height. Fixes: division by zero Fixes: 9FwqJRpgNJo3 Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24851 Division by zero Replicated through UnModified FFmpeg with UBSAN --- libswscale/swscale.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libswscale/swscale.c b/libswscale/swscale.c index d224c3275b..070b0f5939 100644 --- a/libswscale/swscale.c +++ b/libswscale/swscale.c @@ -1562,6 +1562,12 @@ int sws_frame_setup(SwsContext *ctx, const AVFrame *dst, const AVFrame *src) goto fail; } + if (src_fmt.height <= 0 || dst_fmt.height <= 0) { + err_msg = "Interlaced frames need at least two lines"; + ret = AVERROR(EINVAL); + goto fail; + } + src_ok = ff_test_fmt(backends, &src_fmt, 0); dst_ok = ff_test_fmt(backends, &dst_fmt, 1); if ((!src_ok || !dst_ok) && !ff_fmt_equal(&src_fmt, &dst_fmt)) { -- 2.52.0 >From 2e2d2e45df7984c925c34e7da0e5499ae3fb2498 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:31:13 +0200 Subject: [PATCH 10/13] avutil/imgutils: compute the line size of bitstream formats without overflow Fixes: signed integer overflow Fixes: P0BVJKDC5JLx Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24756 Signed integer overflow Replicated through UnModified FFmpeg with UBSAN --- libavutil/imgutils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libavutil/imgutils.c b/libavutil/imgutils.c index 8ff8a6be41..1aa6ab0cd3 100644 --- a/libavutil/imgutils.c +++ b/libavutil/imgutils.c @@ -69,7 +69,7 @@ int image_get_linesize(int width, int plane, linesize = max_step * shifted_w; if (desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) - linesize = (linesize + 7) >> 3; + linesize = (linesize >> 3) + !!(linesize & 7); return linesize; } -- 2.52.0 >From 0babfe27510d4b4390d7879b4b0596d69e4c8c5c Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:37:41 +0200 Subject: [PATCH 11/13] ffplay: round the chapter length without overflowing A chapter length near INT64_MAX overflowed the rounding, the hour count was also truncated to int. Fixes: signed integer overflow Fixes: 7JHyBJsQ7bNE Regression since: 80a55704b7 Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24589 Signed integer overflow Replicated through UnModified FFmpeg with UBSAN --- fftools/ffplay.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fftools/ffplay.c b/fftools/ffplay.c index c9369d6a72..bea5816089 100644 --- a/fftools/ffplay.c +++ b/fftools/ffplay.c @@ -1374,10 +1374,10 @@ static void chapter_list_script(VideoState *is, AVBPrint *script) av_bprintf(script, CHAPTER_LIST_EVENT "Hint,{\\an5\\pos(%d,%d)}i\n", l.detail_x, y + l.line / 2); if (row->length < 0) continue; - seconds = (row->length + AV_TIME_BASE / 2) / AV_TIME_BASE; + seconds = av_rescale_rnd(row->length, 1, AV_TIME_BASE, AV_ROUND_NEAR_INF); av_bprintf(script, CHAPTER_LIST_EVENT "%s,{\\an6\\pos(%d,%d)}", style, l.length_x, y + l.line / 2); if (seconds >= 3600) - av_bprintf(script, "%d:%02d:%02d\n", (int)(seconds / 3600), (int)(seconds / 60 % 60), (int)(seconds % 60)); + av_bprintf(script, "%"PRId64":%02d:%02d\n", seconds / 3600, (int)(seconds / 60 % 60), (int)(seconds % 60)); else av_bprintf(script, "%d:%02d\n", (int)(seconds / 60), (int)(seconds % 60)); } -- 2.52.0 >From f7adeba059f9a8be00ac15781d625b5f79fa5398 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:39:11 +0200 Subject: [PATCH 12/13] avformat/flacdec: do not flush the parser on a zero length read in flac_read_timestamp() A read that returns no data because the input reported EOF transiently was passed to the parser as the EOF flush, the data of the next read was then parsed by the flushed parser. Fixes: NULL pointer dereference Fixes: iEqlwpm7chzS Found during the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24909 NULL pointer dereference Replicated through API with ASAN --- libavformat/flacdec.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libavformat/flacdec.c b/libavformat/flacdec.c index 8d907bdc30..12d4b7305f 100644 --- a/libavformat/flacdec.c +++ b/libavformat/flacdec.c @@ -311,6 +311,10 @@ av_unused static int64_t flac_read_timestamp(AVFormatContext *s, int stream_inde if (ret >= 0 || ret == AVERROR(EAGAIN)) ret = ff_raw_read_partial_packet(s, pkt); + if (!ret) { + av_packet_unref(pkt); + continue; + } if (ret < 0){ if (ret == AVERROR(EAGAIN)) continue; -- 2.52.0 >From b6600ee457565b82b32f0efdd03d37f491c53d22 Mon Sep 17 00:00:00 2001 From: Michael Niedermayer <[email protected]> Date: Sun, 11 Oct 2026 07:39:11 +0200 Subject: [PATCH 13/13] avformat/libcurl: fail a ranged request that delivered no data A completed reply with a valid Content-Range but an empty body left the request offset unchanged, so the identical request was sent again without limit. Fixes: endless request loop Fixes: nnnKyLY2U5HN Found during triage of the review of https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24607 Endless request loop Replicated through UnModified FFmpeg --- libavformat/libcurl.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libavformat/libcurl.c b/libavformat/libcurl.c index 80c1f7a9fa..6f17d0ded3 100644 --- a/libavformat/libcurl.c +++ b/libavformat/libcurl.c @@ -769,6 +769,16 @@ static void on_done(CurlContext *c, CURLcode code) if (c->end_off > 0) file_end = FFMIN(file_end, c->end_off - 1); if (c->seekable && c->request_end >= 0 && c->request_end < file_end) { + if (!received) { + av_log(c->h, AV_LOG_ERROR, "Server sent back an empty reply " + "for the range starting at %"PRId64"\n", c->request_start); + pthread_mutex_lock(&c->mutex); + c->loop->num_errors++; + update_status_locked(c, AVERROR(EIO), code); + pthread_cond_broadcast(&c->cond); + pthread_mutex_unlock(&c->mutex); + return; + } c->is_initial = 0; start_request(c); return; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
