Commit message: Fixed bug #75571: Potential infinite loop in gdImageCreateFromGifCtx

Due to a signedness confusion in `GetCode_` a corrupt GIF file can
trigger an infinite loop.  Furthermore we make sure that a GIF without
any palette entries is treated as invalid *after* open palette entries
have been removed.
CVE: CVE-2018-5711
CWE: CWE-681
Vulnerability description: gd_gif_in.c in the GD Graphics Library (aka libgd), as used in PHP before 5.6.33, 7.0.x before 7.0.27, 7.1.x before 7.1.13, and 7.2.x before 7.2.1, has an integer signedness error that leads to an infinite loop via a crafted GIF file, as demonstrated by a call to the imagecreatefromgif or imagecreatefromstring PHP function. This is related to GetCode_ and gdImageCreateFromGifCtx.

Project: php-src
File: gd_gif_in.c

GetCode_(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP)
{
	int           i, j, ret;
	unsigned char count;

	if (flag) {
		scd->curbit = 0;
		scd->lastbit = 0;
		scd->last_byte = 0;
		scd->done = FALSE;
		return 0;
	}

	if ( (scd->curbit + code_size) >= scd->lastbit) {
		if (scd->done) {
			if (scd->curbit >= scd->lastbit) {
				/* Oh well */
			}
			return -1;
		}
		scd->buf[0] = scd->buf[scd->last_byte-2];
		scd->buf[1] = scd->buf[scd->last_byte-1];

               if ((count = GetDataBlock(fd, &scd->buf[2], ZeroDataBlockP)) <= 0)
			scd->done = TRUE;

		scd->last_byte = 2 + count;
		scd->curbit = (scd->curbit - scd->lastbit) + 16;
		scd->lastbit = (2+count)*8 ;
	}

	if ((scd->curbit + code_size - 1) >= (CSD_BUF_SIZE * 8)) {
		ret = -1;
	} else {
		ret = 0;
		for (i = scd->curbit, j = 0; j < code_size; ++i, ++j) {
			ret |= ((scd->buf[i / 8] & (1 << (i % 8))) != 0) << j;
		}
	}

	scd->curbit += code_size;
	return ret;
}

Project: php-src
File: gd_gif_in.c

gdImagePtr gdImageCreateFromGifCtx(gdIOCtxPtr fd) /* {{{ */
{
	int BitPixel;
#if 0
	int ColorResolution;
	int Background;
	int AspectRatio;
#endif
	int Transparent = (-1);
	unsigned char   buf[16];
	unsigned char   c;
	unsigned char   ColorMap[3][MAXCOLORMAPSIZE];
	unsigned char   localColorMap[3][MAXCOLORMAPSIZE];
	int             imw, imh, screen_width, screen_height;
	int             gif87a, useGlobalColormap;
	int             bitPixel;
	int	       i;
	/*1.4//int             imageCount = 0; */

	int ZeroDataBlock = FALSE;
	int haveGlobalColormap;
	gdImagePtr im = 0;

	memset(ColorMap, 0, 3 * MAXCOLORMAPSIZE);
	memset(localColorMap, 0, 3 * MAXCOLORMAPSIZE);

	/*1.4//imageNumber = 1; */
	if (! ReadOK(fd,buf,6)) {
		return 0;
	}
	if (strncmp((char *)buf,"GIF",3) != 0) {
		return 0;
	}

	if (memcmp((char *)buf+3, "87a", 3) == 0) {
		gif87a = 1;
	} else if (memcmp((char *)buf+3, "89a", 3) == 0) {
		gif87a = 0;
	} else {
		return 0;
	}

	if (! ReadOK(fd,buf,7)) {
		return 0;
	}

	BitPixel        = 2<<(buf[4]&0x07);
#if 0
	ColorResolution = (int) (((buf[4]&0x70)>>3)+1);
	Background      = buf[5];
	AspectRatio     = buf[6];
#endif
	screen_width = imw = LM_to_uint(buf[0],buf[1]);
	screen_height = imh = LM_to_uint(buf[2],buf[3]);

	haveGlobalColormap = BitSet(buf[4], LOCALCOLORMAP);    /* Global Colormap */
	if (haveGlobalColormap) {
		if (ReadColorMap(fd, BitPixel, ColorMap)) {
			return 0;
		}
	}

	for (;;) {
		int top, left;
		int width, height;

		if (! ReadOK(fd,&c,1)) {
			return 0;
		}
		if (c == ';') {         /* GIF terminator */
			goto terminated;
		}

		if (c == '!') {         /* Extension */
			if (! ReadOK(fd,&c,1)) {
				return 0;
			}
			DoExtension(fd, c, &Transparent, &ZeroDataBlock);
			continue;
		}

		if (c != ',') {         /* Not a valid start character */
			continue;
		}

		/*1.4//++imageCount; */

		if (! ReadOK(fd,buf,9)) {
			return 0;
		}

		useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);

		bitPixel = 1<<((buf[8]&0x07)+1);
		left = LM_to_uint(buf[0], buf[1]);
		top = LM_to_uint(buf[2], buf[3]);
		width = LM_to_uint(buf[4], buf[5]);
		height = LM_to_uint(buf[6], buf[7]);

		if (left + width > screen_width || top + height > screen_height) {
			if (VERBOSE) {
				printf("Frame is not confined to screen dimension.\n");
			}
			return 0;
		}

		if (!(im = gdImageCreate(width, height))) {
			return 0;
		}
		im->interlace = BitSet(buf[8], INTERLACE);
		if (!useGlobalColormap) {
			if (ReadColorMap(fd, bitPixel, localColorMap)) { 
				gdImageDestroy(im);
				return 0;
			}
			ReadImage(im, fd, width, height, localColorMap, 
					BitSet(buf[8], INTERLACE), &ZeroDataBlock);
		} else {
			if (!haveGlobalColormap) {
				gdImageDestroy(im);
				return 0;
			}
			ReadImage(im, fd, width, height,
						ColorMap, 
						BitSet(buf[8], INTERLACE), &ZeroDataBlock);
		}
		if (Transparent != (-1)) {
			gdImageColorTransparent(im, Transparent);
		}
		goto terminated;
	}

terminated:
	/* Terminator before any image was declared! */
	if (!im) {
		return 0;
	}
	if (!im->colorsTotal) {
		gdImageDestroy(im);
		return 0;
	}
	/* Check for open colors at the end, so
	   we can reduce colorsTotal and ultimately
	   BitsPerPixel */
	for (i=((im->colorsTotal-1)); (i>=0); i--) {
		if (im->open[i]) {
			im->colorsTotal--;
		} else {
			break;
		}
	}
	return im;
}