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]
