Commit message: Avoid potentially dangerous signed to unsigned conversion

We make sure to never pass a negative `rlen` as size to memcpy(). See
also <https://bugs.php.net/bug.php?id=73280>.

Patch provided by Emmanuel Law.
CVE: CVE-2016-8670
CWE: CWE-119
Vulnerability description: Integer signedness error in the dynamicGetbuf function in gd_io_dp.c in the GD Graphics Library (aka libgd) through 2.2.3, as used in PHP before 5.6.28 and 7.x before 7.0.13, allows remote attackers to cause a denial of service (stack-based buffer overflow) or possibly have unspecified other impact via a crafted imagecreatefromstring call.

Project: libgd
File: gd_io_dp.c

static int allocDynamic(dynamicPtr *dp, int initialSize, void *data)
{
	if(data == NULL) {
		dp->logicalSize = 0;
		dp->dataGood = FALSE;
		dp->data = gdMalloc(initialSize);
	} else {
		dp->logicalSize = initialSize;
		dp->dataGood = TRUE;
		dp->data = data;
	}

	if(dp->data != NULL) {
		dp->realSize = initialSize;
		dp->dataGood = TRUE;
		dp->pos = 0;
		return TRUE;
	} else {
		dp->realSize = 0;
		return FALSE;
	}
}

Project: libgd
File: gd_io_dp.c

static int appendDynamic(dynamicPtr * dp, const void *src, int size)
{
	int bytesNeeded;
	char *tmp;

	if(!dp->dataGood) {
		return FALSE;
	}

	/* bytesNeeded = dp->logicalSize + size; */
	bytesNeeded = dp->pos + size;

	if(bytesNeeded > dp->realSize) {
		/* 2.0.21 */
		if(!dp->freeOK) {
			return FALSE;
		}

		if(overflow2(dp->realSize, 2)) {
			return FALSE;
		}

		if(!gdReallocDynamic(dp, bytesNeeded * 2)) {
			dp->dataGood = FALSE;
			return FALSE;
		}
	}

	/* if we get here, we can be sure that we have enough bytes
	 * to copy safely */

	/*printf("Mem OK Size: %d, Pos: %d\n", dp->realSize, dp->pos); */

	tmp = (char *)dp->data;
	memcpy ((void *)(tmp + (dp->pos)), src, size);
	dp->pos += size;

	if(dp->pos > dp->logicalSize) {
		dp->logicalSize = dp->pos;
	};

	return TRUE;
}

Project: libgd
File: gd_io_dp.c

static int dynamicGetchar(gdIOCtxPtr ctx)
{
	unsigned char b;
	int rv;

	rv = dynamicGetbuf(ctx, &b, 1);

	if(rv != 1) {
		return EOF;
	} else {
		return b; /* (b & 0xff); */
	}
}

Project: libgd
File: gd_io_dp.c

static int dynamicPutbuf(struct gdIOCtx *ctx, const void *buf, int size)
{
	dpIOCtx *dctx;
	dctx = (dpIOCtx *)ctx;

	appendDynamic(dctx->dp, buf, size);

	if(dctx->dp->dataGood) {
		return size;
	} else {
		return -1;
	};
}

Project: libgd
File: gd_io_dp.c

static void dynamicPutchar(struct gdIOCtx *ctx, int a)
{
	unsigned char b;
	dpIOCtxPtr dctx;

	b = a;
	dctx = (dpIOCtxPtr) ctx;

	appendDynamic(dctx->dp, &b, 1);
}

Project: libgd
File: gd_io_dp.c

static long dynamicTell(struct gdIOCtx *ctx)
{
	dpIOCtx *dctx;

	dctx = (dpIOCtx *)ctx;
	return (dctx->dp->pos);
}

Project: libgd
File: gd_io_dp.c

BGD_DECLARE(void *) gdDPExtractData (struct gdIOCtx *ctx, int *size)
{
	dynamicPtr *dp;
	dpIOCtx *dctx;
	void *data;

	dctx = (dpIOCtx *)ctx;
	dp = dctx->dp;

	/* clean up the data block and return it */
	if(dp->dataGood) {
		trimDynamic(dp);
		*size = dp->logicalSize;
		data = dp->data;
	} else {
		*size = 0;
		data = NULL;
		/* 2.0.21: never free memory we don't own */
		if((dp->data != NULL) && (dp->freeOK)) {
			gdFree(dp->data);
		}
	}

	dp->data = NULL;
	dp->realSize = 0;
	dp->logicalSize = 0;

	return data;
}

Project: libgd
File: gd_io_dp.c

BGD_DECLARE(gdIOCtx *) gdNewDynamicCtx(int initialSize, void *data)
{
	/* 2.0.23: Phil Moore: 'return' keyword was missing! */
	return gdNewDynamicCtxEx(initialSize, data, 1);
}

Project: libgd
File: gd_io_dp.c

static int gdReallocDynamic(dynamicPtr *dp, int required)
{
	void *newPtr;

	/* First try gdRealloc().  If that doesn't work, make a new
	 * memory block and copy. */
	if((newPtr = gdRealloc(dp->data, required))) {
		dp->realSize = required;
		dp->data = newPtr;
		return TRUE;
	}

	/* create a new pointer */
	newPtr = gdMalloc(required);
	if(!newPtr) {
		dp->dataGood = FALSE;
		return FALSE;
	}

	/* copy the old data into it */
	memcpy(newPtr, dp->data, dp->logicalSize);
	gdFree(dp->data);
	dp->data = newPtr;

	dp->realSize = required;
	return TRUE;
}

Project: libgd
File: gd_io_dp.c

static dynamicPtr *newDynamic(int initialSize, void *data, int freeOKFlag)
{
	dynamicPtr *dp;

	dp = (dynamicPtr *) gdMalloc(sizeof (dynamicPtr));
	if(dp == NULL) {
		return NULL;
	}

	if(!allocDynamic(dp, initialSize, data)) {
		gdFree(dp);
		return NULL;
	}

	dp->pos = 0;
	dp->freeOK = freeOKFlag;

	return dp;
}

Project: libgd
File: gd_io_dp.c

static int trimDynamic(dynamicPtr *dp)
{
	/* 2.0.21: we don't reallocate memory we don't own */
	if(!dp->freeOK) {
		return TRUE;
	}

	return gdReallocDynamic(dp, dp->logicalSize);
}