CVE-2016-5116: Improper Restriction of Operations within the Bounds of a Memory Buffer (CWE-119)
value: gd_xbm.c in the GD Graphics Library (aka libgd) before 2.2.0, as used in certain custom PHP 5.5.x configurations, allows context-dependent attackers to obtain sensitive information from process memory or cause a denial of service (stack-based buffer under-read and application crash) via a long name.
CWE description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
CVSS v3 base score: 9.1
Published: 2016-08-07T10:59Z
Repository: https://github.com/libgd/libgd
Language: C
Files changed: src/gd_xbm.c
Commit date: 2016-05-14 01:38:18 -0400
Commit message: xbm: avoid stack overflow (read) with large names #211

We use the name passed in to printf into a local stack buffer which is
limited to 4000 bytes.  So given a large enough value, lots of stack
data is leaked.  Rewrite the code to do simple memory copies with most
of the strings to avoid that issue, and only use stack buffer for small
numbers of constant size.

This closes #211.

Diff: diff --git a/src/gd_xbm.c b/src/gd_xbm.c
index 74d839b2..d28fdfc5 100644
--- a/src/gd_xbm.c
+++ b/src/gd_xbm.c
@@ -178,21 +178,24 @@ BGD_DECLARE(gdImagePtr) gdImageCreateFromXbm(FILE * fd)
 
 
 /* {{{ gdCtxPrintf */
 static void gdCtxPrintf(gdIOCtx * out, const char *format, ...)
 {
-	char buf[4096];
+	char buf[1024];
 	int len;
 	va_list args;
 
 	va_start(args, format);
 	len = vsnprintf(buf, sizeof(buf)-1, format, args);
 	va_end(args);
 	out->putBuf(out, buf, len);
 }
 /* }}} */
 
+/* The compiler will optimize strlen(constant) to a constant number. */
+#define gdCtxPuts(out, s) out->putBuf(out, s, strlen(s))
+
 /* {{{ gdImageXbmCtx */
 BGD_DECLARE(void) gdImageXbmCtx(gdImagePtr image, char* file_name, int fg, gdIOCtx * out)
 {
 	int x, y, c, b, sx, sy, p;
 	char *name, *f;
@@ -213,13 +216,30 @@ BGD_DECLARE(void) gdImageXbmCtx(gdImagePtr image, char* file_name, int fg, gdIOC
 				name[i] = '_';
 			}
 		}
 	}
 
-	gdCtxPrintf(out, "#define %s_width %d\n", name, gdImageSX(image));
-	gdCtxPrintf(out, "#define %s_height %d\n", name, gdImageSY(image));
-	gdCtxPrintf(out, "static unsigned char %s_bits[] = {\n  ", name);
+	/* Since "name" comes from the user, run it through a direct puts.
+	 * Trying to printf it into a local buffer means we'd need a large
+	 * or dynamic buffer to hold it all. */
+
+	/* #define <name>_width 1234 */
+	gdCtxPuts(out, "#define ");
+	gdCtxPuts(out, name);
+	gdCtxPuts(out, "_width ");
+	gdCtxPrintf(out, "%d\n", gdImageSX(image));
+
+	/* #define <name>_height 1234 */
+	gdCtxPuts(out, "#define ");
+	gdCtxPuts(out, name);
+	gdCtxPuts(out, "_height ");
+	gdCtxPrintf(out, "%d\n", gdImageSY(image));
+
+	/* static unsigned char <name>_bits[] = {\n */
+	gdCtxPuts(out, "static unsigned char ");
+	gdCtxPuts(out, name);
+	gdCtxPuts(out, "_bits[] = {\n  ");
 
 	free(name);
 
 	b = 1;
 	p = 0;
@@ -232,13 +252,13 @@ BGD_DECLARE(void) gdImageXbmCtx(gdImagePtr image, char* file_name, int fg, gdIOC
 				c |= b;
 			}
 			if ((b == 128) || (x == sx && y == sy)) {
 				b = 1;
 				if (p) {
-					gdCtxPrintf(out, ", ");
+					gdCtxPuts(out, ", ");
 					if (!(p%12)) {
-						gdCtxPrintf(out, "\n  ");
+						gdCtxPuts(out, "\n  ");
 						p = 12;
 					}
 				}
 				p++;
 				gdCtxPrintf(out, "0x%02X", c);
@@ -246,8 +266,8 @@ BGD_DECLARE(void) gdImageXbmCtx(gdImagePtr image, char* file_name, int fg, gdIOC
 			} else {
 				b <<= 1;
 			}
 		}
 	}
-	gdCtxPrintf(out, "};\n");
+	gdCtxPuts(out, "};\n");
 }
 /* }}} */

Vulnerable code: 	char buf[4096];
	gdCtxPrintf(out, "#define %s_width %d\n", name, gdImageSX(image));
	gdCtxPrintf(out, "#define %s_height %d\n", name, gdImageSY(image));
	gdCtxPrintf(out, "static unsigned char %s_bits[] = {\n  ", name);
					gdCtxPrintf(out, ", ");
						gdCtxPrintf(out, "\n  ");
	gdCtxPrintf(out, "};\n");
Fixed code: 	char buf[1024];
/* The compiler will optimize strlen(constant) to a constant number. */
#define gdCtxPuts(out, s) out->putBuf(out, s, strlen(s))

	/* Since "name" comes from the user, run it through a direct puts.
	 * Trying to printf it into a local buffer means we'd need a large
	 * or dynamic buffer to hold it all. */

	/* #define <name>_width 1234 */
	gdCtxPuts(out, "#define ");
	gdCtxPuts(out, name);
	gdCtxPuts(out, "_width ");
	gdCtxPrintf(out, "%d\n", gdImageSX(image));

	/* #define <name>_height 1234 */
	gdCtxPuts(out, "#define ");
	gdCtxPuts(out, name);
	gdCtxPuts(out, "_height ");
	gdCtxPrintf(out, "%d\n", gdImageSY(image));

	/* static unsigned char <name>_bits[] = {\n */
	gdCtxPuts(out, "static unsigned char ");
	gdCtxPuts(out, name);
	gdCtxPuts(out, "_bits[] = {\n  ");
					gdCtxPuts(out, ", ");
						gdCtxPuts(out, "\n  ");
	gdCtxPuts(out, "};\n");