Merge pull request #18 from ActiveState/BE-139-cve-2021-28676

BE-139-cve-2021-28676
This commit is contained in:
Marc Gutman 2023-04-20 15:25:04 -05:00 committed by GitHub
commit fefc469fda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 242 additions and 181 deletions

View File

@ -27,6 +27,10 @@ Changelog (Pillow)
combination of \r and \n as line endings. combination of \r and \n as line endings.
[rickprice] [rickprice]
- Fix CVE-2021-28676: FliDecode did not properly check that the block advance
was non-zero, potentally leading to an infinite loop on load.
[rickprice]
6.2.2.4 (2023-03-29) 6.2.2.4 (2023-03-29)
------------------ ------------------

View File

@ -46,7 +46,8 @@ class TestFileFli(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file) self.assertRaises(
SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
def test_n_frames(self): def test_n_frames(self):
im = Image.open(static_test_file) im = Image.open(static_test_file)
@ -96,3 +97,13 @@ class TestFileFli(PillowTestCase):
expected = Image.open("Tests/images/a_fli.png") expected = Image.open("Tests/images/a_fli.png")
self.assert_image_equal(im, expected) self.assert_image_equal(im, expected)
def test_timeouts(self):
for test_file in [
"Tests/images/timeout-9139147ce93e20eb14088fe238e541443ffd64b3.fli",
"Tests/images/timeout-bff0a9dc7243a8e6ede2408d2ffa6a9964698b87.fli",
]:
with open(test_file, "rb") as f:
with Image.open(f) as im:
with self.assertRaises(OSError):
im.load()

View File

@ -25,6 +25,9 @@ This release addresses several critical CVEs.
:cve:`CVE-2020-10994`: In libImaging/Jpeg2KDecode.c in Pillow before 7.1.0, there are multiple out-of-bounds reads via a crafted JP2 file. :cve:`CVE-2020-10994`: In libImaging/Jpeg2KDecode.c in Pillow before 7.1.0, there are multiple out-of-bounds reads via a crafted JP2 file.
:cve:`CVE-2021-28676``: FliDecode did not properly check that the block advance was non-zero,
potentally leading to an infinite loop on load.
:cve:`CVE-2021-28677`: An issue was discovered in Pillow before 8.2.0. For EPS :cve:`CVE-2021-28677`: An issue was discovered in Pillow before 8.2.0. For EPS
data, the readline implementation used in EPSImageFile data, the readline implementation used in EPSImageFile
has to deal with any combination of \r and \n as line has to deal with any combination of \r and \n as line

View File

@ -14,21 +14,22 @@
* See the README file for information on usage and redistribution. * See the README file for information on usage and redistribution.
*/ */
#include "Imaging.h" #include "Imaging.h"
#define I16(ptr) ((ptr)[0] + ((ptr)[1] << 8))
#define I16(ptr)\ #define I32(ptr) \
((ptr)[0] + ((ptr)[1] << 8))
#define I32(ptr)\
((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24)) ((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24))
#define ERR_IF_DATA_OOB(offset) \
if ((data + (offset)) > ptr + bytes) { \
state->errcode = IMAGING_CODEC_OVERRUN; \
return -1; \
}
int int ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8 *buf,
ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t bytes) Py_ssize_t bytes) {
{ UINT8 *ptr;
UINT8* ptr;
int framesize; int framesize;
int c, chunks, advance; int c, chunks, advance;
int l, lines; int l, lines;
@ -55,34 +56,39 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
state->errcode = IMAGING_CODEC_OVERRUN; state->errcode = IMAGING_CODEC_OVERRUN;
return -1; return -1;
} }
if (I16(ptr+4) != 0xF1FA) { if (I16(ptr + 4) != 0xF1FA) {
state->errcode = IMAGING_CODEC_UNKNOWN; state->errcode = IMAGING_CODEC_UNKNOWN;
return -1; return -1;
} }
chunks = I16(ptr+6); chunks = I16(ptr + 6);
ptr += 16; ptr += 16;
bytes -= 16; bytes -= 16;
/* Process subchunks */ /* Process subchunks */
for (c = 0; c < chunks; c++) { for (c = 0; c < chunks; c++) {
UINT8* data; UINT8 *data;
if (bytes < 10) { if (bytes < 10) {
state->errcode = IMAGING_CODEC_OVERRUN; state->errcode = IMAGING_CODEC_OVERRUN;
return -1; return -1;
} }
data = ptr + 6; data = ptr + 6;
switch (I16(ptr+4)) { switch (I16(ptr + 4)) {
case 4: case 11: case 4:
case 11:
/* FLI COLOR chunk */ /* FLI COLOR chunk */
break; /* ignored; handled by Python code */ break; /* ignored; handled by Python code */
case 7: case 7:
/* FLI SS2 chunk (word delta) */ /* FLI SS2 chunk (word delta) */
lines = I16(data); data += 2; /* OOB ok, we've got 4 bytes min on entry */
lines = I16(data);
data += 2;
for (l = y = 0; l < lines && y < state->ysize; l++, y++) { for (l = y = 0; l < lines && y < state->ysize; l++, y++) {
UINT8* buf = (UINT8*) im->image[y]; UINT8 *local_buf = (UINT8 *)im->image[y];
int p, packets; int p, packets;
packets = I16(data); data += 2; ERR_IF_DATA_OOB(2)
packets = I16(data);
data += 2;
while (packets & 0x8000) { while (packets & 0x8000) {
/* flag word */ /* flag word */
if (packets & 0x4000) { if (packets & 0x4000) {
@ -91,36 +97,44 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
state->errcode = IMAGING_CODEC_OVERRUN; state->errcode = IMAGING_CODEC_OVERRUN;
return -1; return -1;
} }
buf = (UINT8*) im->image[y]; local_buf = (UINT8 *)im->image[y];
} else { } else {
/* store last byte (used if line width is odd) */ /* store last byte (used if line width is odd) */
buf[state->xsize-1] = (UINT8) packets; local_buf[state->xsize - 1] = (UINT8)packets;
} }
packets = I16(data); data += 2; ERR_IF_DATA_OOB(2)
packets = I16(data);
data += 2;
} }
for (p = x = 0; p < packets; p++) { for (p = x = 0; p < packets; p++) {
ERR_IF_DATA_OOB(2)
x += data[0]; /* pixel skip */ x += data[0]; /* pixel skip */
if (data[1] >= 128) { if (data[1] >= 128) {
i = 256-data[1]; /* run */ ERR_IF_DATA_OOB(4)
if (x + i + i > state->xsize) i = 256 - data[1]; /* run */
if (x + i + i > state->xsize) {
break; break;
}
for (j = 0; j < i; j++) { for (j = 0; j < i; j++) {
buf[x++] = data[2]; local_buf[x++] = data[2];
buf[x++] = data[3]; local_buf[x++] = data[3];
} }
data += 2 + 2; data += 2 + 2;
} else { } else {
i = 2 * (int) data[1]; /* chunk */ i = 2 * (int)data[1]; /* chunk */
if (x + i > state->xsize) if (x + i > state->xsize) {
break; break;
memcpy(buf + x, data + 2, i); }
ERR_IF_DATA_OOB(2 + i)
memcpy(local_buf + x, data + 2, i);
data += 2 + i; data += 2 + i;
x += i; x += i;
} }
} }
if (p < packets) if (p < packets) {
break; /* didn't process all packets */ break; /* didn't process all packets */
} }
}
if (l < lines) { if (l < lines) {
/* didn't process all lines */ /* didn't process all lines */
state->errcode = IMAGING_CODEC_OVERRUN; state->errcode = IMAGING_CODEC_OVERRUN;
@ -129,29 +143,39 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
break; break;
case 12: case 12:
/* FLI LC chunk (byte delta) */ /* FLI LC chunk (byte delta) */
y = I16(data); ymax = y + I16(data+2); data += 4; /* OOB Check ok, we have 4 bytes min here */
y = I16(data);
ymax = y + I16(data + 2);
data += 4;
for (; y < ymax && y < state->ysize; y++) { for (; y < ymax && y < state->ysize; y++) {
UINT8* out = (UINT8*) im->image[y]; UINT8 *out = (UINT8 *)im->image[y];
ERR_IF_DATA_OOB(1)
int p, packets = *data++; int p, packets = *data++;
for (p = x = 0; p < packets; p++, x += i) { for (p = x = 0; p < packets; p++, x += i) {
ERR_IF_DATA_OOB(2)
x += data[0]; /* skip pixels */ x += data[0]; /* skip pixels */
if (data[1] & 0x80) { if (data[1] & 0x80) {
i = 256-data[1]; /* run */ i = 256 - data[1]; /* run */
if (x + i > state->xsize) if (x + i > state->xsize) {
break; break;
}
ERR_IF_DATA_OOB(3)
memset(out + x, data[2], i); memset(out + x, data[2], i);
data += 3; data += 3;
} else { } else {
i = data[1]; /* chunk */ i = data[1]; /* chunk */
if (x + i > state->xsize) if (x + i > state->xsize) {
break; break;
}
ERR_IF_DATA_OOB(2 + i)
memcpy(out + x, data + 2, i); memcpy(out + x, data + 2, i);
data += i + 2; data += i + 2;
} }
} }
if (p < packets) if (p < packets) {
break; /* didn't process all packets */ break; /* didn't process all packets */
} }
}
if (y < ymax) { if (y < ymax) {
/* didn't process all lines */ /* didn't process all lines */
state->errcode = IMAGING_CODEC_OVERRUN; state->errcode = IMAGING_CODEC_OVERRUN;
@ -160,25 +184,31 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
break; break;
case 13: case 13:
/* FLI BLACK chunk */ /* FLI BLACK chunk */
for (y = 0; y < state->ysize; y++) for (y = 0; y < state->ysize; y++) {
memset(im->image[y], 0, state->xsize); memset(im->image[y], 0, state->xsize);
}
break; break;
case 15: case 15:
/* FLI BRUN chunk */ /* FLI BRUN chunk */
/* OOB, ok, we've got 4 bytes min on entry */
for (y = 0; y < state->ysize; y++) { for (y = 0; y < state->ysize; y++) {
UINT8* out = (UINT8*) im->image[y]; UINT8 *out = (UINT8 *)im->image[y];
data += 1; /* ignore packetcount byte */ data += 1; /* ignore packetcount byte */
for (x = 0; x < state->xsize; x += i) { for (x = 0; x < state->xsize; x += i) {
ERR_IF_DATA_OOB(2)
if (data[0] & 0x80) { if (data[0] & 0x80) {
i = 256 - data[0]; i = 256 - data[0];
if (x + i > state->xsize) if (x + i > state->xsize) {
break; /* safety first */ break; /* safety first */
}
ERR_IF_DATA_OOB(i + 1)
memcpy(out + x, data + 1, i); memcpy(out + x, data + 1, i);
data += i + 1; data += i + 1;
} else { } else {
i = data[0]; i = data[0];
if (x + i > state->xsize) if (x + i > state->xsize) {
break; /* safety first */ break; /* safety first */
}
memset(out + x, data[1], i); memset(out + x, data[1], i);
data += 2; data += 2;
} }
@ -192,9 +222,13 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
break; break;
case 16: case 16:
/* COPY chunk */ /* COPY chunk */
if (state->xsize > bytes / state->ysize) {
/* not enough data for frame */
return ptr - buf; /* bytes consumed */
}
for (y = 0; y < state->ysize; y++) { for (y = 0; y < state->ysize; y++) {
UINT8* buf = (UINT8*) im->image[y]; UINT8 *local_buf = (UINT8 *)im->image[y];
memcpy(buf, data, state->xsize); memcpy(local_buf, data, state->xsize);
data += state->xsize; data += state->xsize;
} }
break; break;
@ -208,6 +242,15 @@ ImagingFliDecode(Imaging im, ImagingCodecState state, UINT8* buf, Py_ssize_t byt
return -1; return -1;
} }
advance = I32(ptr); advance = I32(ptr);
if (advance == 0) {
// If there's no advance, we're in in infinite loop
state->errcode = IMAGING_CODEC_BROKEN;
return -1;
}
if (advance < 0 || advance > bytes) {
state->errcode = IMAGING_CODEC_OVERRUN;
return -1;
}
ptr += advance; ptr += advance;
bytes -= advance; bytes -= advance;
} }