PR #25796 opened by michaelni
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25796
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25796.patch


>From 9e3ad3c7749e761ea88b39bed2eef6f880a8418b Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Tue, 3 Nov 2026 15:25:11 +0100
Subject: [PATCH 1/8] avfilter/vf_vibrance: offset the 8-bit planar source by
 the source linesize

Regression since 725328672a.

Fixes: out of array read
Fixes: qafvJhC3VKaH
Fixes: 
swarm-Source-pointer-initialized-using-destination-linesize-in-vibrance_slice8-3c9b5e

Found-by: zhang xingxing <[email protected]>

Out of array read Replicated through UnModified FFmpeg with ASAN

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavfilter/vf_vibrance.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libavfilter/vf_vibrance.c b/libavfilter/vf_vibrance.c
index 295e5824120..273532e7500 100644
--- a/libavfilter/vf_vibrance.c
+++ b/libavfilter/vf_vibrance.c
@@ -85,9 +85,9 @@ static int vibrance_slice8(AVFilterContext *avctx, void *arg, 
int jobnr, int nb_
     const ptrdiff_t bslinesize = in->linesize[1];
     const ptrdiff_t rslinesize = in->linesize[2];
     const ptrdiff_t aslinesize = in->linesize[3];
-    const uint8_t *gsrc = in->data[0] + slice_start * glinesize;
-    const uint8_t *bsrc = in->data[1] + slice_start * blinesize;
-    const uint8_t *rsrc = in->data[2] + slice_start * rlinesize;
+    const uint8_t *gsrc = in->data[0] + slice_start * gslinesize;
+    const uint8_t *bsrc = in->data[1] + slice_start * bslinesize;
+    const uint8_t *rsrc = in->data[2] + slice_start * rslinesize;
     uint8_t *gptr = frame->data[0] + slice_start * glinesize;
     uint8_t *bptr = frame->data[1] + slice_start * blinesize;
     uint8_t *rptr = frame->data[2] + slice_start * rlinesize;
-- 
2.54.0


>From 2e63f41feaaddbb3e09cfa7a83ae27b2dc88bd98 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Fri, 6 Nov 2026 03:35:38 +0100
Subject: [PATCH 2/8] avfilter/avf_concat: split computing the padding silence
 from sending it

Keep the silence to send per output and send it one frame per call.

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavfilter/avf_concat.c | 65 +++++++++++++++++++++++++---------------
 1 file changed, 41 insertions(+), 24 deletions(-)

diff --git a/libavfilter/avf_concat.c b/libavfilter/avf_concat.c
index f6a1f758f52..09b3f091a6b 100644
--- a/libavfilter/avf_concat.c
+++ b/libavfilter/avf_concat.c
@@ -48,6 +48,11 @@ typedef struct ConcatContext {
         int64_t nb_frames;
         unsigned eof;
     } *in;
+    struct concat_silence {
+        int64_t pts;
+        int64_t nb_samples;
+        int64_t sent;
+    } *silence;
 } ConcatContext;
 
 #define OFFSET(x) offsetof(ConcatContext, x)
@@ -244,16 +249,14 @@ static void find_next_delta_ts(AVFilterContext *ctx, 
int64_t *seg_delta)
     *seg_delta = pts;
 }
 
-static int send_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no,
-                        int64_t seg_delta)
+static int queue_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no,
+                         int64_t seg_delta)
 {
     ConcatContext *cat = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[out_no];
-    int64_t base_pts = cat->in[in_no].pts + cat->delta_ts - seg_delta;
-    int64_t nb_samples, sent = 0;
-    int frame_nb_samples, ret;
+    struct concat_silence *silence = &cat->silence[out_no];
+    int64_t nb_samples;
     AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate };
-    AVFrame *buf;
 
     if (!rate_tb.den)
         return AVERROR_BUG;
@@ -263,24 +266,31 @@ static int send_silence(AVFilterContext *ctx, unsigned 
in_no, unsigned out_no,
         return AVERROR_INVALIDDATA;
     nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
                               outlink->time_base, rate_tb);
