diff --git a/NEWS b/NEWS index ba7a6bc80584..64b039ce286a 100644 --- a/NEWS +++ b/NEWS @@ -209,6 +209,7 @@ PHP NEWS invalid quality parameter. (David Carlier) . Check overflow/underflow for imagescale/imagefilter. (David Carlier) . Added gdImageClone to bundled libgd. (David Carlier) + . Fixed bug GH-21163 (Integer overflow in gdImageCopy* functions). (Pierre Joye, Jordi Kroon) - Gettext: . bind_textdomain_codeset, textdomain and d(*)gettext functions diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index 44a90773ee12..dc79cb1da64d 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -3,6 +3,7 @@ #include #include #include "gd.h" +#include "gd_intern.h" #include "gdhelpers.h" #include "gd_errors.h" @@ -2314,7 +2315,9 @@ void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int tox, toy; int i; int colorMap[gdMaxColors]; - + if (!gdImageClipCopy(dst, &dstX, &dstY, &srcX, &srcY, &w, &h)) { + return; + } if (dst->trueColor) { /* 2.0: much easier when the destination is truecolor. */ /* 2.0.10: needs a transparent-index check that is still valid if @@ -2394,6 +2397,9 @@ void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int s int x, y; int tox, toy; int ncR, ncG, ncB; + if (!gdImageClipCopy(dst, &dstX, &dstY, &srcX, &srcY, &w, &h)) { + return; + } toy = dstY; for (y = srcY; y < (srcY + h); y++) { @@ -2435,6 +2441,11 @@ void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, i int tox, toy; int ncR, ncG, ncB; float g; + + if (!gdImageClipCopy(dst, &dstX, &dstY, &srcX, &srcY, &w, &h)) { + return; + } + toy = dstY; for (y = srcY; (y < (srcY + h)); y++) { @@ -2499,7 +2510,9 @@ void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int if (overflow2(sizeof(int), srcH)) { return; } - + if (!gdImageClipCopyResized(dst, &dstX, &dstY, &dstW, &dstH, &srcX, &srcY, &srcW, &srcH)) { + return; + } stx = (int *) gdMalloc (sizeof (int) * srcW); sty = (int *) gdMalloc (sizeof (int) * srcH); @@ -2601,6 +2614,9 @@ void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, i gdImageCopyResized (dst, src, dstX, dstY, srcX, srcY, dstW, dstH, srcW, srcH); return; } + if (!gdImageClipCopyResized(dst, &dstX, &dstY, &dstW, &dstH, &srcX, &srcY, &srcW, &srcH)) { + return; + } for (y = dstY; (y < dstY + dstH); y++) { sy1 = ((double) y - (double) dstY) * (double) srcH / (double) dstH; sy2 = ((double) (y + 1) - (double) dstY) * (double) srcH / (double) dstH; diff --git a/ext/gd/libgd/gd_intern.h b/ext/gd/libgd/gd_intern.h index ce72bf1cc1f9..e0f3a71e2bd7 100644 --- a/ext/gd/libgd/gd_intern.h +++ b/ext/gd/libgd/gd_intern.h @@ -1,5 +1,8 @@ #ifndef GD_INTERN_H #define GD_INTERN_H + +#include + #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #endif @@ -10,5 +13,124 @@ #define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c))) #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) -#endif +/** + * Clip a copy operation to the destination image bounds. + * Adjusts srcX/srcY to match any clipping applied to dstX/dstY. + * Returns 0 if the operation is entirely outside dst (nothing to do), + * 1 if there is work to do. + */ +static inline int gdImageClipCopy( + gdImagePtr dst, + int *dstX, int *dstY, + int *srcX, int *srcY, + int *w, int *h) +{ + int x1, y1, x2, y2; + + /* overflow-safe dst rect: [dstX, dstY] to [dstX+w, dstY+h] */ + x1 = *dstX; + y1 = *dstY; + + /* check w/h are positive */ + if (*w <= 0 || *h <= 0) { + return 0; + } + + /* overflow check for dstX+w and dstY+h */ + if (*dstX > 0 && *w > INT_MAX - *dstX) { + x2 = INT_MAX; + } else { + x2 = *dstX + *w; + } + if (*dstY > 0 && *h > INT_MAX - *dstY) { + y2 = INT_MAX; + } else { + y2 = *dstY + *h; + } + + /* entirely outside dst? */ + if (x1 >= gdImageSX(dst) || y1 >= gdImageSY(dst) || + x2 <= 0 || y2 <= 0) { + return 0; + } + + /* clip left */ + if (x1 < 0) { + *srcX -= x1; /* advance srcX by the same amount */ + *w += x1; /* reduce width */ + *dstX = 0; + } + + /* clip top */ + if (y1 < 0) { + *srcY -= y1; + *h += y1; + *dstY = 0; + } + + /* clip right */ + if (*dstX + *w > gdImageSX(dst)) { + *w = gdImageSX(dst) - *dstX; + } + + /* clip bottom */ + if (*dstY + *h > gdImageSY(dst)) { + *h = gdImageSY(dst) - *dstY; + } + + /* sanity: clipping may have reduced w/h to zero */ + if (*w <= 0 || *h <= 0) { + return 0; + } + + return 1; +} +static inline int gdImageClipCopyResized( + gdImagePtr dst, + int *dstX, int *dstY, + int *dstW, int *dstH, + int *srcX, int *srcY, + int *srcW, int *srcH) +{ + int orig_dstW = *dstW; + int orig_dstH = *dstH; + + if (*dstW <= 0 || *dstH <= 0 || *srcW <= 0 || *srcH <= 0) { + return 0; + } + if (*dstX >= gdImageSX(dst) || *dstY >= gdImageSY(dst) || + *dstX + *dstW <= 0 || *dstY + *dstH <= 0) { + return 0; + } + + /* clip left — adjust srcX proportionally */ + if (*dstX < 0) { + *srcX += (-*dstX) * *srcW / orig_dstW; + *srcW -= (-*dstX) * *srcW / orig_dstW; + *dstW += *dstX; + *dstX = 0; + } + /* clip top */ + if (*dstY < 0) { + *srcY += (-*dstY) * *srcH / orig_dstH; + *srcH -= (-*dstY) * *srcH / orig_dstH; + *dstH += *dstY; + *dstY = 0; + } + /* clip right */ + if (*dstX + *dstW > gdImageSX(dst)) { + int clip = *dstX + *dstW - gdImageSX(dst); + *srcW -= clip * *srcW / orig_dstW; + *dstW = gdImageSX(dst) - *dstX; + } + /* clip bottom */ + if (*dstY + *dstH > gdImageSY(dst)) { + int clip = *dstY + *dstH - gdImageSY(dst); + *srcH -= clip * *srcH / orig_dstH; + *dstH = gdImageSY(dst) - *dstY; + } + if (*dstW <= 0 || *dstH <= 0) return 0; + return 1; +} +#endif /* GD_INTERN_H */ diff --git a/ext/gd/tests/gh21163.phpt b/ext/gd/tests/gh21163.phpt new file mode 100644 index 000000000000..46f9c76266f8 --- /dev/null +++ b/ext/gd/tests/gh21163.phpt @@ -0,0 +1,79 @@ +--TEST-- +GH-17772 (prevents signed int overflow in gdImageCopy functions) +--EXTENSIONS-- +gd +--SKIPIF-- + +--FILE-- + +--EXPECT-- +OK imagecopy +OK imagecopymerge +OK imagecopymergegray +OK imagecopyresized +OK imagecopyresampled +done