CVE-2016-10167: Improper Input Validation (CWE-20)
value: The gdImageCreateFromGd2Ctx function in gd_gd2.c in the GD Graphics Library (aka libgd) before 2.2.4 allows remote attackers to cause a denial of service (application crash) via a crafted image file.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
CVSS v3 base score: 5.5
Published: 2017-03-15T15:59Z
Repository: https://github.com/libgd/libgd
Language: C
Files changed: src/gd_gd2.c, tests/gd2/.gitignore, tests/gd2/CMakeLists.txt, tests/gd2/Makemodule.am, tests/gd2/too_few_image_data.c
Commit date: 2016-12-13 15:33:42 +0100
Commit message: Fix DOS vulnerability in gdImageCreateFromGd2Ctx()

We must not pretend that there are image data if there are none. Instead
we fail reading the image file gracefully.

Diff: diff --git a/src/gd_gd2.c b/src/gd_gd2.c
index 3c716826..d07828c7 100644
--- a/src/gd_gd2.c
+++ b/src/gd_gd2.c
@@ -501,22 +501,20 @@ BGD_DECLARE(gdImagePtr) gdImageCreateFromGd2Ctx (gdIOCtxPtr in)
 				if (!gd2_compressed (fmt)) {
 					for (x = xlo; x < xhi; x++) {
 
 						if (im->trueColor) {
 							if (!gdGetInt (&im->tpixels[y][x], in)) {
-								/*printf("EOF while reading\n"); */
-								/*gdImageDestroy(im); */
-								/*return 0; */
-								im->tpixels[y][x] = 0;
+								gd_error("gd2: EOF while reading\n");
+								gdImageDestroy(im);
+								return NULL;
 							}
 						} else {
 							int ch;
 							if (!gdGetByte (&ch, in)) {
-								/*printf("EOF while reading\n"); */
-								/*gdImageDestroy(im); */
-								/*return 0; */
-								ch = 0;
+								gd_error("gd2: EOF while reading\n");
+								gdImageDestroy(im);
+								return NULL;
 							}
 							im->pixels[y][x] = ch;
 						}
 					}
 				} else {
diff --git a/tests/gd2/.gitignore b/tests/gd2/.gitignore
index 136a2d50..39d8bf4e 100644
--- a/tests/gd2/.gitignore
+++ b/tests/gd2/.gitignore
@@ -4,5 +4,6 @@
 /gd2_im2im
 /gd2_null
 /gd2_read
 /gd2_read_corrupt
 /php_bug_72339
+/too_few_image_data
diff --git a/tests/gd2/CMakeLists.txt b/tests/gd2/CMakeLists.txt
index 8fcc9065..5a61d113 100644
--- a/tests/gd2/CMakeLists.txt
+++ b/tests/gd2/CMakeLists.txt
@@ -5,8 +5,9 @@ LIST(APPEND TESTS_FILES
 	gd2_im2im
 	gd2_null
 	php_bug_72339
 	gd2_read
 	gd2_read_corrupt
+	too_few_image_data
 )
 
 ADD_GD_TESTS()
diff --git a/tests/gd2/Makemodule.am b/tests/gd2/Makemodule.am
index d69aee02..c1fbf440 100644
--- a/tests/gd2/Makemodule.am
+++ b/tests/gd2/Makemodule.am
@@ -1,11 +1,12 @@
 libgd_test_programs += \
 	gd2/bug_289 \
 	gd2/bug00309 \
 	gd2/gd2_empty_file \
 	gd2/php_bug_72339 \
-	gd2/gd2_read_corrupt
+	gd2/gd2_read_corrupt \
+	gd2/too_few_image_data
 
 if HAVE_LIBZ
 libgd_test_programs += \
 	gd2/gd2_null
 
@@ -21,6 +22,8 @@ EXTRA_DIST += \
 	gd2/conv_gd2_exp.gd2 \
 	gd2/conv_test.gd2 \
 	gd2/conv_test_exp.png \
 	gd2/empty.gd2 \
 	gd2/invalid_header.gd2 \
-	gd2/invalid_neg_size.gd2
+	gd2/invalid_neg_size.gd2 \
+	gd2/php_bug_72339_exp.gd2 \
+	gd2/too_few_image_data.gd2
diff --git a/tests/gd2/too_few_image_data.c b/tests/gd2/too_few_image_data.c
new file mode 100644
index 00000000..3153a080
--- /dev/null
+++ b/tests/gd2/too_few_image_data.c
@@ -0,0 +1,22 @@
+/*
+too_few_image_data.gd2 claims to have a size of 12336x48 pixels, but doesn't
+provide as much image data. We test that gdImageCreateFromGd2Ctx() returns NULL
+in this case.
+*/
+
+#include "gd.h"
+#include "gdtest.h"
+
+int main()
+{
+    gdImagePtr im;
+    FILE *fp;
+
+    fp = gdTestFileOpen2("gd2", "too_few_image_data.gd2");
+    gdTestAssert(fp != NULL);
+    im = gdImageCreateFromGd2(fp);
+    gdTestAssert(im == NULL);
+    fclose(fp);
+
+    return gdNumFailures();
+}
diff --git a/tests/gd2/too_few_image_data.gd2 b/tests/gd2/too_few_image_data.gd2
new file mode 100644
index 00000000..1c797d1a
Binary files /dev/null and b/tests/gd2/too_few_image_data.gd2 differ

Vulnerable code: 								/*printf("EOF while reading\n"); */
								/*gdImageDestroy(im); */
								/*return 0; */
								im->tpixels[y][x] = 0;
								/*printf("EOF while reading\n"); */
								/*gdImageDestroy(im); */
								/*return 0; */
								ch = 0;
	gd2/gd2_read_corrupt
	gd2/invalid_neg_size.gd2
Fixed code: 								gd_error("gd2: EOF while reading\n");
								gdImageDestroy(im);
								return NULL;
								gd_error("gd2: EOF while reading\n");
								gdImageDestroy(im);
								return NULL;
/too_few_image_data
	too_few_image_data
	gd2/gd2_read_corrupt \
	gd2/too_few_image_data
	gd2/invalid_neg_size.gd2 \
	gd2/php_bug_72339_exp.gd2 \
	gd2/too_few_image_data.gd2
/*
too_few_image_data.gd2 claims to have a size of 12336x48 pixels, but doesn't
provide as much image data. We test that gdImageCreateFromGd2Ctx() returns NULL
in this case.
*/

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

int main()
{
    gdImagePtr im;
    FILE *fp;

    fp = gdTestFileOpen2("gd2", "too_few_image_data.gd2");
    gdTestAssert(fp != NULL);
    im = gdImageCreateFromGd2(fp);
    gdTestAssert(im == NULL);
    fclose(fp);

    return gdNumFailures();
}