Fixes: OOM Fixes: 3541/clusterfuzz-testcase-minimized-6469958596820992 Adds support for decoding codeblock data larger than 8kb Reduces decoder memory consumption Found-by: continuous fuzzing process https://github.com/google/oss-fuzz/tree/master/projects/ffmpeg Signed-off-by: Michael Niedermayer <michael@niedermayer.cc>tags/n4.0
@@ -663,7 +663,8 @@ static void encode_cblk(Jpeg2000EncoderContext *s, Jpeg2000T1Context *t1, Jpeg20 | |||||
bpno = cblk->nonzerobits - 1; | bpno = cblk->nonzerobits - 1; | ||||
} | } | ||||
ff_mqc_initenc(&t1->mqc, cblk->data); | |||||
cblk->data[0] = 0; | |||||
ff_mqc_initenc(&t1->mqc, cblk->data + 1); | |||||
for (passno = 0; bpno >= 0; passno++){ | for (passno = 0; bpno >= 0; passno++){ | ||||
nmsedec=0; | nmsedec=0; | ||||
@@ -796,7 +797,7 @@ static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in | |||||
if (cblk->ninclpasses){ | if (cblk->ninclpasses){ | ||||
if (s->buf_end - s->buf < cblk->passes[cblk->ninclpasses-1].rate) | if (s->buf_end - s->buf < cblk->passes[cblk->ninclpasses-1].rate) | ||||
return -1; | return -1; | ||||
bytestream_put_buffer(&s->buf, cblk->data, cblk->passes[cblk->ninclpasses-1].rate | |||||
bytestream_put_buffer(&s->buf, cblk->data + 1, cblk->passes[cblk->ninclpasses-1].rate | |||||
- cblk->passes[cblk->ninclpasses-1].flushed_len); | - cblk->passes[cblk->ninclpasses-1].flushed_len); | ||||
bytestream_put_buffer(&s->buf, cblk->passes[cblk->ninclpasses-1].flushed, | bytestream_put_buffer(&s->buf, cblk->passes[cblk->ninclpasses-1].flushed, | ||||
cblk->passes[cblk->ninclpasses-1].flushed_len); | cblk->passes[cblk->ninclpasses-1].flushed_len); | ||||
@@ -937,6 +938,9 @@ static int encode_tile(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno | |||||
} | } | ||||
} | } | ||||
} | } | ||||
prec->cblk[cblkno].data = av_malloc(1 + 8192); | |||||
if (!prec->cblk[cblkno].data) | |||||
return AVERROR(ENOMEM); | |||||
encode_cblk(s, &t1, prec->cblk + cblkno, tile, xx1 - xx0, yy1 - yy0, | encode_cblk(s, &t1, prec->cblk + cblkno, tile, xx1 - xx0, yy1 - yy0, | ||||
bandpos, codsty->nreslevels - reslevelno - 1); | bandpos, codsty->nreslevels - reslevelno - 1); | ||||
xx0 = xx1; | xx0 = xx1; | ||||
@@ -357,7 +357,6 @@ static int init_prec(Jpeg2000Band *band, | |||||
comp->reslevel[reslevelno-1].coord[1][0]; | comp->reslevel[reslevelno-1].coord[1][0]; | ||||
} | } | ||||
cblk->zero = 0; | |||||
cblk->lblock = 3; | cblk->lblock = 3; | ||||
cblk->length = 0; | cblk->length = 0; | ||||
memset(cblk->lengthinc, 0, sizeof(cblk->lengthinc)); | memset(cblk->lengthinc, 0, sizeof(cblk->lengthinc)); | ||||
@@ -598,9 +597,18 @@ void ff_jpeg2000_cleanup(Jpeg2000Component *comp, Jpeg2000CodingStyle *codsty) | |||||
for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++) { | for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++) { | ||||
if (band->prec) { | if (band->prec) { | ||||
Jpeg2000Prec *prec = band->prec + precno; | Jpeg2000Prec *prec = band->prec + precno; | ||||
int nb_code_blocks = prec->nb_codeblocks_height * prec->nb_codeblocks_width; | |||||
av_freep(&prec->zerobits); | av_freep(&prec->zerobits); | ||||
av_freep(&prec->cblkincl); | av_freep(&prec->cblkincl); | ||||
av_freep(&prec->cblk); | |||||
if (prec->cblk) { | |||||
int cblkno; | |||||
for (cblkno = 0; cblkno < nb_code_blocks; cblkno ++) { | |||||
Jpeg2000Cblk *cblk = &prec->cblk[cblkno]; | |||||
av_freep(&cblk->data); | |||||
} | |||||
av_freep(&prec->cblk); | |||||
} | |||||
} | } | ||||
} | } | ||||
@@ -168,8 +168,8 @@ typedef struct Jpeg2000Cblk { | |||||
uint16_t lengthinc[JPEG2000_MAX_PASSES]; | uint16_t lengthinc[JPEG2000_MAX_PASSES]; | ||||
uint8_t nb_lengthinc; | uint8_t nb_lengthinc; | ||||
uint8_t lblock; | uint8_t lblock; | ||||
uint8_t zero; | |||||
uint8_t data[8192]; | |||||
uint8_t *data; | |||||
size_t data_allocated; | |||||
int nb_terminations; | int nb_terminations; | ||||
int nb_terminationsinc; | int nb_terminationsinc; | ||||
int data_start[JPEG2000_MAX_PASSES]; | int data_start[JPEG2000_MAX_PASSES]; | ||||
@@ -1001,10 +1001,18 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, | |||||
if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0) | if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0) | ||||
return ret; | return ret; | ||||
if (ret > sizeof(cblk->data)) { | |||||
if (ret > cblk->data_allocated) { | |||||
size_t new_size = FFMAX(2*cblk->data_allocated, ret); | |||||
void *new = av_realloc(cblk->data, new_size); | |||||
if (new) { | |||||
cblk->data = new; | |||||
cblk->data_allocated = new_size; | |||||
} | |||||
} | |||||
if (ret > cblk->data_allocated) { | |||||
avpriv_request_sample(s->avctx, | avpriv_request_sample(s->avctx, | ||||
"Block with lengthinc greater than %"SIZE_SPECIFIER"", | "Block with lengthinc greater than %"SIZE_SPECIFIER"", | ||||
sizeof(cblk->data)); | |||||
cblk->data_allocated); | |||||
return AVERROR_PATCHWELCOME; | return AVERROR_PATCHWELCOME; | ||||
} | } | ||||
cblk->lengthinc[cblk->nb_lengthinc++] = ret; | cblk->lengthinc[cblk->nb_lengthinc++] = ret; | ||||
@@ -1030,8 +1038,16 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, | |||||
for (cblkno = 0; cblkno < nb_code_blocks; cblkno++) { | for (cblkno = 0; cblkno < nb_code_blocks; cblkno++) { | ||||
Jpeg2000Cblk *cblk = prec->cblk + cblkno; | Jpeg2000Cblk *cblk = prec->cblk + cblkno; | ||||
for (cwsno = 0; cwsno < cblk->nb_lengthinc; cwsno ++) { | for (cwsno = 0; cwsno < cblk->nb_lengthinc; cwsno ++) { | ||||
if (cblk->data_allocated < cblk->length + cblk->lengthinc[cwsno] + 4) { | |||||
size_t new_size = FFMAX(2*cblk->data_allocated, cblk->length + cblk->lengthinc[cwsno] + 4); | |||||
void *new = av_realloc(cblk->data, new_size); | |||||
if (new) { | |||||
cblk->data = new; | |||||
cblk->data_allocated = new_size; | |||||
} | |||||
} | |||||
if ( bytestream2_get_bytes_left(&s->g) < cblk->lengthinc[cwsno] | if ( bytestream2_get_bytes_left(&s->g) < cblk->lengthinc[cwsno] | ||||
|| sizeof(cblk->data) < cblk->length + cblk->lengthinc[cwsno] + 4 | |||||
|| cblk->data_allocated < cblk->length + cblk->lengthinc[cwsno] + 4 | |||||
) { | ) { | ||||
av_log(s->avctx, AV_LOG_ERROR, | av_log(s->avctx, AV_LOG_ERROR, | ||||
"Block length %"PRIu16" or lengthinc %d is too large, left %d\n", | "Block length %"PRIu16" or lengthinc %d is too large, left %d\n", | ||||