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

The shorten decoder declares AV_CODEC_CAP_DELAY but does not handle
draining (empty) packets safely. During draining, the decoder retries
decoding stale data from its internal buffer, returning
AVERROR_INVALIDDATA on every call until decode.c's safety counter
overflows and returns AVERROR_BUG, crashing the fuzzer harness
assertion.

Additionally, in container formats where extradata_size > 0,
read_header() skips audio header parsing (goto end). If extradata is
corrupted or fuzzed without setting valid audio parameters, outputting a
frame causes decode.c to assert AVERROR_BUG ("An invalid frame was
output by a decoder").

Fix both issues inside shorten_decode_frame():
1. During draining (!avpkt->data), allow decode to proceed normally with
   buffered data (so multi-block buffers are fully drained). At
   finish_frame, if no frame was produced, clear the buffer so
   subsequent drain calls return EOF without retrying stale data.
2. Validate sample_rate and ch_layout after read_header(s) returns,
   ensuring uninitialized extradata fails cleanly with
   AVERROR_INVALIDDATA instead of emitting an invalid frame.

Signed-off-by: Aolin Feng <[email protected]>



>From 66e1725318935823ffa8bbda087fc23fb47277c9 Mon Sep 17 00:00:00 2001
From: Aolin Feng <[email protected]>
Date: Thu, 23 Jul 2026 18:00:00 +0000
Subject: [PATCH] avcodec/shorten: fix AVERROR_BUG during draining with invalid
 data

The shorten decoder declares AV_CODEC_CAP_DELAY but does not handle
draining (empty) packets safely. During draining, the decoder retries
decoding stale data from its internal buffer, returning
AVERROR_INVALIDDATA on every call until decode.c's safety counter
overflows and returns AVERROR_BUG, crashing the fuzzer harness
assertion.

Additionally, in container formats where extradata_size > 0,
read_header() skips audio header parsing (goto end). If extradata is
corrupted or fuzzed without setting valid audio parameters, outputting a
frame causes decode.c to assert AVERROR_BUG ("An invalid frame was
output by a decoder").

Fix both issues inside shorten_decode_frame():
1. During draining (!avpkt->data), allow decode to proceed normally with
   buffered data (so multi-block buffers are fully drained). At
   finish_frame, if no frame was produced, clear the buffer so
   subsequent drain calls return EOF without retrying stale data.
2. Validate sample_rate and ch_layout after read_header(s) returns,
   ensuring uninitialized extradata fails cleanly with
   AVERROR_INVALIDDATA instead of emitting an invalid frame.

Signed-off-by: Aolin Feng <[email protected]>
---
 libavcodec/shorten.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/libavcodec/shorten.c b/libavcodec/shorten.c
index c0325a81f8..d1f4fcffa9 100644
--- a/libavcodec/shorten.c
+++ b/libavcodec/shorten.c
@@ -564,6 +564,12 @@ static int shorten_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
     buf_size         += s->bitstream_size;
     s->bitstream_size = buf_size;
 
+    /* During draining, if no buffered data remains, return EOF. */
+    if (!avpkt->data && !buf_size) {
+        *got_frame_ptr = 0;
+        return 0;
+    }
+
     /* do not decode until buffer has at least max_framesize bytes or
      * the end of the file has been reached */
     if (buf_size < s->max_framesize && avpkt->data) {
@@ -580,6 +586,10 @@ static int shorten_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
 
         if ((ret = read_header(s)) < 0)
             return ret;
+        /* Validate audio parameters so uninitialized extradata fails cleanly
+         * with AVERROR_INVALIDDATA instead of emitting an invalid frame. */
+        if (avctx->sample_rate <= 0 || 
!av_channel_layout_check(&avctx->ch_layout))
+            return AVERROR_INVALIDDATA;
 
         if (avpkt->size) {
             int max_framesize = s->blocksize * s->channels * 8;
@@ -774,6 +784,14 @@ static int shorten_decode_frame(AVCodecContext *avctx, 
AVFrame *frame,
 
 finish_frame:
     s->bitindex = get_bits_count(&s->gb) - 8 * (get_bits_count(&s->gb) / 8);
+    /* During draining, if no frame was produced, the remaining buffer is
+     * unparseable stale data. Clear it so the next drain call returns EOF
+     * instead of retrying the same bytes in an infinite loop. */
+    if (!avpkt->data && !*got_frame_ptr) {
+        s->bitstream_size  = 0;
+        s->bitstream_index = 0;
+        return 0;
+    }
     i           = get_bits_count(&s->gb) / 8;
     if (i > buf_size) {
         av_log(s->avctx, AV_LOG_ERROR, "overread: %d\n", i - buf_size);
-- 
2.52.0

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

Reply via email to