CVE-2019-6978 (C, published 2019-01-28T08:29Z)
Description: value: The GD Graphics Library (aka LibGD) 2.2.5 has a double free in the gdImage*Ptr() functions in gd_gif_out.c, gd_jpeg.c, and gd_wbmp.c. NOTE: PHP is unaffected.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-415 \u2014 Double Free
The product calls free() twice on the same memory address, potentially leading to modification of unexpected memory locations.
Repository: https://github.com/libgd/libgd
Files changed:
src/gd_gif_out.c
src/gd_jpeg.c
src/gd_wbmp.c
tests/jpeg/.gitignore
tests/jpeg/CMakeLists.txt
tests/jpeg/Makemodule.am
tests/jpeg/jpeg_ptr_double_free.c
Commit message: Fix #492: Potential double-free in gdImage*Ptr()

Whenever `gdImage*Ptr()` calls `gdImage*Ctx()` and the latter fails, we
must not call `gdDPExtractData()`; otherwise a double-free would
happen.  Since `gdImage*Ctx()` are void functions, and we can't change
that for BC reasons, we're introducing static helpers which are used
internally.

We're adding a regression test for `gdImageJpegPtr()`, but not for
`gdImageGifPtr()` and `gdImageWbmpPtr()` since we don't know how to
trigger failure of the respective `gdImage*Ctx()` calls.

This potential security issue has been reported by Solmaz Salimi (aka.
Rooney).


Vulnerable code:
	gdImageGifCtx(im, out);
	rv = gdDPExtractData(out, size);
			return;
	gdImageJpegCtx(im, out, quality);
	rv = gdDPExtractData(out, size);
		return;
		return;
		return;
	gdImageWBMPCtx(im, fg, out);
	rv = gdDPExtractData(out, size);
	jpeg/jpeg_null

Fixed code:
static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out);
	if (!_gdImageGifCtx(im, out)) {
		rv = gdDPExtractData(out, size);
	} else {
		rv = NULL;
	}
{
	_gdImageGifCtx(im, out);
}

/* returns 0 on success, 1 on failure */
static int _gdImageGifCtx(gdImagePtr im, gdIOCtxPtr out)
			return 1;

	return 0;
static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality);

	if (!_gdImageJpegCtx(im, out, quality)) {
		rv = gdDPExtractData(out, size);
	} else {
		rv = NULL;
	}
{
	_gdImageJpegCtx(im, outfile, quality);
}

/* returns 0 on success, 1 on failure */
static int _gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality)
		return 1;
		return 1;
	return 0;
static int _gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out);

{
	_gdImageWBMPCtx(image, fg, out);
}

/* returns 0 on success, 1 on failure */
static int _gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out)
		return 1;
		freewbmp(wbmp);
		return 1;

	return 0;
	if (!_gdImageWBMPCtx(im, fg, out)) {
		rv = gdDPExtractData(out, size);
	} else {
		rv = NULL;
	}
/jpeg_ptr_double_free
	jpeg_ptr_double_free
	jpeg/jpeg_null \
	jpeg/jpeg_ptr_double_free
/**
 * Test that failure to convert to JPEG returns NULL
 *
 * We are creating an image, set its width to zero, and pass this image to
 * `gdImageJpegPtr()` which is supposed to fail, and as such should return NULL.
 *
 * See also <https://github.com/libgd/libgd/issues/381>
 */


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


int main()
{
    gdImagePtr src, dst;
    int size;

    src = gdImageCreateTrueColor(1, 10);
    gdTestAssert(src != NULL);

    src->sx = 0; /* this hack forces gdImageJpegPtr() to fail */

    dst = gdImageJpegPtr(src, &size, 0);
    gdTestAssert(dst == NULL);

    gdImageDestroy(src);

    return gdNumFailures();
}