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


>From ad93a63fc8566cfff5845dc9126167c30f4d74c7 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 31 Oct 2026 05:17:46 +0100
Subject: [PATCH 1/5] avcodec/flicvideo: bound pixel writes at both ends of the
 frame storage

Fixes: out of array write
Fixes: GGsIHTeUwf0U
Fixes: AISLE-2026-0150-00642
Out of array write Replicated through API with ASAN

Found-by: Joshua Rogers (AISLE)
---
 libavcodec/flicvideo.c | 56 ++++++++++++++++++++++++------------------
 1 file changed, 32 insertions(+), 24 deletions(-)

diff --git a/libavcodec/flicvideo.c b/libavcodec/flicvideo.c
index 21f861dd3ae..d00ff9610d6 100644
--- a/libavcodec/flicvideo.c
+++ b/libavcodec/flicvideo.c
@@ -61,28 +61,40 @@
 #define FLC_MAGIC_CARPET_SYNTHETIC_TYPE_CODE (0xAF13)
 
 static inline int check_pixel_ptr(ptrdiff_t ptr, int n,
-                                  ptrdiff_t limit, int direction)
+                                  ptrdiff_t lo, ptrdiff_t hi)
 {
-    if (( direction && ptr + n > limit) ||
-        (!direction && ptr + n < limit))
+    if (ptr < lo || ptr + n > hi)
         return AVERROR_INVALIDDATA;
     return 0;
 }
 
 #define CHECK_PIXEL_PTR(n) \
 { \
-    ret = check_pixel_ptr(pixel_ptr, (n), pixel_limit, direction); \
+    ret = check_pixel_ptr(pixel_ptr, (n), pixel_lo, pixel_hi); \
     if (ret < 0) \
         return ret; \
 }
 
 #define CHECK_Y_PTR() \
 { \
-    ret = check_pixel_ptr(y_ptr, 0, pixel_limit, direction); \
+    ret = check_pixel_ptr(y_ptr, 0, pixel_lo, pixel_hi); \
     if (ret < 0) \
         return ret; \
 }
 
