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

magy_decode_frame() discarded the return values of the slice jobs, so a slice 
that failed (an uncompressed slice shorter than the plane, an invalid VLC code) 
still produced a frame with that slice unwritten. Since the multi-symbol VLC 
reader was added (8b7391cb5f), a compressed slice that runs out of bits is not 
detected at all: READ_PLANE stops when the bits run out and the remaining 
pixels keep the previous contents of the frame buffer, which with the default 
pool is an earlier decoded frame. Andreas noted the ignored execute2() return 
values in #20673.

A truncated MAGY packet in an AVI is enough to see it: the frame is output as a 
normal one, and its unwritten rows show an earlier picture of the same stream 
(or of the previous stream, for an application that reuses the context after 
avcodec_flush_buffers()).

The patch fails a row that ends before its width, passes a return array to 
execute2() and returns the first slice error. With AV_CODEC_FLAG_OUTPUT_CORRUPT 
the frame is output with AV_FRAME_FLAG_CORRUPT instead, as apv_decode does.

Tested: fate-magicyuv (7 sample tests) and fate-vsynth{1,2,3,_lena}-magicyuv 
pass on an ASan build. The truncated testcases now give "Slice 0 is incomplete 
or invalid." and no frame; with -flags output_corrupt they are output and 
marked corrupt.

This issue was found by an LLM-Enhanced Static Analysis Engine and verified 
manually.



>From 1558724f3a7ebc2c13b8403dad06542162268e60 Mon Sep 17 00:00:00 2001
From: Martin Mirchev <[email protected]>
Date: Sat, 7 Nov 2026 11:30:50 +0800
Subject: [PATCH] avcodec/magicyuv: do not output frames with incomplete slices

The return values of the slice jobs were discarded, so a slice that
failed (an uncompressed slice shorter than the plane, an invalid VLC
code) still led to *got_frame = 1 with the rest of that slice never
written. Since the multi-symbol VLC reader was added, a compressed slice
whose bitstream ends early is not even detected: READ_PLANE stops when
the bits run out and the remaining pixels keep whatever the frame buffer
held before, which with the default pool is an earlier decoded frame.

Fail a row that ends before its width, collect the slice return values
and return the error, or with AV_CODEC_FLAG_OUTPUT_CORRUPT output the
frame marked AV_FRAME_FLAG_CORRUPT.

Signed-off-by: Martin Mirchev <[email protected]>
---
 libavcodec/magicyuv.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/libavcodec/magicyuv.c b/libavcodec/magicyuv.c
index 04fb6cc147e..bd20806ad4d 100644
--- a/libavcodec/magicyuv.c
+++ b/libavcodec/magicyuv.c
@@ -69,6 +69,8 @@ typedef struct MagicYUVContext {
     int               vshift[4];
     Slice            *slices[4];      // slice bitstream positions for each 
plane
     unsigned int      slices_size[4]; // slice sizes for each plane
+    int              *slice_ret;      // return value of each slice job
+    unsigned int      slice_ret_size;
     VLC               vlc[4];         // VLC for each plane
     VLC_MULTI         multi[4];       // Buffer for joint VLC data
     int (*magy_decode_slice)(AVCodecContext *avctx, void *tdata,
@@ -132,6 +134,8 @@ static void magicyuv_median_pred16(uint16_t *dst, const 
uint16_t *src1,
     } \
     for (; x < width && get_bits_left(&gb) > 0; x++) \
         dst[x] = get_vlc2(&gb, vlc, vlc_bits, 3); \
+    if (x < width) \
+        return AVERROR_INVALIDDATA; \
     dst += stride; \
 }
 
@@ -619,6 +623,11 @@ static int magy_decode_frame(AVCodecContext *avctx, 
AVFrame *p,
     if (bytestream2_get_byteu(&gb) != s->planes)
         return AVERROR_INVALIDDATA;
 
+    av_fast_malloc(&s->slice_ret, &s->slice_ret_size,
+                   s->nb_slices * sizeof(*s->slice_ret));
+    if (!s->slice_ret)
+        return AVERROR(ENOMEM);
+
     bytestream2_skipu(&gb, s->nb_slices * s->planes);
 
     table_size = header_size + first_offset - bytestream2_tell(&gb);
@@ -635,7 +644,17 @@ static int magy_decode_frame(AVCodecContext *avctx, 
AVFrame *p,
 
     s->buf = avpkt->data;
     s->p = p;
-    avctx->execute2(avctx, s->magy_decode_slice, NULL, NULL, s->nb_slices);
+    avctx->execute2(avctx, s->magy_decode_slice, NULL, s->slice_ret, 
s->nb_slices);
+
+    for (i = 0; i < s->nb_slices; i++) {
+        if (s->slice_ret[i] < 0) {
+            av_log(avctx, AV_LOG_ERROR, "Slice %d is incomplete or 
invalid.\n", i);
+            if (!(avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT))
+                return s->slice_ret[i];
+            p->flags |= AV_FRAME_FLAG_CORRUPT;
+            break;
+        }
+    }
 
     if (is_rgb) {
         FFSWAP(uint8_t*, p->data[0], p->data[1]);
@@ -669,6 +688,9 @@ static av_cold int magy_decode_end(AVCodecContext *avctx)
     MagicYUVContext * const s = avctx->priv_data;
     int i;
 
+    av_freep(&s->slice_ret);
+    s->slice_ret_size = 0;
+
     for (i = 0; i < FF_ARRAY_ELEMS(s->slices); i++) {
         av_freep(&s->slices[i]);
         s->slices_size[i] = 0;
-- 
2.54.0

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

Reply via email to