Vulnerability: ** DISPUTED ** gdImageGd2Ptr in gd_gd2.c in the GD Graphics Library (aka LibGD) through 2.3.2 has a double free. NOTE: the vendor's position is "The GD2 image format is a proprietary image format of libgd. It has to be regarded as being obsolete, and should only be used for development and testing purposes."
Vulnerability ID: CVE-2021-40145
CWE: CWE-415
Severity score: 2.9
Project: https://github.com/libgd/libgd
Published: 2021-08-26
File: src/gd_gd2.c
Commit message: Merge pull request #713 from me22bee/_gdImageGd2

gdImageGd2Ptr memory leak
Commit author: Pierre Joye

Vulnerable version (before fix):
 _gd2PutHeader (gdImagePtr im, gdIOCtx * out, int cs, int fmt, int cx, int cy)  }  static void _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt) { 	int ncx, ncy, cx, cy; 	int x, y, ylo, yhi, xlo, xhi; 	int chunkLen; _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt) 		/* */ 		chunkData = gdCalloc (cs * bytesPerPixel * cs, 1); 		if (!chunkData) { 			goto fail; 		} 		compData = gdCalloc (compMax, 1); 		if (!compData) { 			goto fail; 		}  _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)  		chunkIdx = gdCalloc (idxSize * sizeof (t_chunk_info), 1); 		if (!chunkIdx) { 			goto fail; 		} 	}; _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt) 	} 	GD2_DBG (printf ("Done\n"));  }  /* BGD_DECLARE(void *) gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size) 	void *rv; 	gdIOCtx *out = gdNewDynamicCtx (2048, NULL); 	if (out == NULL) return NULL; 	_gdImageGd2 (im, out, cs, fmt); 	rv = gdDPExtractData (out, size); 	out->gd_free (out); 	return rv; }

Fixed version (after fix):
 _gd2PutHeader (gdImagePtr im, gdIOCtx * out, int cs, int fmt, int cx, int cy)  }  /* returns 0 on success, 1 on failure */ static int _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt) { 	int ret = 0; 	int ncx, ncy, cx, cy; 	int x, y, ylo, yhi, xlo, xhi; 	int chunkLen; _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt) 		/* */ 		chunkData = gdCalloc (cs * bytesPerPixel * cs, 1); 		if (!chunkData) { 			ret = 1; 			goto fail; 		} 		compData = gdCalloc (compMax, 1); 		if (!compData) { 			ret = 1; 			goto fail; 		}  _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)  		chunkIdx = gdCalloc (idxSize * sizeof (t_chunk_info), 1); 		if (!chunkIdx) { 			ret = 1; 			goto fail; 		} 	}; _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt) 	} 	GD2_DBG (printf ("Done\n"));  	return ret;  }  /* BGD_DECLARE(void *) gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size) 	void *rv; 	gdIOCtx *out = gdNewDynamicCtx (2048, NULL); 	if (out == NULL) return NULL; 	 	if (_gdImageGd2(im, out, cs, fmt)) { 		rv = NULL; 	} else { 		rv = gdDPExtractData(out, size); 	} 	 	out->gd_free (out); 	return rv; }

Patch:
patch: @@ -910,9 +910,11 @@ _gd2PutHeader (gdImagePtr im, gdIOCtx * out, int cs, int fmt, int cx, int cy)
 
 }
 
-static void
+/* returns 0 on success, 1 on failure */
+static int
 _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)
 {
+	int ret = 0;
 	int ncx, ncy, cx, cy;
 	int x, y, ylo, yhi, xlo, xhi;
 	int chunkLen;
@@ -974,10 +976,12 @@ _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)
 		/* */
 		chunkData = gdCalloc (cs * bytesPerPixel * cs, 1);
 		if (!chunkData) {
+			ret = 1;
 			goto fail;
 		}
 		compData = gdCalloc (compMax, 1);
 		if (!compData) {
+			ret = 1;
 			goto fail;
 		}
 
@@ -992,6 +996,7 @@ _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)
 
 		chunkIdx = gdCalloc (idxSize * sizeof (t_chunk_info), 1);
 		if (!chunkIdx) {
+			ret = 1;
 			goto fail;
 		}
 	};
@@ -1107,6 +1112,8 @@ _gdImageGd2 (gdImagePtr im, gdIOCtx * out, int cs, int fmt)
 	}
 	GD2_DBG (printf ("Done\n"));
 
+	return ret;
+
 }
 
 /*
@@ -1128,8 +1135,13 @@ BGD_DECLARE(void *) gdImageGd2Ptr (gdImagePtr im, int cs, int fmt, int *size)
 	void *rv;
 	gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
 	if (out == NULL) return NULL;
-	_gdImageGd2 (im, out, cs, fmt);
-	rv = gdDPExtractData (out, size);
+	
+	if (_gdImageGd2(im, out, cs, fmt)) {
+		rv = NULL;
+	} else {
+		rv = gdDPExtractData(out, size);
+	}
+	
 	out->gd_free (out);
 	return rv;
 }
path: src/gd_gd2.c