Commit 188d520972a for php.net
commit 188d520972a127a75adb0c9e10017fc665e8823c
Author: David Carlier <devnexen@gmail.com>
Date: Sat Mar 8 19:24:44 2025 +0000
ext/gd: calls with array types check strengthening.
close GH-18005
diff --git a/NEWS b/NEWS
index d688ef5aad3..6a37ca9791c 100644
--- a/NEWS
+++ b/NEWS
@@ -32,6 +32,10 @@ PHP NEWS
(ndossche)
. Fixed bug #66095 (Hide libmagic dynamic symbols). (orlitzky)
+- GD:
+ . imagesetstyle()/imagefilter()/imagecrop() check array argument entries
+ types. (David Carlier)
+
- Hash:
. Upgrade xxHash to 0.8.2. (timwolla)
diff --git a/UPGRADING b/UPGRADING
index a97ad00061a..a7c91c4501e 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -103,6 +103,11 @@ PHP 8.6 UPGRADE NOTES
5. Changed Functions
========================================
+- GD:
+ . imagesetstyle(), imagefilter() and imagecrop() filter their
+ array arguments types/values and raise a TypeError/ValueError
+ accordingly.
+
- mysqli:
. The return structure of mysqli_get_charset() no longer contains
the undocumented "comment" element. The value of "charsetnr" is
diff --git a/ext/gd/gd.c b/ext/gd/gd.c
index 2624d13b51d..ce2f74f6cdb 100644
--- a/ext/gd/gd.c
+++ b/ext/gd/gd.c
@@ -640,7 +640,20 @@ PHP_FUNCTION(imagesetstyle)
stylearr = safe_emalloc(num_styles, sizeof(int), 0);
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(styles), item) {
- stylearr[index++] = zval_get_long(item);
+ bool failed = false;
+ ZVAL_DEREF(item);
+ zend_long tmp = zval_try_get_long(item, &failed);
+ if (failed) {
+ efree(stylearr);
+ zend_argument_type_error(2, "must only have elements of type int, %s given", zend_zval_type_name(item));
+ RETURN_THROWS();
+ }
+ if (ZEND_LONG_EXCEEDS_INT(tmp)) {
+ efree(stylearr);
+ zend_argument_value_error(2, "elements must be between %d and %d", INT_MIN, INT_MAX);
+ RETURN_THROWS();
+ }
+ stylearr[index++] = (int) tmp;
} ZEND_HASH_FOREACH_END();
gdImageSetStyle(im, stylearr, index);
@@ -3595,7 +3608,20 @@ static void php_image_filter_scatter(INTERNAL_FUNCTION_PARAMETERS)
colors = safe_emalloc(num_colors, sizeof(int), 0);
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(hash_colors), color) {
- *(colors + i++) = (int) zval_get_long(color);
+ bool failed = false;
+ ZVAL_DEREF(color);
+ zend_long tmp = zval_try_get_long(color, &failed);
+ if (failed) {
+ efree(colors);
+ zend_argument_type_error(5, "must be of type int, %s given", zend_zval_type_name(color));
+ RETURN_THROWS();
+ }
+ if (tmp < 0 || ZEND_LONG_INT_OVFL(tmp)) {
+ efree(colors);
+ zend_argument_value_error(5, "value must be between 0 and %d", INT_MAX);
+ RETURN_THROWS();
+ }
+ colors[i++] = (int) tmp;
} ZEND_HASH_FOREACH_END();
RETVAL_BOOL(gdImageScatterColor(im, (int)scatter_sub, (int)scatter_plus, colors, num_colors));
@@ -3763,6 +3789,23 @@ PHP_FUNCTION(imageantialias)
}
/* }}} */
+static bool php_gd_zval_try_get_c_int(zval *tmp, const char *field, int *res) {
+ zend_long r;
+ bool failed = false;
+ ZVAL_DEREF(tmp);
+ r = zval_try_get_long(tmp, &failed);
+ if (failed) {
+ zend_argument_type_error(2, "\"%s\" key must be of type int, %s given", field, zend_zval_type_name(tmp));
+ return false;
+ }
+ if (UNEXPECTED(ZEND_LONG_EXCEEDS_INT(r))) {
+ zend_argument_value_error(2, "\"%s\" key must be between %d and %d", field, INT_MIN, INT_MAX);
+ return false;
+ }
+ *res = (int)r;
+ return true;
+}
+
/* {{{ Crop an image using the given coordinates and size, x, y, width and height. */
PHP_FUNCTION(imagecrop)
{
@@ -3781,28 +3824,36 @@ PHP_FUNCTION(imagecrop)
im = php_gd_libgdimageptr_from_zval_p(IM);
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "x", sizeof("x") -1)) != NULL) {
- rect.x = zval_get_long(tmp);
+ if (!php_gd_zval_try_get_c_int(tmp, "x", &rect.x)) {
+ RETURN_THROWS();
+ }
} else {
zend_argument_value_error(2, "must have an \"x\" key");
RETURN_THROWS();
}
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "y", sizeof("y") - 1)) != NULL) {
- rect.y = zval_get_long(tmp);
+ if (!php_gd_zval_try_get_c_int(tmp, "y", &rect.y)) {
+ RETURN_THROWS();
+ }
} else {
zend_argument_value_error(2, "must have a \"y\" key");
RETURN_THROWS();
}
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "width", sizeof("width") - 1)) != NULL) {
- rect.width = zval_get_long(tmp);
+ if (!php_gd_zval_try_get_c_int(tmp, "width", &rect.width)) {
+ RETURN_THROWS();
+ }
} else {
zend_argument_value_error(2, "must have a \"width\" key");
RETURN_THROWS();
}
if ((tmp = zend_hash_str_find(Z_ARRVAL_P(z_rect), "height", sizeof("height") - 1)) != NULL) {
- rect.height = zval_get_long(tmp);
+ if (!php_gd_zval_try_get_c_int(tmp, "height", &rect.height)) {
+ RETURN_THROWS();
+ }
} else {
zend_argument_value_error(2, "must have a \"height\" key");
RETURN_THROWS();
diff --git a/ext/gd/tests/bug66356.phpt b/ext/gd/tests/bug66356.phpt
index b65fb54f571..616a270115c 100644
--- a/ext/gd/tests/bug66356.phpt
+++ b/ext/gd/tests/bug66356.phpt
@@ -7,10 +7,14 @@
$img = imagecreatetruecolor(10, 10);
// POC #1
-var_dump(imagecrop($img, array("x" => "a", "y" => 0, "width" => 10, "height" => 10)));
+var_dump(imagecrop($img, array("x" => 0, "y" => 0, "width" => 10, "height" => 10)));
-$arr = array("x" => "a", "y" => "12b", "width" => 10, "height" => 10);
-var_dump(imagecrop($img, $arr));
+$arr = array("x" => 2147483647, "y" => 2147483647, "width" => 10, "height" => 10);
+try {
+ imagecrop($img, $arr);
+} catch (\ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
print_r($arr);
// POC #2
@@ -28,12 +32,11 @@
--EXPECTF--
object(GdImage)#2 (0) {
}
-object(GdImage)#2 (0) {
-}
+imagecrop(): Argument #2 ($rectangle) overflow with "x" and "width" keys
Array
(
- [x] => a
- [y] => 12b
+ [x] => 2147483647
+ [y] => 2147483647
[width] => 10
[height] => 10
)
@@ -41,9 +44,9 @@
Warning: imagecrop(): %cne parameter to a memory allocation multiplication is negative or zero, failing operation gracefully
in %s on line %d
bool(false)
-object(GdImage)#2 (0) {
+object(GdImage)#3 (0) {
}
-object(GdImage)#2 (0) {
+object(GdImage)#3 (0) {
}
Warning: imagecrop(): %croduct of memory allocation multiplication would exceed INT_MAX, failing operation gracefully
diff --git a/ext/gd/tests/gh18005.phpt b/ext/gd/tests/gh18005.phpt
new file mode 100644
index 00000000000..5282c0be026
--- /dev/null
+++ b/ext/gd/tests/gh18005.phpt
@@ -0,0 +1,92 @@
+--TEST--
+GH-18005: imagesetstyle, imagefilter, imagecrop array values type checks
+--EXTENSIONS--
+gd
+--SKIPIF--
+<?php
+if (PHP_INT_SIZE != 8) die('skip this test is for 64bit platforms only');
+?>
+--FILE--
+<?php
+class A {}
+$img = imagecreatetruecolor(1, 1);
+try {
+ imagesetstyle($img, [0, new A()]);
+} catch (\TypeError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagesetstyle($img, [0, PHP_INT_MIN]);
+} catch (\ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagefilter($img, IMG_FILTER_SCATTER, 0, 0, [new A()]);
+} catch (\TypeError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagefilter($img, IMG_FILTER_SCATTER, 0, 0, [-1]);
+} catch (\ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagecrop($img, ["x" => PHP_INT_MIN, "y" => 0, "width" => 0, "height" => 0]);
+} catch (\ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagecrop($img, ["x" => 0, "y" => PHP_INT_MIN, "width" => 0, "height" => 0]);
+} catch (\ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagecrop($img, ["x" => 0, "y" => 0, "width" => PHP_INT_MAX, "height" => 0]);
+} catch (\ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagecrop($img, ["x" => 0, "y" => 0, "width" => 0, "height" => PHP_INT_MAX]);
+} catch (\ValueError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+
+try {
+ imagecrop($img, ["x" => new A(), "y" => 0, "width" => 0, "height" => 0]);
+} catch (\TypeError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagecrop($img, ["x" => 0, "y" => new A(), "width" => 0, "height" => 0]);
+} catch (\TypeError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagecrop($img, ["x" => 0, "y" => 0, "width" => new A(), "height" => 0]);
+} catch (\TypeError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+try {
+ imagecrop($img, ["x" => 0, "y" => 0, "width" => 0, "height" => new A()]);
+} catch (\TypeError $e) {
+ echo $e->getMessage() . PHP_EOL;
+}
+
+$one = 1;
+var_dump(imagecrop($img, ["x" => &$one, "y" => &$one, "width" => &$one, "height" => &$one]));
+?>
+--EXPECTF--
+imagesetstyle(): Argument #2 ($style) must only have elements of type int, A given
+imagesetstyle(): Argument #2 ($style) elements must be between %i and %d
+imagefilter(): Argument #5 must be of type int, A given
+imagefilter(): Argument #5 value must be between 0 and 2147483647
+imagecrop(): Argument #2 ($rectangle) "x" key must be between %i and %d
+imagecrop(): Argument #2 ($rectangle) "y" key must be between %i and %d
+imagecrop(): Argument #2 ($rectangle) "width" key must be between %i and %d
+imagecrop(): Argument #2 ($rectangle) "height" key must be between %i and %d
+imagecrop(): Argument #2 ($rectangle) "x" key must be of type int, A given
+imagecrop(): Argument #2 ($rectangle) "y" key must be of type int, A given
+imagecrop(): Argument #2 ($rectangle) "width" key must be of type int, A given
+imagecrop(): Argument #2 ($rectangle) "height" key must be of type int, A given
+object(GdImage)#2 (0) {
+}