-    frame_nb_samples = FFMAX(9600, rate_tb.den / 5); /* arbitrary */
-    while (nb_samples) {
-        frame_nb_samples = FFMIN(frame_nb_samples, nb_samples);
-        buf = ff_get_audio_buffer(outlink, frame_nb_samples);
-        if (!buf)
-            return AVERROR(ENOMEM);
-        av_samples_set_silence(buf->extended_data, 0, frame_nb_samples,
-                               outlink->ch_layout.nb_channels, 
outlink->format);
-        buf->pts = base_pts + av_rescale_q(sent, rate_tb, outlink->time_base);
-        ret = ff_filter_frame(outlink, buf);
-        if (ret < 0)
-            return ret;
-        sent       += frame_nb_samples;
-        nb_samples -= frame_nb_samples;
-    }
+    silence->pts        = cat->in[in_no].pts + cat->delta_ts - seg_delta;
+    silence->nb_samples = nb_samples;
+    silence->sent       = 0;
     return 0;
 }
 
+static int send_silence(AVFilterContext *ctx, unsigned out_no)
+{
+    ConcatContext *cat = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[out_no];
+    struct concat_silence *silence = &cat->silence[out_no];
+    AVRational rate_tb = { 1, outlink->sample_rate };
+    int frame_nb_samples = FFMIN(FFMAX(9600, rate_tb.den / 5), /* arbitrary */
+                                 silence->nb_samples - silence->sent);
+    AVFrame *buf = ff_get_audio_buffer(outlink, frame_nb_samples);
+
+    if (!buf)
+        return AVERROR(ENOMEM);
+    av_samples_set_silence(buf->extended_data, 0, frame_nb_samples,
+                           outlink->ch_layout.nb_channels, outlink->format);
+    buf->pts = silence->pts + av_rescale_q(silence->sent, rate_tb, 
outlink->time_base);
+    silence->sent += frame_nb_samples;
+    return ff_filter_frame(outlink, buf);
+}
+
 static int flush_segment(AVFilterContext *ctx)
 {
     int ret;
@@ -299,10 +309,15 @@ static int flush_segment(AVFilterContext *ctx)
         str = cat->nb_streams[AVMEDIA_TYPE_VIDEO];
         str_max = str + cat->nb_streams[AVMEDIA_TYPE_AUDIO];
         for (; str < str_max; str++) {
-            ret = send_silence(ctx, cat->cur_idx - ctx->nb_outputs + str, str,
-                               seg_delta);
+            ret = queue_silence(ctx, cat->cur_idx - ctx->nb_outputs + str, str,
+                                seg_delta);
             if (ret < 0)
                 return ret;
+            while (cat->silence[str].sent < cat->silence[str].nb_samples) {
+                ret = send_silence(ctx, str);
+                if (ret < 0)
+                    return ret;
+            }
         }
     }
     return 0;
@@ -345,7 +360,8 @@ static av_cold int init(AVFilterContext *ctx)
     }
 
     cat->in = av_calloc(ctx->nb_inputs, sizeof(*cat->in));
-    if (!cat->in)
+    cat->silence = av_calloc(ctx->nb_outputs, sizeof(*cat->silence));
+    if (!cat->in || !cat->silence)
         return AVERROR(ENOMEM);
     cat->nb_in_active = ctx->nb_outputs;
     return 0;
@@ -356,6 +372,7 @@ static av_cold void uninit(AVFilterContext *ctx)
     ConcatContext *cat = ctx->priv;
 
     av_freep(&cat->in);
+    av_freep(&cat->silence);
 }
 
 static int activate(AVFilterContext *ctx)
-- 
2.54.0


>From eeb67d8731082f1c0ebd73aa16141a198535a4f0 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Fri, 6 Nov 2026 04:20:53 +0100
Subject: [PATCH 3/8] avfilter/avf_concat: count the padding silence back from
 the segment end

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavfilter/avf_concat.c | 21 +++++++++------------
 1 file changed, 9 insertions(+), 12 deletions(-)

diff --git a/libavfilter/avf_concat.c b/libavfilter/avf_concat.c
index 09b3f091a6b..00855d88885 100644
--- a/libavfilter/avf_concat.c
+++ b/libavfilter/avf_concat.c
@@ -49,9 +49,8 @@ typedef struct ConcatContext {
         unsigned eof;
     } *in;
     struct concat_silence {
-        int64_t pts;
+        int64_t end_pts;
         int64_t nb_samples;
-        int64_t sent;
     } *silence;
 } ConcatContext;
 
@@ -255,7 +254,6 @@ static int queue_silence(AVFilterContext *ctx, unsigned 
in_no, unsigned out_no,
     ConcatContext *cat = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[out_no];
     struct concat_silence *silence = &cat->silence[out_no];
-    int64_t nb_samples;
     AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate };
 
     if (!rate_tb.den)
