Skip to content

Commit f891baa

Browse files
wiredfoolradarhere
authored andcommitted
Fix OOB read in SgiRleDecode.c
* From Pillow 4.3.0->8.1.0 * CVE-2021-25293
1 parent cbfdde7 commit f891baa

9 files changed

+81
-14
lines changed
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Tests/test_sgi_crash.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
"Tests/images/sgi_crash.bin",
1212
"Tests/images/crash-6b7f2244da6d0ae297ee0754a424213444e92778.sgi",
1313
"Tests/images/ossfuzz-5730089102868480.sgi",
14+
"Tests/images/crash-754d9c7ec485ffb76a90eeaab191ef69a2a3a3cd.sgi",
15+
"Tests/images/crash-465703f71a0f0094873a3e0e82c9f798161171b8.sgi",
16+
"Tests/images/crash-64834657ee604b8797bf99eac6a194c124a9a8ba.sgi",
17+
"Tests/images/crash-abcf1c97b8fe42a6c68f1fb0b978530c98d57ced.sgi",
18+
"Tests/images/crash-b82e64d4f3f76d7465b6af535283029eda211259.sgi",
19+
"Tests/images/crash-c1b2595b8b0b92cc5f38b6635e98e3a119ade807.sgi",
20+
"Tests/images/crash-db8bfa78b19721225425530c5946217720d7df4e.sgi",
1421
],
1522
)
1623
def test_crashes(test_file):

src/libImaging/SgiRleDecode.c

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,59 @@ static void read4B(UINT32* dest, UINT8* buf)
2525
*dest = (UINT32)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
2626
}
2727

