Skip to content

Commit e5c3b4b

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: Fix out-of-bounds write in ext-bcmath bccomp() via bc_str2num() Add NEWS entries Fix GHSA-vc5h-9ppw-p5f3: phar circular symlink crash Fix SQL injection in ext-pgsql via E'...' backslash breakout libgd patch for CVE-2026-9672
2 parents 3de8e64 + 4f83876 commit e5c3b4b

17 files changed

Lines changed: 178 additions & 32 deletions

NEWS

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ PHP NEWS
4545
. Lock unmodified readonly properties for modification after clone-with.
4646
(NickSdot)
4747

48+
- BCMath:
49+
. Fixed GHSA-x692-q9x7-8c3f (Out-of-bounds write in bccomp()).
50+
(CVE-2026-17544) (Recep Asan)
51+
4852
- Calendar:
4953
. Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with
5054
INT_MAX year). (arshidkv12)
@@ -71,6 +75,9 @@ PHP NEWS
7175
. Fixed bug GH-11020 (exif_read_data() emits a spurious "Illegal IFD size"
7276
warning when an IFD is not followed by a next-IFD offset). (Eyüp Can Akman)
7377

78+
- GD:
79+
. Upgrade libgd. (CVE-2026-9672) (Pierre Joye)
80+
7481
- Hash:
7582
. Fixed bug GH-18173 (ext/hash relies on implementation-defined malloc
7683
alignment). (iliaal)
@@ -112,12 +119,18 @@ PHP NEWS
112119
. Fixed bug GH-22665 (Out-of-bounds write when the ODBC driver reports a
113120
diagnostic message length beyond the error buffer). (iliaal)
114121

122+
- PGSQL:
123+
. Fixed GHSA-7qpv-r5mr-78m4 (SQL injection via E'...' backslash breakout).
124+
(CVE-2026-17543) (ilutov)
125+
115126
- Phar:
116127
. Fixed inconsistent handling of the magic ".phar" directory. Paths such as
117128
"/.phar" remain protected, while non-magic paths that merely start with
118129
".phar" are handled consistently across file and directory creation,
119130
copying, ArrayAccess, stream lookup, directory iteration and extraction.
120131
(Weilin Du)
132+
. Fixed GHSA-vc5h-9ppw-p5f3 (Crash via recursive symlinks). (CVE-2026-7260)
133+
(Jakub Zelenka)
121134

122135
- PHPDBG:
123136
. Fixed bug GH-17387 (Trivial crash in phpdbg lexer). (iliaal)

ext/bcmath/libbcmath/src/str2num.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ bool bc_str2num(bc_num *num, const char *str, const char *end, size_t scale, siz
179179
if (str_scale > 0) {
180180
const char *fractional_new_end = bc_skip_zero_reverse(fractional_end, fractional_ptr);
181181
str_scale -= fractional_end - fractional_new_end; /* fractional_end >= fractional_new_end */
182+
fractional_end = fractional_new_end;
182183
}
183184
}
184185
} else {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
--TEST--
2+
GHSA-x692-q9x7-8c3f: bccomp() out-of-bounds write
3+
--CREDITS--
4+
Recep Asan (recepasan)
5+
--FILE--
6+
<?php
7+
8+
$n = '1.' . '9' . str_repeat('0', 300) . '1';
9+
var_dump(bccomp($n, '0', 300));
10+
11+
?>
12+
--EXPECT--
13+
int(1)

ext/gd/libgd/gd_gif_in.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, i
452452
sd->table[1][i] = i;
453453
}
454454
for (; i < (1<<MAX_LWZ_BITS); ++i)
455-
sd->table[0][i] = sd->table[1][0] = 0;
455+
sd->table[0][i] = sd->table[1][i] = 0;
456456

457457
sd->sp = sd->stack;
458458

@@ -496,6 +496,8 @@ LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, i
496496

497497
if (count != 0)
498498
return -2;
499+
500+
return -2;
499501
}
500502

