Skip to content

Commit f26aa41

Browse files
committed
prevent signed int overflow in gdImageCopy functions
1 parent 96be28c commit f26aa41

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

ext/gd/libgd/gd.c

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2326,8 +2326,48 @@ void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int
23262326
_gdImageFilledVRectangle(im, x1, y1, x2, y2, color);
23272327
}
23282328

2329+
static int _gdValidateCopyRectBounds(
2330+
const gdImagePtr dst,
2331+
const gdImagePtr src,
2332+
int dstX, int dstY,
2333+
int srcX, int srcY,
2334+
int w, int h
2335+
) {
2336+
/* Check for null pointers */
2337+
if (!dst || !src) {
2338+
return 0;
2339+
}
2340+
2341+
/* Check for overflow in dstX + w */
2342+
if (w > 0 && dstX > INT_MAX - w) {
2343+
return 0;
2344+
}
2345+
2346+
/* Check for overflow in dstY + h */
2347+
if (h > 0 && dstY > INT_MAX - h) {
2348+
return 0;
2349+
}
2350+
2351+
/* Check for overflow in srcX + w */
2352+
if (w > 0 && srcX > INT_MAX - w) {
2353+
return 0;
2354+
}
2355+
2356+
/* Check for overflow in srcY + h */
2357+
if (h > 0 && srcY > INT_MAX - h) {
2358+
return 0;
2359+
}
2360+
2361+
return 1;
2362+
}
2363+
2364+
23292365
void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
23302366
{
2367+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, w, h)) {
2368+
return;
2369+
}
2370+
23312371
int c;
23322372
int x, y;
23332373
int tox, toy;
@@ -2409,6 +2449,10 @@ void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX,
24092449
so it doesn't pay attention to the alpha channel. */
24102450
void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
24112451
{
2452+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, w, h)) {
2453+
return;
2454+
}
2455+
24122456
int c, dc;
24132457
int x, y;
24142458
int tox, toy;
@@ -2449,6 +2493,10 @@ void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int s
24492493
so it doesn't pay attention to the alpha channel. */
24502494
void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
24512495
{
2496+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, w, h)) {
2497+
return;
2498+
}
2499+
24522500
int c, dc;
24532501
int x, y;
24542502
int tox, toy;
@@ -2503,6 +2551,10 @@ void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, i
25032551

25042552
void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
25052553
{
2554+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, dstW, dstH)) {
2555+
return;
2556+
}
2557+
25062558
int c;
25072559
int x, y;
25082560
int tox, toy;
@@ -2613,6 +2665,10 @@ void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int
26132665

26142666
void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
26152667
{
2668+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, dstW, dstH)) {
2669+
return;
2670+
}
2671+
26162672
int x, y;
26172673
double sy1, sy2, sx1, sx2;
26182674

ext/gd/tests/gh21163.phpt

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
--TEST--
2+
GH-17772 (prevents signed int overflow in gdImageCopy functions)
3+
--EXTENSIONS--
4+
gd
5+
--SKIPIF--
6+
<?php
7+
if (!GD_BUNDLED) die("skip requires bundled GD library");
8+
?>
9+
--FILE--
10+
<?php
11+
function makeImages(): array {
12+
$dst = imagecreatetruecolor(2, 2);
13+
$src = imagecreatetruecolor(2, 2);
14+
15+
$blackDst = imagecolorallocate($dst, 0, 0, 0);
16+
$whiteSrc = imagecolorallocate($src, 255, 255, 255);
17+
18+
imagefilledrectangle($dst, 0, 0, 1, 1, $blackDst);
19+
imagefilledrectangle($src, 0, 0, 1, 1, $whiteSrc);
20+
21+
return [$dst, $src];
22+
}
23+
24+
function assertDstUnchanged(string $label, $dst, int $expectedColor): void {
25+
$actual = imagecolorat($dst, 0, 0);
26+
if ($actual !== $expectedColor) {
27+
echo "FAIL $label: dst changed, got $actual expected $expectedColor\n";
28+
return;
29+
}
30+
echo "OK $label\n";
31+
}
32+
33+
/*
34+
Use values that will overflow a 32 bit signed int when adding w or h.
35+
INT_MAX is 0x7fffffff.
36+
*/
37+
$nearIntMax = 0x7fffffff - 5;
38+
$w = 10;
39+
$h = 10;
40+
41+
/* imagecopy */
42+
[$dst, $src] = makeImages();
43+
$expected = imagecolorat($dst, 0, 0);
44+
imagecopy($dst, $src, $nearIntMax, 0, 0, 0, $w, $h);
45+
assertDstUnchanged('imagecopy', $dst, $expected);
46+
47+
/* imagecopymerge */
48+
[$dst, $src] = makeImages();
49+
$expected = imagecolorat($dst, 0, 0);
50+
imagecopymerge($dst, $src, $nearIntMax, 0, 0, 0, $w, $h, 50);
51+
assertDstUnchanged('imagecopymerge', $dst, $expected);
52+
53+
/* imagecopymergegray */
54+
[$dst, $src] = makeImages();
55+
$expected = imagecolorat($dst, 0, 0);
56+
imagecopymergegray($dst, $src, $nearIntMax, 0, 0, 0, $w, $h, 50);
57+
assertDstUnchanged('imagecopymergegray', $dst, $expected);
58+
59+
/* imagecopyresized */
60+
[$dst, $src] = makeImages();
61+
$expected = imagecolorat($dst, 0, 0);
62+
imagecopyresized($dst, $src, $nearIntMax, 0, 0, 0, $w, $h, 1, 1);
63+
assertDstUnchanged('imagecopyresized', $dst, $expected);
64+
65+
/* imagecopyresampled */
66+
[$dst, $src] = makeImages();
67+
$expected = imagecolorat($dst, 0, 0);
68+
imagecopyresampled($dst, $src, $nearIntMax, 0, 0, 0, $w, $h, 1, 1);
69+
assertDstUnchanged('imagecopyresampled', $dst, $expected);
70+
71+
echo "done\n";
72+
?>
73+
--EXPECT--
74+
OK imagecopy
75+
OK imagecopymerge
76+
OK imagecopymergegray
77+
OK imagecopyresized
78+
OK imagecopyresampled
79+
done

0 commit comments

Comments
 (0)