CVE-2016-6911 (C, published 2017-01-26T15:59Z)
Description: value: The dynamicGetbuf function in the GD Graphics Library (aka libgd) before 2.2.4 allows remote attackers to cause a denial of service (out-of-bounds read) via a crafted TIFF image.
Severity: MEDIUM (CVSS v3 base score: 5.5)
Weakness: CWE-125 \u2014 Out-of-bounds Read
The product reads data past the end, or before the beginning, of the intended buffer.
Repository: https://github.com/libgd/libgd
Files changed:
src/gd_io_dp.c
src/gd_tiff.c
tests/tiff/.gitignore
tests/tiff/CMakeLists.txt
tests/tiff/Makemodule.am
tests/tiff/tiff_invalid_read.c
Commit message: 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 security@libgd.org.

CVE-2016-6911


Vulnerable code:
			/* 2.0.34: EOF is incorrect. We use 0 for
			 * errors and EOF, just like fileGetbuf,
			 * which is a simple fread() wrapper.
			 * TBB. Original bug report: Daniel Cowgill. */
			return 0; /* NOT EOF */
	TIFFReadRGBAImage(tif, width, height, buffer, 0);

	for(y = 0; y < height; y++) {
		for(x = 0; x < width; x++) {
			/* if it doesn't already exist, allocate a new colour,
			 * else use existing one */
			rgba = buffer[(y * width + x)];
			a = (0xff - TIFFGetA(rgba)) / 2;
			color = gdTrueColorAlpha(TIFFGetR(rgba), TIFFGetG(rgba), TIFFGetB(rgba), a);

			/* set pixel colour to this colour */
			gdImageSetPixel(im, x, height - y - 1, color);
	return GD_SUCCESS;
	tiff/CMakeLists.txt

Fixed code:
/* returns the number of bytes actually read; 0 on EOF and error */
	if (dp->pos < 0 || dp->pos >= dp->realSize) {
		return 0;
	}

			return 0;
	if (dp->pos + rlen > dp->realSize) {
		rlen = dp->realSize - dp->pos;
	}

	int success;
	success = TIFFReadRGBAImage(tif, width, height, buffer, 1);

	if (success) {
		for(y = 0; y < height; y++) {
			for(x = 0; x < width; x++) {
				/* if it doesn't already exist, allocate a new colour,
				 * else use existing one */
				rgba = buffer[(y * width + x)];
				a = (0xff - TIFFGetA(rgba)) / 2;
				color = gdTrueColorAlpha(TIFFGetR(rgba), TIFFGetG(rgba), TIFFGetB(rgba), a);
	
				/* set pixel colour to this colour */
				gdImageSetPixel(im, x, height - y - 1, color);
			}
	return success;
/tiff_invalid_read
	tiff_invalid_read
	tiff/tiff_invalid_read \
	tiff/CMakeLists.txt \
	tiff/tiff_invalid_read_1.tiff \
	tiff/tiff_invalid_read_2.tiff \
	tiff/tiff_invalid_read_3.tiff
/*
We're testing that reading corrupt TIFF files doesn't cause any memory issues,
and that the operation gracefully fails (i.e. gdImageCreateFromTiffPtr() returns
NULL).
*/

#include "gd.h"
#include "gdtest.h"


static void check_file(char *basename);
static size_t read_test_file(char **buffer, char *basename);


int main()
{
    check_file("tiff_invalid_read_1.tiff");
    check_file("tiff_invalid_read_2.tiff");
    check_file("tiff_invalid_read_3.tiff");

    return gdNumFailures();
}


static void check_file(char *basename)
{
    gdImagePtr im;
    char *buffer;
    size_t size;

    size = read_test_file(&buffer, basename);
    im = gdImageCreateFromTiffPtr(size, (void *) buffer);
    gdTestAssert(im == NULL);
    free(buffer);
}


static size_t read_test_file(char **buffer, char *basename)
{
    char *filename;
    FILE *fp;
    size_t exp_size, act_size;

    filename = gdTestFilePath2("tiff", basename);
    fp = fopen(filename, "rb");
    gdTestAssert(fp != NULL);

	fseek(fp, 0, SEEK_END);
	exp_size = ftell(fp);
	fseek(fp, 0, SEEK_SET);

    *buffer = malloc(exp_size);
    gdTestAssert(*buffer != NULL);
    act_size = fread(*buffer, sizeof(**buffer), exp_size, fp);
    gdTestAssert(act_size == exp_size);

    fclose(fp);
    free(filename);

    return act_size;
}