501503
incode = code;
@@ -562,7 +564,7 @@ ReadImage(gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)
562564
int v;
563565
int xpos = 0, ypos = 0, pass = 0;
564566
int i;
565-
LZW_STATIC_DATA sd;
567+
LZW_STATIC_DATA sd = {0};
566568

567569

568570
/*

ext/pgsql/pgsql.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4890,7 +4890,7 @@ static int php_pgsql_convert_match(const zend_string *str, zend_string *regex)
48904890
*/
48914891
static zend_string *php_pgsql_add_quotes(zend_string *src)
48924892
{
4893-
return zend_string_concat3("E'", strlen("E'"), ZSTR_VAL(src), ZSTR_LEN(src), "'", strlen("'"));
4893+
return zend_string_concat3("'", strlen("'"), ZSTR_VAL(src), ZSTR_LEN(src), "'", strlen("'"));
48944894
}
48954895
/* }}} */
48964896

@@ -5161,7 +5161,6 @@ PHP_PGSQL_API zend_result php_pgsql_convert(PGconn *pg_link, const zend_string *
51615161
zend_string *str;
51625162
/* PostgreSQL ignores \0 */
51635163
str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0);
5164-
/* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */
51655164
ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str),
51665165
Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err);
51675166
if (escape_err) {

ext/pgsql/tests/10pg_convert_9.phpt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ $converted = pg_convert($db, $table_name, $fields);
2424

2525
var_dump($converted);
2626

27+
var_dump(pg_convert($db, $table_name, ['str' => "\\' OR 1=1"]));
28+
2729
/* Invalid values */
2830
try {
2931
$converted = pg_convert($db, $table_name, [5 => 'AAA']);
@@ -51,6 +53,10 @@ try {
5153
} catch (\TypeError $e) {
5254
echo $e->getMessage(), \PHP_EOL;
5355
}
56+
57+
/* standard_conforming_strings = 1 */
58+
pg_query($db, "SET standard_conforming_strings = 1");
59+
var_dump(pg_convert($db, $table_name, ['str' => "\\' OR 1=1"]));
5460
?>
5561
--CLEAN--
5662
<?php
@@ -65,12 +71,20 @@ array(3) {
6571
[""num""]=>
6672
string(4) "1234"
6773
[""str""]=>
68-
string(6) "E'AAA'"
74+
string(5) "'AAA'"
6975
[""bin""]=>
70-
string(12) "E'\\x424242'"
76+
string(11) "'\\x424242'"
77+
}
78+
array(1) {
79+
[""str""]=>
80+
string(13) "'\\'' OR 1=1'"
7181
}
7282
Array of values must be an associative array with string keys
7383
Array of values must be an associative array with string keys
7484
Values must be of type string|int|float|bool|null, array given
7585
Values must be of type string|int|float|bool|null, stdClass given
7686
Values must be of type string|int|float|bool|null, PgSql\Connection given
87+
array(1) {
88+
[""str""]=>
89+
string(12) "'\'' OR 1=1'"
90+
}

ext/pgsql/tests/10pg_convert_json_array.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ pg_query($db, "DROP TABLE IF EXISTS {$table_name_92}");
4242
--EXPECT--
4343
array(2) {
4444
[""textary""]=>
45-
string(51) "E'{"meeting", "lunch", "training", "presentation"}'"
45+
string(50) "'{"meeting", "lunch", "training", "presentation"}'"
4646
[""jsn""]=>
47-
string(22) "E'{"f1":1,"f2":"foo"}'"
47+
string(21) "'{"f1":1,"f2":"foo"}'"
4848
}
4949
OK

ext/pgsql/tests/12pg_insert_9.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ $db = pg_connect($conn_str);
6565
pg_query($db, "DROP TABLE IF EXISTS {$table_name}");
6666
?>
6767
--EXPECTF--
68-
INSERT INTO "table_12pg_insert_9" ("num","str","bin") VALUES (1234,E'AAA',E'\\x424242');
68+
INSERT INTO "table_12pg_insert_9" ("num","str","bin") VALUES (1234,'AAA','\\x424242');
6969
INSERT INTO "table_12pg_insert_9" ("num","str","bin") VALUES ('1234','AAA','BBB');
7070
object(PgSql\Result)#%d (0) {
7171
}

ext/pgsql/tests/14pg_update_9.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ $db = pg_connect($conn_str);
3939
pg_query($db, "DROP TABLE IF EXISTS {$table_name}");
4040
?>
4141
--EXPECT--
42-
UPDATE "table_14pg_update_9" SET "num"=1234,"str"=E'ABC',"bin"=E'\\x58595a' WHERE "num"=1234;
42+
UPDATE "table_14pg_update_9" SET "num"=1234,"str"='ABC',"bin"='\\x58595a' WHERE "num"=1234;
4343
UPDATE "table_14pg_update_9" SET "num"='1234',"str"='ABC',"bin"='XYZ' WHERE "num"='1234';
4444
Ok
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
--TEST--
2+
GHSA-7qpv-r5mr-78m4: SQL injection via E'...' backslash breakout
3+
--CREDITS--
4+
expatch.llc
5+
--EXTENSIONS--
6+
pgsql
7+
--SKIPIF--
8+
<?php include("inc/skipif.inc"); ?>
9+
--FILE--
10+
<?php
11+
include 'inc/config.inc';
12+
13+
$db = pg_connect($conn_str);
14+
15+
pg_query($db, "SET standard_conforming_strings = 1");
16+
17+
pg_query($db, "DROP TABLE IF EXISTS ghsa_7qpv_r5mr_78m4");
18+
pg_query($db, "CREATE TABLE ghsa_7qpv_r5mr_78m4 (id serial primary key, name text, admin boolean)");
19+
pg_query($db, "INSERT INTO ghsa_7qpv_r5mr_78m4 (name, admin) VALUES ('alice', false), ('bob', false)");
20+
21+
$params = ['name' => "zzz' OR 1=1 --"];
22+
echo pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n";
23+
printf("returned: %d\n\n", count(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)));
24+
25+
$params = ['name' => "zzz\\' OR 1=1 --"];
26+
echo pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n";
27+
printf("returned: %d\n\n", count(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)));
28+
29+
$params = ['name' => "john\\', true) --", 'admin' => 'false'];
30+
echo pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n";
31+
pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params);
32+
var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', ['id' => 3])[0]['admin']);
33+
echo "\n";
34+
35+
$params = ['name' => "jake\\', true) --", 'admin' => 'f'];
36+
echo pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_ESCAPE|PGSQL_DML_STRING) . "\n";
37+
pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_EXEC|PGSQL_DML_ESCAPE);
38+
var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', ['id' => 4])[0]['admin']);
39+
40+
?>
41+
--EXPECT--
42+
SELECT * FROM "ghsa_7qpv_r5mr_78m4" WHERE "name"='zzz'' OR 1=1 --';
43+
returned: 0
44+
45+
SELECT * FROM "ghsa_7qpv_r5mr_78m4" WHERE "name"='zzz\'' OR 1=1 --';
46+
returned: 0
47+
48+
INSERT INTO "ghsa_7qpv_r5mr_78m4" ("name","admin") VALUES ('john\'', true) --','f');
49+
string(1) "f"
50+
51+
INSERT INTO "ghsa_7qpv_r5mr_78m4" ("name","admin") VALUES ('jake\'', true) --','f');
52+
string(1) "f"
53+
--CLEAN--
54+
<?php
55+
include('inc/config.inc');
56+
$db = pg_connect($conn_str);
57+
pg_query($db, "DROP TABLE IF EXISTS ghsa_7qpv_r5mr_78m4");
58+
?>

0 commit comments

Comments
 (0)