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

When ff_format_output_open() succeeds avf2->pb (AVIOContext + buffer)
is allocated. If the subsequent avformat_write_header() fails,
ctx->header_written stays 0, so the cleanup path in
fifo_thread_write_trailer() never calls ff_format_io_close(avf2, &avf2->pb).

On the next recovery attempt ff_format_output_open() overwrites avf2->pb
with a fresh allocation, making the old pointer permanently unreachable.
With max_recovery_attempts=0 (unlimited retries) the leak is unbounded.

Measured with rtmps:// output (~150 KB per AVIOContext + TLS session):
1 retry/2s over 9 hours = ~2.3 GB leaked -> OOM kill in production.

Reproducer (triggers avformat_write_header failure on every attempt):
  ffmpeg -f lavfi -re -i testsrc=size=320x240:rate=25 \
         -f lavfi -re -i sine=frequency=440:sample_rate=44100 \
         -map 0:v -map 1:a -c:v flv -b:v 300k -c:a aac -b:a 64k \
         -flags +global_header \
         -f fifo -fifo_format flv \
         -attempt_recovery 1 -recovery_wait_time 1 \
         -max_recovery_attempts 0 -drop_pkts_on_overflow 1 \
         /dev/full
  Without fix: VmRSS +820 KB/60s. With fix: VmRSS stable (<12 KB/60s).

Fix: call ff_format_io_close(avf2, &avf2->pb) in the failure path so
the AVIOContext is released before the next recovery attempt.

Signed-off-by: Elimeshi1 <[email protected]>


>From 935eec6ed27d11195039a8145ac96d2a14d60fdc Mon Sep 17 00:00:00 2001
From: Elimeshi1 <[email protected]>
Date: Wed, 15 Apr 2026 15:29:34 +0300
Subject: [PATCH] avformat/fifo: fix avf2->pb leak on avformat_write_header()
 failure

When ff_format_output_open() succeeds avf2->pb (AVIOContext + buffer)
is allocated. If the subsequent avformat_write_header() fails,
ctx->header_written stays 0, so the cleanup path in
fifo_thread_write_trailer() never calls ff_format_io_close(avf2, &avf2->pb).

On the next recovery attempt ff_format_output_open() overwrites avf2->pb
with a fresh allocation, making the old pointer permanently unreachable.
With max_recovery_attempts=0 (unlimited retries) the leak is unbounded.

Measured with rtmps:// output (~150 KB per AVIOContext + TLS session):
1 retry/2s over 9 hours = ~2.3 GB leaked -> OOM kill in production.

Reproducer (triggers avformat_write_header failure on every attempt):
  ffmpeg -f lavfi -re -i testsrc=size=320x240:rate=25 \
         -f lavfi -re -i sine=frequency=440:sample_rate=44100 \
         -map 0:v -map 1:a -c:v flv -b:v 300k -c:a aac -b:a 64k \
         -flags +global_header \
         -f fifo -fifo_format flv \
         -attempt_recovery 1 -recovery_wait_time 1 \
         -max_recovery_attempts 0 -drop_pkts_on_overflow 1 \
         /dev/full
  Without fix: VmRSS +820 KB/60s. With fix: VmRSS stable (<12 KB/60s).

Fix: call ff_format_io_close(avf2, &avf2->pb) in the failure path so
the AVIOContext is released before the next recovery attempt.

Signed-off-by: Elimeshi1 <[email protected]>
---
 libavformat/fifo.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libavformat/fifo.c b/libavformat/fifo.c
index e66002b902..bb9c79e615 100644
--- a/libavformat/fifo.c
+++ b/libavformat/fifo.c
@@ -146,8 +146,11 @@ static int fifo_thread_write_header(FifoThreadContext *ctx)
         ffstream(avf2->streams[i])->cur_dts = 0;
 
     ret = avformat_write_header(avf2, &format_options);
-    if (!ret)
+    if (!ret) {
         ctx->header_written = 1;
+    } else {
+        ff_format_io_close(avf2, &avf2->pb);
+    }
 
     // Check for options unrecognized by underlying muxer
     if (format_options) {
-- 
2.52.0

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

Reply via email to