@@ -264,11 +262,9 @@ static int queue_silence(AVFilterContext *ctx, unsigned 
in_no, unsigned out_no,
         return AVERROR_INVALIDDATA;
     if (seg_delta < cat->in[in_no].pts)
         return AVERROR_INVALIDDATA;
-    nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
-                              outlink->time_base, rate_tb);
-    silence->pts        = cat->in[in_no].pts + cat->delta_ts - seg_delta;
-    silence->nb_samples = nb_samples;
-    silence->sent       = 0;
+    silence->nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
+                                       outlink->time_base, rate_tb);
+    silence->end_pts    = cat->delta_ts;
     return 0;
 }
 
@@ -279,15 +275,16 @@ static int send_silence(AVFilterContext *ctx, unsigned 
out_no)
     struct concat_silence *silence = &cat->silence[out_no];
     AVRational rate_tb = { 1, outlink->sample_rate };
     int frame_nb_samples = FFMIN(FFMAX(9600, rate_tb.den / 5), /* arbitrary */
-                                 silence->nb_samples - silence->sent);
+                                 silence->nb_samples);
     AVFrame *buf = ff_get_audio_buffer(outlink, frame_nb_samples);
 
     if (!buf)
         return AVERROR(ENOMEM);
     av_samples_set_silence(buf->extended_data, 0, frame_nb_samples,
                            outlink->ch_layout.nb_channels, outlink->format);
-    buf->pts = silence->pts + av_rescale_q(silence->sent, rate_tb, 
outlink->time_base);
-    silence->sent += frame_nb_samples;
+    buf->pts = silence->end_pts -
+               av_rescale_q(silence->nb_samples, rate_tb, outlink->time_base);
+    silence->nb_samples -= frame_nb_samples;
     return ff_filter_frame(outlink, buf);
 }
 
@@ -313,7 +310,7 @@ static int flush_segment(AVFilterContext *ctx)
                                 seg_delta);
             if (ret < 0)
                 return ret;
-            while (cat->silence[str].sent < cat->silence[str].nb_samples) {
+            while (cat->silence[str].nb_samples) {
                 ret = send_silence(ctx, str);
                 if (ret < 0)
                     return ret;
-- 
2.54.0


>From 35aca8f5a1882f23a59a008a3a5d1cdb8ebdf53b Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Fri, 6 Nov 2026 03:35:38 +0100
Subject: [PATCH 4/8] avfilter/avf_concat: send the padding silence one frame
 at a time

Fixes: OOM
Fixes: rYbksRctTGL7
Fixes: swarm-Unbounded-audio-silence-allocation-driven-by-crafted-PTS-c5e945

Found-by: zhang xingxing <[email protected]>

OOM Replicated through UnModified FFmpeg with RSS monitoring

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavfilter/avf_concat.c | 34 +++++++++++++++++++++++++---------
 1 file changed, 25 insertions(+), 9 deletions(-)

diff --git a/libavfilter/avf_concat.c b/libavfilter/avf_concat.c
index 00855d88885..63ffc70afd3 100644
--- a/libavfilter/avf_concat.c
+++ b/libavfilter/avf_concat.c
@@ -248,12 +248,21 @@ static void find_next_delta_ts(AVFilterContext *ctx, 
int64_t *seg_delta)
     *seg_delta = pts;
 }
 
+static int silence_pending(AVFilterContext *ctx, unsigned out_no)
+{
+    ConcatContext *cat = ctx->priv;
+
+    return cat->silence[out_no].nb_samples &&
+           !ff_outlink_get_status(ctx->outputs[out_no]);
+}
+
 static int queue_silence(AVFilterContext *ctx, unsigned in_no, unsigned out_no,
                          int64_t seg_delta)
 {
     ConcatContext *cat = ctx->priv;
     AVFilterLink *outlink = ctx->outputs[out_no];
     struct concat_silence *silence = &cat->silence[out_no];
+    int64_t nb_samples;
     AVRational rate_tb = { 1, ctx->inputs[in_no]->sample_rate };
 
     if (!rate_tb.den)
@@ -262,9 +271,12 @@ static int queue_silence(AVFilterContext *ctx, unsigned 
in_no, unsigned out_no,
         return AVERROR_INVALIDDATA;
     if (seg_delta < cat->in[in_no].pts)
         return AVERROR_INVALIDDATA;
-    silence->nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
-                                       outlink->time_base, rate_tb);
-    silence->end_pts    = cat->delta_ts;
+    nb_samples = av_rescale_q(seg_delta - cat->in[in_no].pts,
+                              outlink->time_base, rate_tb);
+    if (nb_samples < 0 || nb_samples > INT64_MAX - silence->nb_samples)
+        return AVERROR_INVALIDDATA;
+    silence->nb_samples += nb_samples;
+    silence->end_pts     = cat->delta_ts;
     return 0;
 }
 
@@ -310,11 +322,6 @@ static int flush_segment(AVFilterContext *ctx)
                                 seg_delta);
             if (ret < 0)
                 return ret;
