Skip to content

Commit 4859d69

Browse files
committed
Fix invalid read in gdImageCreateFromTiffPtr()
tiff_invalid_read.tiff is corrupt, and causes an invalid read in gdImageCreateFromTiffPtr(), but not in gdImageCreateFromTiff(). The culprit is dynamicGetbuf(), which doesn't check for out-of-bound reads. In this case, dynamicGetbuf() is called with a negative dp->pos, but also positive buffer overflows have to be handled, in which case 0 has to be returned (cf. commit 75e29a9). Fixing dynamicGetbuf() exhibits that the corrupt TIFF would still create the image, because the return value of TIFFReadRGBAImage() is not checked. We do that, and let createFromTiffRgba() fail if TIFFReadRGBAImage() fails. This issue had been reported by Ibrahim El-Sayed to [email protected]. CVE-2016-6911
1 parent fb0e0cc commit 4859d69

File tree

9 files changed

+94
-19
lines changed

9 files changed

+94
-19
lines changed

src/gd_io_dp.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ static void dynamicPutchar(struct gdIOCtx *ctx, int a)
263263
appendDynamic(dctx->dp, &b, 1);
264264
}
265265

266+
/* returns the number of bytes actually read; 0 on EOF and error */
266267
static int dynamicGetbuf(gdIOCtxPtr ctx, void *buf, int len)
267268
{
268269
int rlen, remain;
@@ -272,21 +273,25 @@ static int dynamicGetbuf(gdIOCtxPtr ctx, void *buf, int len)
272273
dctx = (dpIOCtxPtr) ctx;
273274
dp = dctx->dp;
274275

276+
if (dp->pos < 0 || dp->pos >= dp->realSize) {
277+
return 0;
278+
}
279+
275280
remain = dp->logicalSize - dp->pos;
276281
if(remain >= len) {
277282
rlen = len;
278283
} else {
279284
if(remain <= 0) {
280-
/* 2.0.34: EOF is incorrect. We use 0 for
281-
* errors and EOF, just like fileGetbuf,
282-
* which is a simple fread() wrapper.
283-
* TBB. Original bug report: Daniel Cowgill. */
284-
return 0; /* NOT EOF */
285+
return 0;
285286
}
286287

287288
rlen = remain;
288289
}
289290

291+
if (dp->pos + rlen > dp->realSize) {
292+
rlen = dp->realSize - dp->pos;
293+
}
294+
290295
memcpy(buf, (void *) ((char *)dp->data + dp->pos), rlen);
291296
dp->pos += rlen;
292297

src/gd_tiff.c

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ static int createFromTiffRgba(TIFF * tif, gdImagePtr im)
759759
int height = im->sy;
760760
uint32 *buffer;
761761
uint32 rgba;
762+
int success;
762763

763764
/* switch off colour merging on target gd image just while we write out
764765
* content - we want to preserve the alpha data until the user chooses
@@ -771,26 +772,28 @@ static int createFromTiffRgba(TIFF * tif, gdImagePtr im)
771772
return GD_FAILURE;
772773
}
773774

774-
TIFFReadRGBAImage(tif, width, height, buffer, 0);
775-
776-
for(y = 0; y < height; y++) {
777-
for(x = 0; x < width; x++) {
778-
/* if it doesn't already exist, allocate a new colour,
779-
* else use existing one */
780-
rgba = buffer[(y * width + x)];
781-
a = (0xff - TIFFGetA(rgba)) / 2;
782-
color = gdTrueColorAlpha(TIFFGetR(rgba), TIFFGetG(rgba), TIFFGetB(rgba), a);
783-
784-
/* set pixel colour to this colour */
785-
gdImageSetPixel(im, x, height - y - 1, color);
775+
success = TIFFReadRGBAImage(tif, width, height, buffer, 1);
776+
777+
if (success) {
778+
for(y = 0; y < height; y++) {
779+
for(x = 0; x < width; x++) {
780+
/* if it doesn't already exist, allocate a new colour,
781+
* else use existing one */
782+
rgba = buffer[(y * width + x)];
783+
a = (0xff - TIFFGetA(rgba)) / 2;
784+
color = gdTrueColorAlpha(TIFFGetR(rgba), TIFFGetG(rgba), TIFFGetB(rgba), a);
785+
786+
/* set pixel colour to this colour */
787+
gdImageSetPixel(im, x, height - y - 1, color);
788+
}
786789
}
787790
}
788791

789792
gdFree(buffer);
790793

791794
/* now reset colour merge for alpha blending routines */
792795
gdImageAlphaBlending(im, alphaBlendingFlag);
793-
return GD_SUCCESS;
796+
return success;
794797
}
795798

