From 5f0da26d974f1a22246eb084c9dfaf0181d4e647 Mon Sep 17 00:00:00 2001 From: dkgkdfg65 Date: Wed, 3 Jun 2026 00:51:21 +0800 Subject: [PATCH] avcodec/jpeg2000dec: fix cdef remapping out-of-bounds write (CVE-2025-9951) Backports the upstream fix onto the 7.1.1 base: - 104d6846c1be avcodec/jpeg2000dec: move cdef default check into get_siz() - 01a292c7e365 avcodec/jpeg2000dec: implement cdef remapping during pixel format matching A crafted cdef box could remap a component onto an arbitrary plane, so WRITE_FRAME indexed planes out of bounds and wrote past a smaller plane's buffer (heap-buffer-overflow in write_frame_8). get_siz() now finalizes cdef early and rejects channel combinations that don't match the component count. Verified with AddressSanitizer on the 7.1.1 build: the proof-of-concept file no longer overflows (rejected as invalid) and valid JPEG2000 input still decodes. --- libavcodec/jpeg2000dec.c | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c index 2e09b279dc985..508013a7f196d 100644 --- a/libavcodec/jpeg2000dec.c +++ b/libavcodec/jpeg2000dec.c @@ -271,6 +271,25 @@ static int get_siz(Jpeg2000DecoderContext *s) return AVERROR_INVALIDDATA; } + for (i = 0; i < s->ncomponents; i++) { + if (s->cdef[i] < 0) { + for (i = 0; i < s->ncomponents; i++) { + s->cdef[i] = i + 1; + } + if ((s->ncomponents & 1) == 0) + s->cdef[s->ncomponents-1] = 0; + } + } + // after here we no longer have to consider negative cdef + + int cdef_used = 0; + for (i = 0; i < s->ncomponents; i++) + cdef_used |= 1<cdef[i]; + + // Check that the channels we have are what we expect for the number of components + if (cdef_used != ((int[]){0,2,3,14,15})[s->ncomponents]) + return AVERROR_INVALIDDATA; + for (i = 0; i < s->ncomponents; i++) { // Ssiz_i XRsiz_i, YRsiz_i uint8_t x = bytestream2_get_byteu(&s->g); s->cbps[i] = (x & 0x7f) + 1; @@ -283,7 +302,9 @@ static int get_siz(Jpeg2000DecoderContext *s) av_log(s->avctx, AV_LOG_ERROR, "Invalid sample separation %d/%d\n", s->cdx[i], s->cdy[i]); return AVERROR_INVALIDDATA; } - log2_chroma_wh |= s->cdy[i] >> 1 << i * 4 | s->cdx[i] >> 1 << i * 4 + 2; + int i_remapped = s->cdef[i] ? s->cdef[i]-1 : (s->ncomponents-1); + + log2_chroma_wh |= s->cdy[i] >> 1 << i_remapped * 4 | s->cdx[i] >> 1 << i_remapped * 4 + 2; } s->numXtiles = ff_jpeg2000_ceildiv(s->width - s->tile_offset_x, s->tile_width); @@ -2853,17 +2874,6 @@ static int jpeg2000_decode_frame(AVCodecContext *avctx, AVFrame *picture, if (ret = jpeg2000_read_bitstream_packets(s)) goto end; - for (int x = 0; x < s->ncomponents; x++) { - if (s->cdef[x] < 0) { - for (x = 0; x < s->ncomponents; x++) { - s->cdef[x] = x + 1; - } - if ((s->ncomponents & 1) == 0) - s->cdef[s->ncomponents-1] = 0; - break; - } - } - avctx->execute2(avctx, jpeg2000_decode_tile, picture, NULL, s->numXtiles * s->numYtiles); jpeg2000_dec_cleanup(s);