-            while (cat->silence[str].nb_samples) {
-                ret = send_silence(ctx, str);
-                if (ret < 0)
-                    return ret;
-            }
         }
     }
     return 0;
@@ -398,6 +405,8 @@ static int activate(AVFilterContext *ctx)
     /* Forward available frames */
     if (cat->cur_idx < ctx->nb_inputs) {
         for (i = 0; i < ctx->nb_outputs; i++) {
+            if (silence_pending(ctx, i))
+                continue;
             ret = ff_inlink_consume_frame(ctx->inputs[cat->cur_idx + i], 
&frame);
             if (ret < 0)
                 return ret;
@@ -413,6 +422,8 @@ static int activate(AVFilterContext *ctx)
         for (i = 0; i < ctx->nb_outputs; i++) {
             AVFilterLink *inlink = ctx->inputs[cat->cur_idx + i];
 
+            if (silence_pending(ctx, i))
+                continue;
             ret = ff_inlink_acknowledge_status(inlink, &status, &pts);
             /* TODO use pts */
             if (ret > 0) {
@@ -437,10 +448,15 @@ static int activate(AVFilterContext *ctx)
     ret = FFERROR_NOT_READY;
     for (i = 0; i < ctx->nb_outputs; i++) {
         if (ff_outlink_frame_wanted(ctx->outputs[i])) {
+            if (silence_pending(ctx, i))
+                return send_silence(ctx, i);
             if (cat->in[cat->cur_idx + i].eof) {
-                for (j = 0; j < ctx->nb_outputs; j++)
+                for (j = 0; j < ctx->nb_outputs; j++) {
+                    if (silence_pending(ctx, j))
+                        return send_silence(ctx, j);
                     if (!cat->in[cat->cur_idx + j].eof)
                         ff_inlink_request_frame(ctx->inputs[cat->cur_idx + j]);
+                }
                 return 0;
             } else {
                 ff_inlink_request_frame(ctx->inputs[cat->cur_idx + i]);
-- 
2.54.0


>From f5bcd20e79feed8a7bc9c90de0e0c77466976276 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Fri, 6 Nov 2026 03:19:21 +0100
Subject: [PATCH 5/8] avcodec/libjxldec: fail on Brotli compressed Exif boxes
 larger than 16 MiB

Regression since 8132ee046d.

Fixes: OOM
Fixes: OBQVvFEd6jIZ
Fixes: swarm-Unbounded-EXIF-buffer-doubling-causes-OOM-fd8b90

Found-by: zhang xingxing <[email protected]>

OOM Replicated through UnModified FFmpeg with RSS monitoring

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/libjxldec.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/libavcodec/libjxldec.c b/libavcodec/libjxldec.c
index a2f125998e9..6eb134416dd 100644
--- a/libavcodec/libjxldec.c
+++ b/libavcodec/libjxldec.c
@@ -45,6 +45,8 @@
 #include <jxl/thread_parallel_runner.h>
 #include "libjxl.h"
 
+#define EXIF_MAX_SIZE (16 << 20)
+
 typedef struct LibJxlDecodeContext {
     void *runner;
     JxlDecoder *decoder;
@@ -629,6 +631,15 @@ success:
             size_t remainder = JxlDecoderReleaseBoxBuffer(ctx->decoder);
             ctx->exif_pos = ctx->exif->size - remainder;
             size_t new_size = ctx->exif->size << 1;
+            JxlBoxType type;
+            if (new_size > EXIF_MAX_SIZE &&
+                (JxlDecoderGetBoxType(ctx->decoder, type, JXL_FALSE) != 
JXL_DEC_SUCCESS ||
+                 AV_RL32(type) == MKTAG('b','r','o','b'))) {
+                av_log(avctx, AV_LOG_ERROR, "Compressed Exif box larger than 
%d bytes\n", EXIF_MAX_SIZE);
+                av_buffer_unref(&ctx->exif);
+                ctx->exif_box = 0;
+                return AVERROR_INVALIDDATA;
+            }
             ret = av_buffer_realloc(&ctx->exif, new_size);
             if (ret < 0)
                 return AVERROR(ENOMEM);
-- 
2.54.0


>From a8592954469f519a828e14794dac1cf01bce1de7 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Tue, 3 Nov 2026 16:13:38 +0100
Subject: [PATCH 6/8] avfilter/dnn_backend_tf: free all output tensors of a
 request

This is not a security issue.

Fixes: hYQpMVEPvybk
Fixes: swarm-tf_free_request-always-frees-only-one-output-tensor-21656a

Found-by: zhang xingxing <[email protected]>

Memory leak Replicated through UnModified FFmpeg with LSan

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavfilter/dnn/dnn_backend_tf.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/libavfilter/dnn/dnn_backend_tf.c b/libavfilter/dnn/dnn_backend_tf.c
index f722e8bf8df..51dd88ce1c9 100644
--- a/libavfilter/dnn/dnn_backend_tf.c
+++ b/libavfilter/dnn/dnn_backend_tf.c
@@ -54,6 +54,7 @@ typedef struct TFModel {
 typedef struct TFInferRequest {
     TF_Output *tf_outputs;
     TF_Tensor **output_tensors;
+    uint32_t nb_output;
     TF_Output *tf_input;
     TF_Tensor *input_tensor;
 } TFInferRequest;
@@ -100,8 +101,7 @@ static void tf_free_request(TFInferRequest *request)
     av_freep(&request->tf_input);
     av_freep(&request->tf_outputs);
     if (request->output_tensors) {
-        int nb_output = 
sizeof(*request->output_tensors)/sizeof(request->output_tensors[0]);
-        for (uint32_t i = 0; i < nb_output; ++i) {
+        for (uint32_t i = 0; i < request->nb_output; ++i) {
             if (request->output_tensors[i]) {
                 TF_DeleteTensor(request->output_tensors[i]);
                 request->output_tensors[i] = NULL;
@@ -673,6 +673,7 @@ static int fill_model_input_tf(TFModel *tf_model, 
TFRequestItem *request) {
         ret = AVERROR(ENOMEM);
         goto err;
     }
+    infer_request->nb_output = task->nb_output;
 
     for (int i = 0; i < task->nb_output; ++i) {
         infer_request->output_tensors[i] = NULL;
-- 
2.54.0


>From ba8efc8856eaf26e00ba569673e68c176b06bcdb Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Tue, 3 Nov 2026 20:41:10 +0100
Subject: [PATCH 7/8] avcodec/libjxldec: return EOF after reporting a truncated
 codestream

Regression since 2c8835dd72.

Fixes: infinite loop
Fixes: k8CycfOvfx1i

Found during triage of the security report OBQVvFEd6jIZ

Infinite loop Replicated through UnModified FFmpeg with a timeout

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavcodec/libjxldec.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libavcodec/libjxldec.c b/libavcodec/libjxldec.c
index 6eb134416dd..693c2b48e11 100644
--- a/libavcodec/libjxldec.c
+++ b/libavcodec/libjxldec.c
@@ -450,6 +450,7 @@ static int libjxl_receive_frame(AVCodecContext *avctx, 
AVFrame *frame)
                 /* jret set by the last iteration of the loop */
                 if (ctx->jret == JXL_DEC_NEED_MORE_INPUT && 
!ctx->frame_complete) {
                     av_log(avctx, AV_LOG_ERROR, "Unexpected end of JXL 
codestream\n");
+                    ctx->jret = JXL_DEC_ERROR;
                     return AVERROR_INVALIDDATA;
                 } else if (ctx->frame_complete) {
                     ctx->jret = JXL_DEC_SUCCESS;
-- 
2.54.0


>From 7f95427bbac833a2aaf061669c70ef346d8067b7 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Tue, 3 Nov 2026 20:42:25 +0100
Subject: [PATCH 8/8] avutil/hash: terminate the hex string when size is 1

Found during triage of the security report qG4gdnzp4yAQ

Unterminated string Replicated through API with ASAN

Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavutil/hash.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavutil/hash.c b/libavutil/hash.c
index fbc24194dea..10f055aa401 100644
--- a/libavutil/hash.c
+++ b/libavutil/hash.c
@@ -228,7 +228,7 @@ void av_hash_final_hex(struct AVHashContext *ctx, uint8_t 
*dst, int size)
     unsigned rsize = av_hash_get_size(ctx), i;
 
     av_hash_final(ctx, buf);
-    for (i = 0; i < FFMIN(rsize, size / 2); i++)
+    for (i = 0; i < FFMIN(rsize, size - size / 2); i++)
         snprintf(dst + i * 2, size - i * 2, "%02x", buf[i]);
 }
 
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to