796799
/*

tests/tiff/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/tiff_dpi
22
/tiff_im2im
33
/tiff_null
4+
/tiff_invalid_read

tests/tiff/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
IF(TIFF_FOUND)
22
LIST(APPEND TESTS_FILES
33
tiff_im2im
4+
tiff_invalid_read
45
tiff_null
56
tiff_dpi
67
)

tests/tiff/Makemodule.am

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ if HAVE_LIBTIFF
22
libgd_test_programs += \
33
tiff/tiff_dpi \
44
tiff/tiff_im2im \
5+
tiff/tiff_invalid_read \
56
tiff/tiff_null
67
endif
78

89
EXTRA_DIST += \
9-
tiff/CMakeLists.txt
10+
tiff/CMakeLists.txt \
11+
tiff/tiff_invalid_read_1.tiff \
12+
tiff/tiff_invalid_read_2.tiff \
13+
tiff/tiff_invalid_read_3.tiff

tests/tiff/tiff_invalid_read.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
We're testing that reading corrupt TIFF files doesn't cause any memory issues,
3+
and that the operation gracefully fails (i.e. gdImageCreateFromTiffPtr() returns
4+
NULL).
5+
*/
6+
7+
#include "gd.h"
8+
#include "gdtest.h"
9+
10+
11+
static void check_file(char *basename);
12+
static size_t read_test_file(char **buffer, char *basename);
13+
14+
15+
int main()
16+
{
17+
check_file("tiff_invalid_read_1.tiff");
18+
check_file("tiff_invalid_read_2.tiff");
19+
check_file("tiff_invalid_read_3.tiff");
20+
21+
return gdNumFailures();
22+
}
23+
24+
25+
static void check_file(char *basename)
26+
{
27+
gdImagePtr im;
28+
char *buffer;
29+
size_t size;
30+
31+
size = read_test_file(&buffer, basename);
32+
im = gdImageCreateFromTiffPtr(size, (void *) buffer);
33+
gdTestAssert(im == NULL);
34+
free(buffer);
35+
}
36+
37+
38+
static size_t read_test_file(char **buffer, char *basename)
39+
{
40+
char *filename;
41+
FILE *fp;
42+
size_t exp_size, act_size;
43+
44+
filename = gdTestFilePath2("tiff", basename);
45+
fp = fopen(filename, "rb");
46+
gdTestAssert(fp != NULL);
47+
48+
fseek(fp, 0, SEEK_END);
49+
exp_size = ftell(fp);
50+
fseek(fp, 0, SEEK_SET);
51+
52+
*buffer = malloc(exp_size);
53+
gdTestAssert(*buffer != NULL);
54+
act_size = fread(*buffer, sizeof(**buffer), exp_size, fp);
55+
gdTestAssert(act_size == exp_size);
56+
57+
fclose(fp);
58+
free(filename);
59+
60+
return act_size;
61+
}

tests/tiff/tiff_invalid_read_1.tiff

3.23 KB
Binary file not shown.

tests/tiff/tiff_invalid_read_2.tiff

429 Bytes
Binary file not shown.

tests/tiff/tiff_invalid_read_3.tiff

428 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)