Skip to content

Commit ff60894

Browse files
authored
Merge pull request #4505 from hugovk/jpeg2k_overflow
Fix bounds overflow in JPEG 2000 decoding
2 parents 2ef59fd + c5e9de1 commit ff60894

File tree

4 files changed

+69
-17
lines changed

4 files changed

+69
-17
lines changed

Tests/check_jp2_overflow.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env python
2+
3+
# Reproductions/tests for OOB read errors in FliDecode.c
4+
5+
# When run in python, all of these images should fail for
6+
# one reason or another, either as a buffer overrun,
7+
# unrecognized datastream, or truncated image file.
8+
# There shouldn't be any segfaults.
9+
#
10+
# if run like
11+
# `valgrind --tool=memcheck python check_jp2_overflow.py 2>&1 | grep Decode.c`
12+
# the output should be empty. There may be python issues
13+
# in the valgrind especially if run in a debug python
14+
# version.
15+
16+
17+
from PIL import Image
18+
19+
repro = ("00r0_gray_l.jp2", "00r1_graya_la.jp2")
20+
21+
for path in repro:
22+
im = Image.open(path)
23+
try:
24+
im.load()
25+
except Exception as msg:
26+
print(msg)

Tests/images/00r0_gray_l.jp2

614 Bytes
Binary file not shown.

Tests/images/00r1_graya_la.jp2

335 Bytes
Binary file not shown.

src/libImaging/Jpeg2KDecode.c

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ j2ku_gray_l(opj_image_t *in, const JPEG2KTILEINFO *tileinfo,
110110
if (shift < 0)
111111
offset += 1 << (-shift - 1);
112112

113+
/* csiz*h*w + offset = tileinfo.datasize */
113114
switch (csiz) {
114115
case 1:
115116
for (y = 0; y < h; ++y) {
@@ -557,8 +558,10 @@ j2k_decode_entry(Imaging im, ImagingCodecState state)
557558
opj_dparameters_t params;
558559
OPJ_COLOR_SPACE color_space;
559560
j2k_unpacker_t unpack = NULL;
560-
size_t buffer_size = 0;
561-
unsigned n;
561+
size_t buffer_size = 0, tile_bytes = 0;
562+
unsigned n, tile_height, tile_width;
563+
int components;
564+
562565

563566
stream = opj_stream_create(BUFFER_SIZE, OPJ_TRUE);
564567

@@ -703,8 +706,44 @@ j2k_decode_entry(Imaging im, ImagingCodecState state)
703706
tile_info.x1 = (tile_info.x1 + correction) >> context->reduce;
704707
tile_info.y1 = (tile_info.y1 + correction) >> context->reduce;
705708

709+
/* Check the tile bounds; if the tile is outside the image area,
710+
or if it has a negative width or height (i.e. the coordinates are
711+
swapped), bail. */
712+
if (tile_info.x0 >= tile_info.x1
713+
|| tile_info.y0 >= tile_info.y1
714+
|| tile_info.x0 < image->x0
715+
|| tile_info.y0 < image->y0
716+
|| tile_info.x1 - image->x0 > im->xsize
717+
|| tile_info.y1 - image->y0 > im->ysize) {
718+
state->errcode = IMAGING_CODEC_BROKEN;
719+
state->state = J2K_STATE_FAILED;
720+
goto quick_exit;
721+
}
722+
723+
/* Sometimes the tile_info.datasize we get back from openjpeg
724+
is less than numcomps*w*h, and we overflow in the
725+
shuffle stage */
726+
727+
tile_width = tile_info.x1 - tile_info.x0;
728+
tile_height = tile_info.y1 - tile_info.y0;
729+
components = tile_info.nb_comps == 3 ? 4 : tile_info.nb_comps;
730+
if (( tile_width > UINT_MAX / components ) ||
731+
( tile_height > UINT_MAX / components ) ||
732+
( tile_width > UINT_MAX / (tile_height * components )) ||
733+
( tile_height > UINT_MAX / (tile_width * components ))) {
734+
state->errcode = IMAGING_CODEC_BROKEN;
735+
state->state = J2K_STATE_FAILED;
736+
goto quick_exit;
737+
}
738+
739+
tile_bytes = tile_width * tile_height * components;
740+
741+
if (tile_bytes > tile_info.data_size) {
742+
tile_info.data_size = tile_bytes;
743+
}
744+
706745
if (buffer_size < tile_info.data_size) {
707-
/* malloc check ok, tile_info.data_size from openjpeg */
746+
/* malloc check ok, overflow and tile size sanity check above */
708747
UINT8 *new = realloc (state->buffer, tile_info.data_size);
709748
if (!new) {
710749
state->errcode = IMAGING_CODEC_MEMORY;
@@ -715,6 +754,7 @@ j2k_decode_entry(Imaging im, ImagingCodecState state)
715754
buffer_size = tile_info.data_size;
716755
}
717756

757+
718758
if (!opj_decode_tile_data(codec,
719759
tile_info.tile_index,
720760
(OPJ_BYTE *)state->buffer,
@@ -725,20 +765,6 @@ j2k_decode_entry(Imaging im, ImagingCodecState state)
725765
goto quick_exit;
726766
}
727767

728-
/* Check the tile bounds; if the tile is outside the image area,
729-
or if it has a negative width or height (i.e. the coordinates are
730-
swapped), bail. */
731-
if (tile_info.x0 >= tile_info.x1
732-
|| tile_info.y0 >= tile_info.y1
733-
|| tile_info.x0 < image->x0
734-
|| tile_info.y0 < image->y0
735-
|| tile_info.x1 - image->x0 > im->xsize
736-
|| tile_info.y1 - image->y0 > im->ysize) {
737-
state->errcode = IMAGING_CODEC_BROKEN;
738-
state->state = J2K_STATE_FAILED;
739-
goto quick_exit;
740-
}
741-
742768
unpack(image, &tile_info, state->buffer, im);
743769
}
744770

0 commit comments

Comments
 (0)