28-
static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
28+
/*
29+
SgiRleDecoding is done in a single channel row oriented set of RLE chunks.
30+
31+
* The file is arranged as
32+
- SGI Header
33+
- Rle Offset Table
34+
- Rle Length Table
35+
- Scanline Data
36+
37+
* Each RLE atom is c->bpc bytes wide (1 or 2)
38+
39+
* Each RLE Chunk is [specifier atom] [ 1 or n data atoms ]
40+
41+
* Copy Atoms are a byte with the high bit set, and the low 7 are
42+
the number of bytes to copy from the source to the
43+
destination. e.g.
44+
45+
CBBBBBBBB or 0CHLHLHLHLHLHL (B=byte, H/L = Hi low bytes)
46+
47+
* Run atoms do not have the high bit set, and the low 7 bits are
48+
the number of copies of the next atom to copy to the
49+
destination. e.g.:
50+
51+
RB -> BBBBB or RHL -> HLHLHLHLHL
52+
53+
The upshot of this is, there is no way to determine the required
54+
length of the input buffer from reloffset and rlelength without
55+
going through the data at that scan line.
56+
57+
Furthermore, there's no requirement that individual scan lines
58+
pointed to from the rleoffset table are in any sort of order or
59+
used only once, or even disjoint. There's also no requirement that
60+
all of the data in the scan line area of the image file be used
61+
62+
*/
63+
static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize, UINT8* end_of_buffer)
2964
{
65+
/*
66+
* n here is the number of rlechunks
67+
* z is the number of channels, for calculating the interleave
68+
* offset to go to RGBA style pixels
69+
* xsize is the row width
70+
* end_of_buffer is the address of the end of the input buffer
71+
*/
72+
3073
UINT8 pixel, count;
3174
int x = 0;
3275

3376
for (;n > 0; n--)
3477
{
78+
if (src > end_of_buffer) {
79+
return -1;
80+
}
3581
pixel = *src++;
3682
if (n == 1 && pixel != 0) {
3783
return n;
@@ -45,13 +91,19 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
4591
}
4692
x += count;
4793
if (pixel & RLE_COPY_FLAG) {
94+
if (src + count > end_of_buffer) {
95+
return -1;
96+
}
4897
while(count--) {
4998
*dest = *src++;
5099
dest += z;
51100
}
52101

53102
}
54103
else {
104+
if (src > end_of_buffer) {
105+
return -1;
106+
}
55107
pixel = *src++;
56108
while (count--) {
57109
*dest = pixel;
@@ -63,14 +115,16 @@ static int expandrow(UINT8* dest, UINT8* src, int n, int z, int xsize)
63115
return 0;
64116
}
65117

66-
static int expandrow2(UINT8* dest, const UINT8* src, int n, int z, int xsize)
118+
static int expandrow2(UINT8* dest, const UINT8* src, int n, int z, int xsize, UINT8* end_of_buffer)
67119
{
68120
UINT8 pixel, count;
69-
70121
int x = 0;
71122

72123
for (;n > 0; n--)
73124
{
125+
if (src + 1 > end_of_buffer) {
126+
return -1;
127+
}
74128
pixel = src[1];
75129
src+=2;
76130
if (n == 1 && pixel != 0) {
@@ -85,13 +139,19 @@ static int expandrow2(UINT8* dest, const UINT8* src, int n, int z, int xsize)
85139
}
86140
x += count;
87141
if (pixel & RLE_COPY_FLAG) {
142+
if (src + 2 * count > end_of_buffer) {
143+
return -1;
144+
}
88145
while(count--) {
89146
memcpy(dest, src, 2);
90147
src += 2;
91148
dest += z * 2;
92149
}
93150
}
94151
else {
152+
if (src + 2 > end_of_buffer) {
153+
return -1;
154+
}
95155
while (count--) {
96156
memcpy(dest, src, 2);
97157
dest += z * 2;
@@ -141,7 +201,10 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
141201
return -1;
142202
}
143203
_imaging_seek_pyFd(state->fd, SGI_HEADER_SIZE, SEEK_SET);
144-
_imaging_read_pyFd(state->fd, (char*)ptr, c->bufsize);
204+
if (_imaging_read_pyFd(state->fd, (char*)ptr, c->bufsize) != c->bufsize) {
205+
state->errcode = IMAGING_CODEC_UNKNOWN;
206+
return -1;
207+
}
145208

146209

147210
/* decoder initialization */
@@ -175,28 +238,28 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
175238
read4B(&c->lengthtab[c->tabindex], &ptr[c->bufindex]);
176239
}
177240

178-
state->count += c->tablen * sizeof(UINT32) * 2;
179-
180241
/* read compressed rows */
181242
for (c->rowno = 0; c->rowno < im->ysize; c->rowno++, state->y += state->ystep)
182243
{
183244
for (c->channo = 0; c->channo < im->bands; c->channo++)
184245
{
185246
c->rleoffset = c->starttab[c->rowno + c->channo * im->ysize];
186247
c->rlelength = c->lengthtab[c->rowno + c->channo * im->ysize];
187-
c->rleoffset -= SGI_HEADER_SIZE;
188248

189-
if (c->rleoffset + c->rlelength > c->bufsize) {
249+
// Check for underflow of rleoffset-SGI_HEADER_SIZE
250+
if (c->rleoffset < SGI_HEADER_SIZE) {
190251
state->errcode = IMAGING_CODEC_OVERRUN;
191252
goto sgi_finish_decode;
192253
}
193254

255+
c->rleoffset -= SGI_HEADER_SIZE;
256+
194257
/* row decompression */
195258
if (c->bpc ==1) {
196-
status = expandrow(&state->buffer[c->channo], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize);
259+
status = expandrow(&state->buffer[c->channo], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize, &ptr[c->bufsize-1]);
197260
}
198261
else {
199-
status = expandrow2(&state->buffer[c->channo * 2], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize);
262+
status = expandrow2(&state->buffer[c->channo * 2], &ptr[c->rleoffset], c->rlelength, im->bands, im->xsize, &ptr[c->bufsize-1]);
200263
}
201264
if (status == -1) {
202265
state->errcode = IMAGING_CODEC_OVERRUN;
@@ -205,16 +268,13 @@ ImagingSgiRleDecode(Imaging im, ImagingCodecState state,
205268
goto sgi_finish_decode;
206269
}
207270

208-
state->count += c->rlelength;
209271
}
210272

211273
/* store decompressed data in image */
212274
state->shuffle((UINT8*)im->image[state->y], state->buffer, im->xsize);
213275

214276
}
215277

216-
c->bufsize++;
217-
218278
sgi_finish_decode: ;
219279

220280
free(c->starttab);
@@ -224,5 +284,5 @@ sgi_finish_decode: ;
224284
state->errcode=err;
225285
return -1;
226286
}
227-
return state->count - c->bufsize;
287+
return 0;
228288
}

0 commit comments

Comments
 (0)