Commit ec8342c2227 for php.net
commit ec8342c2227e46dc3b818d591dce9b8072fe3a1e
Author: David Carlier <devnexen@gmail.com>
Date: Fri May 22 18:34:25 2026 +0100
Fix GH-19666: imageconvolution() unexpected nan filter value.
undefined behavior occurs during cast from float to int when the element
of the filter matrix is -INF. Port the libgd clamp helper which treats
non-finite accumulators as 0 (negative/NaN) or 255 (positive infinity)
instead of bailing out, matching upstream libgd 81d7f2e.
close GH-22129
diff --git a/NEWS b/NEWS
index 460156172ca..db03aa98c3a 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ PHP NEWS
- GD:
. Fixed bug GH-22121 (Double free in gdImageSetStyle() after
overflow-triggered early return). (iliaal)
+ . Fixed bug GH-19666 (imageconvolution() unexpected nan filter value).
+ (David Carlier)
- Intl:
. Fix incorrect argument positions for uninitialized calendar arguments in
diff --git a/ext/gd/libgd/gd_filter.c b/ext/gd/libgd/gd_filter.c
index db364c923ec..201a0a8630f 100644
--- a/ext/gd/libgd/gd_filter.c
+++ b/ext/gd/libgd/gd_filter.c
@@ -7,6 +7,7 @@
#else
# include <unistd.h>
#endif
+#include <math.h>
#include <stdlib.h>
#include <time.h>
@@ -20,6 +21,20 @@
/* Begin filters function */
#define GET_PIXEL_FUNCTION(src)(src->trueColor?gdImageGetTrueColorPixel:gdImageGetPixel)
+static int gdClampFloatToByte(float value)
+{
+ if (!isfinite(value)) {
+ return value > 0.0f ? 255 : 0;
+ }
+ if (value > 255.0f) {
+ return 255;
+ }
+ if (value < 0.0f) {
+ return 0;
+ }
+ return (int)value;
+}
+
#ifdef _WIN32
# define GD_SCATTER_SEED() (unsigned int)(time(0) * GetCurrentProcessId())
#else
@@ -361,6 +376,7 @@ int gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, flo
for ( y=0; y<src->sy; y++) {
for(x=0; x<src->sx; x++) {
+ int new_ri, new_gi, new_bi;
new_r = new_g = new_b = 0;
pxl = f(srcback, x, y);
new_a = gdImageAlpha(srcback, pxl);
@@ -379,13 +395,13 @@ int gdImageConvolution(gdImagePtr src, float filter[3][3], float filter_div, flo
new_g = (new_g/filter_div)+offset;
new_b = (new_b/filter_div)+offset;
- new_r = (new_r > 255.0f)? 255.0f : ((new_r < 0.0f)? 0.0f:new_r);
- new_g = (new_g > 255.0f)? 255.0f : ((new_g < 0.0f)? 0.0f:new_g);
- new_b = (new_b > 255.0f)? 255.0f : ((new_b < 0.0f)? 0.0f:new_b);
+ new_ri = gdClampFloatToByte(new_r);
+ new_gi = gdClampFloatToByte(new_g);
+ new_bi = gdClampFloatToByte(new_b);
- new_pxl = gdImageColorAllocateAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a);
+ new_pxl = gdImageColorAllocateAlpha(src, new_ri, new_gi, new_bi, new_a);
if (new_pxl == -1) {
- new_pxl = gdImageColorClosestAlpha(src, (int)new_r, (int)new_g, (int)new_b, new_a);
+ new_pxl = gdImageColorClosestAlpha(src, new_ri, new_gi, new_bi, new_a);
}
gdImageSetPixel (src, x, y, new_pxl);
}
diff --git a/ext/gd/tests/gh19666.phpt b/ext/gd/tests/gh19666.phpt
new file mode 100644
index 00000000000..2d82b9b4476
--- /dev/null
+++ b/ext/gd/tests/gh19666.phpt
@@ -0,0 +1,34 @@
+--TEST--
+GH-19666 (Unexpected nan value in imageconvolution)
+--EXTENSIONS--
+gd
+--FILE--
+<?php
+$image = imagecreatetruecolor(180, 30);
+
+$gaussian = array(
+ array(1.0, 2.0, 1.0),
+ array(2.0, 4.0, 2.0),
+ array(1.0, 2.0, -INF)
+);
+
+var_dump(imageconvolution($image, $gaussian, 16, 0));
+
+$rgba = imagecolorat($image, 0, 0);
+$colors = imagecolorsforindex($image, $rgba);
+var_dump($colors);
+
+imagedestroy($image);
+?>
+--EXPECT--
+bool(true)
+array(4) {
+ ["red"]=>
+ int(0)
+ ["green"]=>
+ int(0)
+ ["blue"]=>
+ int(0)
+ ["alpha"]=>
+ int(0)
+}