Skip to content

Commit 448e528

Browse files
committed
prevent signed int overflow in gdImageCopy functions
1 parent 315fef2 commit 448e528

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
@@ -2307,8 +2307,48 @@ void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int
23072307
_gdImageFilledVRectangle(im, x1, y1, x2, y2, color);
23082308
}
23092309

2310+
static int _gdValidateCopyRectBounds(
2311+
const gdImagePtr dst,
2312+
const gdImagePtr src,
2313+
int dstX, int dstY,
2314+
int srcX, int srcY,
2315+
int w, int h
2316+
) {
2317+
/* Check for null pointers */
2318+
if (!dst || !src) {
2319+
return 0;
2320+
}
2321+
2322+
/* Check for overflow in dstX + w */
2323+
if (w > 0 && dstX > INT_MAX - w) {
2324+
return 0;
2325+
}
2326+
2327+
/* Check for overflow in dstY + h */
2328+
if (h > 0 && dstY > INT_MAX - h) {
2329+
return 0;
2330+
}
2331+
2332+
/* Check for overflow in srcX + w */
2333+
if (w > 0 && srcX > INT_MAX - w) {
2334+
return 0;
2335+
}
2336+
2337+
/* Check for overflow in srcY + h */
2338+
if (h > 0 && srcY > INT_MAX - h) {
2339+
return 0;
2340+
}
2341+
2342+
return 1;
2343+
}
2344+
2345+
23102346
void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
23112347
{
2348+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, w, h)) {
2349+
return;
2350+
}
2351+
23122352
int c;
23132353
int x, y;
23142354
int tox, toy;
@@ -2390,6 +2430,10 @@ void gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX,
23902430
so it doesn't pay attention to the alpha channel. */
23912431
void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
23922432
{
2433+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, w, h)) {
2434+
return;
2435+
}
2436+
23932437
int c, dc;
23942438
int x, y;
23952439
int tox, toy;
@@ -2430,6 +2474,10 @@ void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int s
24302474
so it doesn't pay attention to the alpha channel. */
24312475
void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
24322476
{
2477+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, w, h)) {
2478+
return;
2479+
}
2480+
24332481
int c, dc;
24342482
int x, y;
24352483
int tox, toy;
@@ -2484,6 +2532,10 @@ void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, i
24842532

24852533
void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
24862534
{
2535+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, dstW, dstH)) {
2536+
return;
2537+
}
2538+
24872539
int c;
24882540
int x, y;
24892541
int tox, toy;
@@ -2594,6 +2646,10 @@ void gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int
25942646

25952647
void gdImageCopyResampled (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
25962648
{
2649+
if (!_gdValidateCopyRectBounds(dst, src, dstX, dstY, srcX, srcY, dstW, dstH)) {
2650+
return;
2651+
}
2652+
25972653
int x, y;
25982654
double sy1, sy2, sx1, sx2;
25992655

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)