+static void pixel_range(const AVFrame *frame, ptrdiff_t *lo, ptrdiff_t *hi)
+{
+    ptrdiff_t linesize = frame->linesize[0];
+
+    if (linesize > 0) {
+        *lo = 0;
+        *hi = frame->height * linesize;
+    } else {
+        *lo = (frame->height - 1) * linesize;
+        *hi = -linesize;
+    }
+}
+
 typedef struct FlicDecodeContext {
     AVCodecContext *avctx;
     AVFrame *frame;
@@ -178,7 +190,7 @@ static int flic_decode_frame_1BPP(AVCodecContext *avctx,
     unsigned int chunk_size;
     int chunk_type;
 
-    int i, j, ret, direction;
+    int i, j, ret;
 
     int lines;
     int compressed_lines;
@@ -189,16 +201,15 @@ static int flic_decode_frame_1BPP(AVCodecContext *avctx,
     int pixel_skip;
     int pixel_countdown;
     unsigned char *pixels;
-    ptrdiff_t pixel_limit;
+    ptrdiff_t pixel_lo, pixel_hi;
 
     bytestream2_init(&g2, buf, buf_size);
 
     if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
         return ret;
 
-    direction = s->frame->linesize[0] > 0;
     pixels = s->frame->data[0];
-    pixel_limit = s->avctx->height * s->frame->linesize[0];
+    pixel_range(s->frame, &pixel_lo, &pixel_hi);
     if (buf_size < 16 || buf_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE)
         return AVERROR_INVALIDDATA;
     frame_size = bytestream2_get_le32(&g2);
@@ -373,7 +384,7 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx,
     unsigned int chunk_size;
     int chunk_type;
 
-    int i, j, ret, direction;
+    int i, j, ret;
 
     int color_packets;
     int color_changes;
@@ -389,16 +400,15 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx,
     int pixel_skip;
     int pixel_countdown;
     unsigned char *pixels;
-    ptrdiff_t pixel_limit;
+    ptrdiff_t pixel_lo, pixel_hi;
 
     bytestream2_init(&g2, buf, buf_size);
 
     if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
         return ret;
 
-    direction = s->frame->linesize[0] > 0;
     pixels = s->frame->data[0];
-    pixel_limit = s->avctx->height * s->frame->linesize[0];
+    pixel_range(s->frame, &pixel_lo, &pixel_hi);
     if (buf_size < 16 || buf_size > INT_MAX - (3 * 256 + 
AV_INPUT_BUFFER_PADDING_SIZE))
         return AVERROR_INVALIDDATA;
     frame_size = bytestream2_get_le32(&g2);
@@ -642,7 +652,7 @@ static int flic_decode_frame_8BPP(AVCodecContext *avctx,
                        "has incorrect size, skipping chunk\n", chunk_size - 6);
                 bytestream2_skip(&g2, chunk_size - 6);
             } else {
-                for (y_ptr = 0; check_pixel_ptr(y_ptr, s->avctx->width, 
pixel_limit, direction) == 0;
+                for (y_ptr = 0; check_pixel_ptr(y_ptr, s->avctx->width, 
pixel_lo, pixel_hi) == 0;
                      y_ptr += s->frame->linesize[0]) {
                     bytestream2_get_buffer(&g2, &pixels[y_ptr],
                                            s->avctx->width);
@@ -711,7 +721,7 @@ static int flic_decode_frame_15_16BPP(AVCodecContext *avctx,
     unsigned int chunk_size;
     int chunk_type;
 
-    int i, j, ret, direction;
+    int i, j, ret;
 
     int lines;
     int compressed_lines;
@@ -722,16 +732,15 @@ static int flic_decode_frame_15_16BPP(AVCodecContext 
*avctx,
     int pixel_countdown;
     unsigned char *pixels;
     int pixel;
-    ptrdiff_t pixel_limit;
+    ptrdiff_t pixel_lo, pixel_hi;
 
     bytestream2_init(&g2, buf, buf_size);
 
     if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
         return ret;
 
-    direction = s->frame->linesize[0] > 0;
     pixels = s->frame->data[0];
-    pixel_limit = s->avctx->height * s->frame->linesize[0];
+    pixel_range(s->frame, &pixel_lo, &pixel_hi);
 
     frame_size = bytestream2_get_le32(&g2);
     bytestream2_skip(&g2, 2);  /* skip the magic number */
@@ -930,7 +939,7 @@ static int flic_decode_frame_15_16BPP(AVCodecContext *avctx,
 
                 if (bytestream2_get_bytes_left(&g2) < 2 * s->avctx->width * 
s->avctx->height )
                     return AVERROR_INVALIDDATA;
-                for (y_ptr = 0; check_pixel_ptr(y_ptr, 2*s->avctx->width, 
pixel_limit, direction) == 0;
+                for (y_ptr = 0; check_pixel_ptr(y_ptr, 2*s->avctx->width, 
pixel_lo, pixel_hi) == 0;
                      y_ptr += s->frame->linesize[0]) {
 
                     pixel_countdown = s->avctx->width;
@@ -997,7 +1006,7 @@ static int flic_decode_frame_24BPP(AVCodecContext *avctx,
     unsigned int chunk_size;
     int chunk_type;
 
-    int i, j, ret, direction;
+    int i, j, ret;
 
     int lines;
     int compressed_lines;
@@ -1008,16 +1017,15 @@ static int flic_decode_frame_24BPP(AVCodecContext 
*avctx,
     int pixel_countdown;
     unsigned char *pixels;
     int pixel;
-    ptrdiff_t pixel_limit;
+    ptrdiff_t pixel_lo, pixel_hi;
 
     bytestream2_init(&g2, buf, buf_size);
 
     if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
         return ret;
 
-    direction = s->frame->linesize[0] > 0;
     pixels = s->frame->data[0];
-    pixel_limit = s->avctx->height * s->frame->linesize[0];
+    pixel_range(s->frame, &pixel_lo, &pixel_hi);
 
     frame_size = bytestream2_get_le32(&g2);
     bytestream2_skip(&g2, 2);  /* skip the magic number */
@@ -1216,7 +1224,7 @@ static int flic_decode_frame_24BPP(AVCodecContext *avctx,
                        "bigger than image, skipping chunk\n", chunk_size - 6);
                 bytestream2_skip(&g2, chunk_size - 6);
             } else {
-                for (y_ptr = 0; check_pixel_ptr(y_ptr, 3*s->avctx->width, 
pixel_limit, direction) == 0;
+                for (y_ptr = 0; check_pixel_ptr(y_ptr, 3*s->avctx->width, 
pixel_lo, pixel_hi) == 0;
                      y_ptr += s->frame->linesize[0]) {
 
                     bytestream2_get_buffer(&g2, pixels + y_ptr, 
3*s->avctx->width);
-- 
2.54.0


>From 7977ff5b7d80fa52beabe47c4840632ad1c898ef Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 31 Oct 2026 05:17:55 +0100
Subject: [PATCH 2/5] avcodec/hap: bound the sum of the uncompressed chunk
 sizes

Fixes: out of array write
Fixes: fglxAK3pz5sP
Fixes: AISLE-2026-0150-00462
Out of array write Replicated through UnModified FFmpeg (32-bit) with ASAN

Found-by: Joshua Rogers (AISLE)
---
 libavcodec/hapdec.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/libavcodec/hapdec.c b/libavcodec/hapdec.c
index 70bf592f2aa..7b35058467b 100644
--- a/libavcodec/hapdec.c
+++ b/libavcodec/hapdec.c
@@ -187,6 +187,7 @@ static int hap_parse_frame_header(AVCodecContext *avctx)
     ctx->tex_size = 0;
     for (i = 0; i < ctx->chunk_count; i++) {
         HapChunk *chunk = &ctx->chunks[i];
+        int64_t uncompressed_size;
 
         /* Check the compressed buffer is valid */
         if (chunk->compressed_offset + (uint64_t)chunk->compressed_size > 
bytestream2_get_bytes_left(gbc))
@@ -199,20 +200,21 @@ static int hap_parse_frame_header(AVCodecContext *avctx)
         /* Fill out uncompressed size */
         if (chunk->compressor == HAP_COMP_SNAPPY) {
             GetByteContext gbc_tmp;
-            int64_t uncompressed_size;
             bytestream2_init(&gbc_tmp, gbc->buffer + chunk->compressed_offset,
                              chunk->compressed_size);
             uncompressed_size = ff_snappy_peek_uncompressed_length(&gbc_tmp);
             if (uncompressed_size < 0) {
                 return uncompressed_size;
             }
-            chunk->uncompressed_size = uncompressed_size;
         } else if (chunk->compressor == HAP_COMP_NONE) {
-            chunk->uncompressed_size = chunk->compressed_size;
+            uncompressed_size = chunk->compressed_size;
         } else {
             return AVERROR_INVALIDDATA;
         }
-        ctx->tex_size += chunk->uncompressed_size;
+        if (uncompressed_size > INT_MAX - ctx->tex_size)
+            return AVERROR_INVALIDDATA;
+        chunk->uncompressed_size = uncompressed_size;
+        ctx->tex_size += uncompressed_size;
     }
 
     av_log(avctx, AV_LOG_DEBUG, "%s compressor\n", compressorstr);
-- 
2.54.0


>From ca1d13e306c640f9da4c6643978d610bf82e78f3 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 31 Oct 2026 05:18:04 +0100
Subject: [PATCH 3/5] avcodec/psd: reject an uncompressed image size above
 INT_MAX

Fixes: out of array write
Fixes: MymIHidCHVJu
Fixes: AISLE-2026-0150-00348
Out of array write Replicated through UnModified FFmpeg (32-bit) with ASAN

Found-by: Joshua Rogers (AISLE)
---
 libavcodec/psd.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libavcodec/psd.c b/libavcodec/psd.c
index 9dc56f8f5ea..f4973ecb7e5 100644
--- a/libavcodec/psd.c
+++ b/libavcodec/psd.c
@@ -444,6 +444,8 @@ static int decode_frame(AVCodecContext *avctx, AVFrame 
*picture,
     }
 
     s->uncompressed_size = s->line_size * s->height * s->channel_count;
+    if (s->uncompressed_size > INT_MAX)
+        return AVERROR_INVALIDDATA;
 
     /* decode picture if need */
     if (s->compression == PSD_RLE) {
-- 
2.54.0


>From d5c3e2951017a157d2db9fcb639e6344345b35f6 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 31 Oct 2026 05:18:14 +0100
Subject: [PATCH 4/5] avcodec/psd: free the RLE buffer when the frame
 allocation fails

This is not a security issue: the leak needs the frame allocation to
fail.

Fixes: memleak
Fixes: 9UXawhjlJguX
Fixes: AISLE-2026-0150-00349
Memory leak Replicated through API with LSAN

Found-by: Joshua Rogers (AISLE)
---
 libavcodec/psd.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libavcodec/psd.c b/libavcodec/psd.c
index f4973ecb7e5..0bf3868e9ca 100644
--- a/libavcodec/psd.c
+++ b/libavcodec/psd.c
@@ -469,8 +469,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame 
*picture,
         ptr_data = s->gb.buffer;
     }
 
-    if ((ret = ff_get_buffer(avctx, picture, 0)) < 0)
+    if ((ret = ff_get_buffer(avctx, picture, 0)) < 0) {
+        av_freep(&s->tmp);
         return ret;
+    }
 
     /* Store data */
     if ((avctx->pix_fmt == AV_PIX_FMT_YA8)||(avctx->pix_fmt == 
AV_PIX_FMT_YA16BE)){/* Interleaved */
-- 
2.54.0


>From 4fd65b002fcd2160f5e17ab021b6703c5e356932 Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Sat, 31 Oct 2026 05:18:18 +0100
Subject: [PATCH 5/5] avcodec/bitpacked: check the packet size before using it
 as a UYVY422 frame

Fixes: out of array read
Fixes: 2dyV2eNJkKBV
Fixes: AISLE-2026-0150-00092
Out of array read Replicated through API with ASAN

Found-by: Joshua Rogers (AISLE)
---
 libavcodec/bitpacked_dec.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/libavcodec/bitpacked_dec.c b/libavcodec/bitpacked_dec.c
index 8d88c556cc9..8afed8f8b2e 100644
--- a/libavcodec/bitpacked_dec.c
+++ b/libavcodec/bitpacked_dec.c
@@ -39,6 +39,13 @@ static int bitpacked_decode_uyvy422(AVCodecContext *avctx, 
AVFrame *frame,
 {
     int ret;
 
+    ret = av_image_fill_arrays(frame->data, frame->linesize, avpkt->data,
+                               avctx->pix_fmt, avctx->width, avctx->height, 1);
+    if (ret < 0)
+        return ret;
+    if (ret > avpkt->size)
+        return AVERROR_INVALIDDATA;
+
     /* there is no need to copy as the data already match
      * a known pixel format */
     frame->buf[0] = av_buffer_ref(avpkt->buf);
@@ -46,13 +53,6 @@ static int bitpacked_decode_uyvy422(AVCodecContext *avctx, 
AVFrame *frame,
         return AVERROR(ENOMEM);
     }
 
-    ret = av_image_fill_arrays(frame->data, frame->linesize, avpkt->data,
-                               avctx->pix_fmt, avctx->width, avctx->height, 1);
-    if (ret < 0) {
-        av_buffer_unref(&frame->buf[0]);
-        return ret;
-    }
-
     return 0;
 }
 
-- 
2.54.0

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

Reply via email to