diff --git a/php-cve-2026-17543.patch b/php-cve-2026-17543.patch new file mode 100644 index 0000000..482c994 --- /dev/null +++ b/php-cve-2026-17543.patch @@ -0,0 +1,185 @@ +From 6a80d9d36a5ea210ea6cc411864c08c71f7417ec Mon Sep 17 00:00:00 2001 +From: Ilija Tovilo +Date: Mon, 27 Jul 2026 16:49:38 +0200 +Subject: [PATCH 2/5] Fix SQL injection in ext-pgsql via E'...' backslash + breakout + +php_pgsql_add_quotes() quotes the string with E'...', but PQescapeStringConn() +does not escape \ unless standard_conforming_strings is off. +PQescapeStringConn() is meant to be used with '', so do that instead. + +Fixes GHSA-7qpv-r5mr-78m4 + +(cherry picked from commit ab048bd83b578119cf81b456526d50498421d617) +(cherry picked from commit eb0de306e0569f8b50065a42f13dd3e2b3729532) +(cherry picked from commit 531196af8ccc43c26ca97aef27b0816e8b4e2f60) +--- + ext/pgsql/pgsql.c | 5 +- + ext/pgsql/tests/10pg_convert_9.phpt | 4 +- + ext/pgsql/tests/10pg_convert_json_array.phpt | 4 +- + ext/pgsql/tests/12pg_insert_9.phpt | 2 +- + ext/pgsql/tests/14pg_update_9.phpt | 2 +- + ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt | 48 ++++++++++++++++++++ + ext/pgsql/tests/bug64609.phpt | 2 +- + ext/pgsql/tests/bug68638.phpt | 2 +- + 8 files changed, 58 insertions(+), 11 deletions(-) + create mode 100644 ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt + +diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c +index 9e06497125..37e6fae705 100644 +--- a/ext/pgsql/pgsql.c ++++ b/ext/pgsql/pgsql.c +@@ -5838,7 +5838,6 @@ static int php_pgsql_add_quotes(zval *src, zend_bool should_free) + assert(Z_TYPE_P(src) == IS_STRING); + assert(should_free == 1 || should_free == 0); + +- smart_str_appendc(&str, 'E'); + smart_str_appendc(&str, '\''); + smart_str_appendl(&str, Z_STRVAL_P(src), Z_STRLEN_P(src)); + smart_str_appendc(&str, '\''); +@@ -6124,8 +6123,8 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con + zend_string *str; + /* PostgreSQL ignores \0 */ + str = zend_string_alloc(Z_STRLEN_P(val) * 2, 0); +- /* better to use PGSQLescapeLiteral since PGescapeStringConn does not handle special \ */ +- ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); ++ ZSTR_LEN(str) = PQescapeStringConn(pg_link, ZSTR_VAL(str), ++ Z_STRVAL_P(val), Z_STRLEN_P(val), &escape_err); + if (escape_err) { + err = 1; + } else { +diff --git a/ext/pgsql/tests/10pg_convert_9.phpt b/ext/pgsql/tests/10pg_convert_9.phpt +index a8395315c6..d956552cdd 100644 +--- a/ext/pgsql/tests/10pg_convert_9.phpt ++++ b/ext/pgsql/tests/10pg_convert_9.phpt +@@ -24,7 +24,7 @@ array(3) { + [""num""]=> + string(4) "1234" + [""str""]=> +- string(6) "E'AAA'" ++ string(5) "'AAA'" + [""bin""]=> +- string(12) "E'\\x424242'" ++ string(11) "'\\x424242'" + } +diff --git a/ext/pgsql/tests/10pg_convert_json_array.phpt b/ext/pgsql/tests/10pg_convert_json_array.phpt +index 960cfd6c99..711d0dff39 100644 +--- a/ext/pgsql/tests/10pg_convert_json_array.phpt ++++ b/ext/pgsql/tests/10pg_convert_json_array.phpt +@@ -30,8 +30,8 @@ if (!pg_insert($db, $table_name_92, $fields)) { + --EXPECT-- + array(2) { + [""textary""]=> +- string(51) "E'{"meeting", "lunch", "training", "presentation"}'" ++ string(50) "'{"meeting", "lunch", "training", "presentation"}'" + [""jsn""]=> +- string(22) "E'{"f1":1,"f2":"foo"}'" ++ string(21) "'{"f1":1,"f2":"foo"}'" + } + OK +diff --git a/ext/pgsql/tests/12pg_insert_9.phpt b/ext/pgsql/tests/12pg_insert_9.phpt +index 275afc55e1..c7e7a98a20 100644 +--- a/ext/pgsql/tests/12pg_insert_9.phpt ++++ b/ext/pgsql/tests/12pg_insert_9.phpt +@@ -24,7 +24,7 @@ var_dump( pg_insert($db, $table_name, $fields, PGSQL_DML_EXEC) ); // Return reso + echo "Ok\n"; + ?> + --EXPECTF-- +-INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES (1234,E'AAA',E'\\x424242'); ++INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES (1234,'AAA','\\x424242'); + INSERT INTO "php_pgsql_test" ("num","str","bin") VALUES ('1234','AAA','BBB'); + resource(%d) of type (pgsql result) + Ok +diff --git a/ext/pgsql/tests/14pg_update_9.phpt b/ext/pgsql/tests/14pg_update_9.phpt +index e3e802b7a2..1e2ae37227 100644 +--- a/ext/pgsql/tests/14pg_update_9.phpt ++++ b/ext/pgsql/tests/14pg_update_9.phpt +@@ -24,6 +24,6 @@ echo pg_update($db, $table_name, $fields, $ids, PGSQL_DML_STRING|PGSQL_DML_ESCAP + echo "Ok\n"; + ?> + --EXPECT-- +-UPDATE "php_pgsql_test" SET "num"=1234,"str"=E'ABC',"bin"=E'\\x58595a' WHERE "num"=1234; ++UPDATE "php_pgsql_test" SET "num"=1234,"str"='ABC',"bin"='\\x58595a' WHERE "num"=1234; + UPDATE "php_pgsql_test" SET "num"='1234',"str"='ABC',"bin"='XYZ' WHERE "num"='1234'; + Ok +diff --git a/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt b/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt +new file mode 100644 +index 0000000000..f7f0dc8aea +--- /dev/null ++++ b/ext/pgsql/tests/GHSA-7qpv-r5mr-78m4.phpt +@@ -0,0 +1,48 @@ ++--TEST-- ++GHSA-7qpv-r5mr-78m4: SQL injection via E'...' backslash breakout ++--CREDITS-- ++expatch.llc ++--SKIPIF-- ++ ++--FILE-- ++ "zzz' OR 1=1 --"]; ++var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)); ++ ++$params = ['name' => "zzz\\' OR 1=1 --"]; ++var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', $params)); ++ ++$params = ['name' => "john\\', true) --", 'admin' => 'false']; ++echo pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_STRING) . "\n"; ++pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params); ++var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', ['id' => 3])[0]['admin']); ++ ++$params = ['name' => "jake\\', true) --", 'admin' => 'f']; ++echo pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_ESCAPE|PGSQL_DML_STRING) . "\n"; ++pg_insert($db, 'ghsa_7qpv_r5mr_78m4', $params, PGSQL_DML_EXEC|PGSQL_DML_ESCAPE); ++var_dump(pg_select($db, 'ghsa_7qpv_r5mr_78m4', ['id' => 4])[0]['admin']); ++ ++?> ++--EXPECT-- ++bool(false) ++bool(false) ++INSERT INTO "ghsa_7qpv_r5mr_78m4" ("name","admin") VALUES ('john\'', true) --','f'); ++string(1) "f" ++INSERT INTO "ghsa_7qpv_r5mr_78m4" ("name","admin") VALUES ('jake\'', true) --','f'); ++string(1) "f" ++--CLEAN-- ++ +diff --git a/ext/pgsql/tests/bug64609.phpt b/ext/pgsql/tests/bug64609.phpt +index e31ec8e019..03cee4aeca 100644 +--- a/ext/pgsql/tests/bug64609.phpt ++++ b/ext/pgsql/tests/bug64609.phpt +@@ -26,5 +26,5 @@ var_dump($converted); + --EXPECT-- + array(1) { + [""a""]=> +- string(5) "E'ok'" ++ string(4) "'ok'" + } +diff --git a/ext/pgsql/tests/bug68638.phpt b/ext/pgsql/tests/bug68638.phpt +index e0701a79f5..336438c86b 100644 +--- a/ext/pgsql/tests/bug68638.phpt ++++ b/ext/pgsql/tests/bug68638.phpt +@@ -32,7 +32,7 @@ pg_query("DROP TABLE $table"); + + ?> + --EXPECT-- +-string(52) "UPDATE "test_68638" SET "value"=E'inf' WHERE "id"=1;" ++string(51) "UPDATE "test_68638" SET "value"='inf' WHERE "id"=1;" + array(2) { + ["id"]=> + string(1) "1" +-- +2.55.0 + diff --git a/php-cve-2026-7260.patch b/php-cve-2026-7260.patch new file mode 100644 index 0000000..41c1853 --- /dev/null +++ b/php-cve-2026-7260.patch @@ -0,0 +1,103 @@ +From 8.2.33, without binary diffs + + +From 69821305b4c6ffba4c58e84105d64f4c540dc5cf Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka +Date: Sun, 3 May 2026 19:26:31 +0200 +Subject: [PATCH 3/5] Fix GHSA-vc5h-9ppw-p5f3: phar circular symlink crash + +Prevents infinite recursion in phar_get_link_source. + +(cherry picked from commit 2e0fa0a44441d74bf8cc4e1ce1c8af9cd4209f52) +(cherry picked from commit c84ffef248fddba29bde47847bd4cd06761e0aab) +(cherry picked from commit 92458605f7f88697973e1183b46296d4c9bf9c46) +--- + .../tests/tar/files/circular_symlinks.tar | Bin 0 -> 10240 bytes + .../tar/files/circular_symlinks_long.tar | Bin 0 -> 215040 bytes + .../tests/tar/files/circular_symlinks_rho.tar | Bin 0 -> 10240 bytes + .../ghsa-vc5h-9ppw-p5f3-symlink-circular.phpt | 27 ++++++++++ + ext/phar/util.c | 51 ++++++++++++++---- + 5 files changed, 67 insertions(+), 11 deletions(-) + create mode 100644 ext/phar/tests/tar/files/circular_symlinks.tar + create mode 100644 ext/phar/tests/tar/files/circular_symlinks_long.tar + create mode 100644 ext/phar/tests/tar/files/circular_symlinks_rho.tar + create mode 100644 ext/phar/tests/tar/ghsa-vc5h-9ppw-p5f3-symlink-circular.phpt + +diff --git a/ext/phar/util.c b/ext/phar/util.c +index 354f0dbaac..639f091f83 100644 +--- a/ext/phar/util.c ++++ b/ext/phar/util.c +@@ -57,30 +57,59 @@ static char *phar_get_link_location(phar_entry_info *entry) /* {{{ */ + } + /* }}} */ + +-phar_entry_info *phar_get_link_source(phar_entry_info *entry) /* {{{ */ ++static phar_entry_info *phar_follow_one_link(phar_entry_info *entry) + { + phar_entry_info *link_entry; + char *link; + +- if (!entry->link) { +- return entry; +- } +- + link = phar_get_link_location(entry); + if (NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), entry->link, strlen(entry->link))) || + NULL != (link_entry = zend_hash_str_find_ptr(&(entry->phar->manifest), link, strlen(link)))) { + if (link != entry->link) { + efree(link); + } +- return phar_get_link_source(link_entry); +- } else { +- if (link != entry->link) { +- efree(link); ++ return link_entry; ++ } ++ ++ if (link != entry->link) { ++ efree(link); ++ } ++ return NULL; ++} ++ ++phar_entry_info *phar_get_link_source(phar_entry_info *entry) ++{ ++ phar_entry_info *slow, *fast; ++ ++ if (!entry->link) { ++ return entry; ++ } ++ ++ /* ++ * Use Floyd's cycle detection algorithm to follow the symlink chain without unbounded ++ * recursion. Each entry has at most one outgoing link, so if a cycle exists the fast pointer ++ * will eventually meet the slow one. Otherwise the fast pointer reaches the end first. ++ */ ++ slow = fast = entry; ++ while (1) { ++ fast = phar_follow_one_link(fast); ++ if (!fast || !fast->link) { ++ return fast; ++ } ++ fast = phar_follow_one_link(fast); ++ if (!fast || !fast->link) { ++ return fast; ++ } ++ ++ /* no need to check slow as it's always behind */ ++ slow = phar_follow_one_link(slow); ++ ++ if (slow == fast) { ++ /* circular symlink chain */ ++ return NULL; + } +- return NULL; + } + } +-/* }}} */ + + /* retrieve a phar_entry_info's current file pointer for reading contents */ + php_stream *phar_get_efp(phar_entry_info *entry, int follow_links) /* {{{ */ +-- +2.55.0 + diff --git a/php-cve-2026-9672.patch b/php-cve-2026-9672.patch new file mode 100644 index 0000000..ee064c4 --- /dev/null +++ b/php-cve-2026-9672.patch @@ -0,0 +1,48 @@ +From e9413a553a530244e46f913d23f6843c25f89d8b Mon Sep 17 00:00:00 2001 +From: Ilija Tovilo +Date: Tue, 28 Jul 2026 02:48:11 +0200 +Subject: [PATCH 1/5] libgd patch for CVE-2026-9672 + +Patch by Pierre Joye (pierrejoye). + +(cherry picked from commit fcd691b377d02285740744bee17c0f298be227d5) +(cherry picked from commit 629e049847acec53b905b4aceba829a0c85f0995) +(cherry picked from commit 3d3d3d57c590a498b0939e78df64a1a5dbac17bc) +--- + ext/gd/libgd/gd_gif_in.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/ext/gd/libgd/gd_gif_in.c b/ext/gd/libgd/gd_gif_in.c +index 1f69723610..e055f90244 100644 +--- a/ext/gd/libgd/gd_gif_in.c ++++ b/ext/gd/libgd/gd_gif_in.c +@@ -450,7 +450,7 @@ LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, i + sd->table[1][i] = i; + } + for (; i < (1<table[0][i] = sd->table[1][0] = 0; ++ sd->table[0][i] = sd->table[1][i] = 0; + + sd->sp = sd->stack; + +@@ -494,6 +494,8 @@ LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, i + + if (count != 0) + return -2; ++ ++ return -2; + } + + incode = code; +@@ -560,7 +562,7 @@ ReadImage(gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap) + int v; + int xpos = 0, ypos = 0, pass = 0; + int i; +- LZW_STATIC_DATA sd; ++ LZW_STATIC_DATA sd = {0}; + + + /* +-- +2.55.0 + diff --git a/php-gh22643.patch b/php-gh22643.patch new file mode 100644 index 0000000..6889023 --- /dev/null +++ b/php-gh22643.patch @@ -0,0 +1,142 @@ +From bbb027263bf9ec3d0ceba01d55307fe728b58f19 Mon Sep 17 00:00:00 2001 +From: Ilija Tovilo +Date: Tue, 7 Jul 2026 20:32:58 +0200 +Subject: [PATCH 4/5] Fix leak on double DatePeriod::__construct() call + +Closes GH-22643 + +(cherry picked from commit 2d86f8cf489ea5410edd19d301c1aefe330599ab) +(cherry picked from commit 591e613e1cf6cc10d84df2c799ede593b7371bc1) +(cherry picked from commit 3f74c8a960c866da1b016bac2fc9e476955fbbc7) +--- + NEWS | 5 ++++ + ext/date/php_date.c | 25 ++++++++++++++++--- + .../DatePeriod_double_constructor_call.phpt | 14 +++++++++++ + 3 files changed, 40 insertions(+), 4 deletions(-) + create mode 100644 ext/date/tests/DatePeriod_double_constructor_call.phpt + +diff --git a/NEWS b/NEWS +index 328fd6d6de..cf9a15804d 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,11 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.2.33 ++ ++- Date: ++ . Fixed leak on double DatePeriod::__construct() call. (ilutov) ++ + Backported from 8.2.32 + + - OpenSSL: +diff --git a/ext/date/php_date.c b/ext/date/php_date.c +index e1a427c5ca..15f13d423f 100644 +--- a/ext/date/php_date.c ++++ b/ext/date/php_date.c +@@ -4670,9 +4670,24 @@ static int date_period_initialize(timelib_time **st, timelib_time **et, timelib_ + return retval; + } /* }}} */ + +-/* {{{ proto DatePeriod::__construct(DateTime $start, DateInterval $interval, int recurrences|DateTime $end) +- Creates new DatePeriod object. +-*/ ++static void date_period_reset(php_period_obj *period_obj) ++{ ++ if (period_obj->start) { ++ timelib_time_dtor(period_obj->start); ++ } ++ if (period_obj->current) { ++ timelib_time_dtor(period_obj->current); ++ } ++ if (period_obj->end) { ++ timelib_time_dtor(period_obj->end); ++ } ++ if (period_obj->interval) { ++ timelib_rel_time_dtor(period_obj->interval); ++ } ++ memset(period_obj, 0, XtOffsetOf(php_period_obj, std)); ++} ++ ++/* {{{ Creates new DatePeriod object. */ + PHP_METHOD(DatePeriod, __construct) + { + php_period_obj *dpobj; +@@ -4696,7 +4711,7 @@ PHP_METHOD(DatePeriod, __construct) + } + + dpobj = Z_PHPPERIOD_P(ZEND_THIS); +- dpobj->current = NULL; ++ date_period_reset(dpobj); + + if (isostr) { + date_period_initialize(&(dpobj->start), &(dpobj->end), &(dpobj->interval), &recurrences, isostr, isostr_len); +@@ -4718,6 +4733,8 @@ PHP_METHOD(DatePeriod, __construct) + } + dpobj->start_ce = date_ce_date; + } else { ++ DATE_CHECK_INITIALIZED(Z_PHPINTERVAL_P(interval)->initialized, Z_OBJCE_P(interval)); ++ + /* init */ + php_interval_obj *intobj = Z_PHPINTERVAL_P(interval); + +diff --git a/ext/date/tests/DatePeriod_double_constructor_call.phpt b/ext/date/tests/DatePeriod_double_constructor_call.phpt +new file mode 100644 +index 0000000000..551d272728 +--- /dev/null ++++ b/ext/date/tests/DatePeriod_double_constructor_call.phpt +@@ -0,0 +1,14 @@ ++--TEST-- ++Double DatePeriod::__construct() call ++--FILE-- ++__construct($start, $interval, 1); ++ ++?> ++===DONE=== ++--EXPECT-- ++===DONE=== +-- +2.55.0 + +From 78793e0bc0a244e228f9e9e9d2a28f48c941f223 Mon Sep 17 00:00:00 2001 +From: Remi Collet +Date: Thu, 30 Jul 2026 09:07:49 +0200 +Subject: [PATCH 5/5] NEWS from 8.2.33 + +(cherry picked from commit 11b947ce14c7fcc77adc740349add4c5c8967a62) +(cherry picked from commit 9aa1f1c43308560bb5dc97cbf8995c66108aeef0) +--- + NEWS | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/NEWS b/NEWS +index cf9a15804d..6e0a091363 100644 +--- a/NEWS ++++ b/NEWS +@@ -6,6 +6,17 @@ Backported from 8.2.33 + - Date: + . Fixed leak on double DatePeriod::__construct() call. (ilutov) + ++- GD: ++ . Upgrade libgd. (CVE-2026-9672) (Pierre Joye) ++ ++- PGSQL: ++ . Fixed GHSA-7qpv-r5mr-78m4 (SQL injection via E'...' backslash breakout). ++ (CVE-2026-17543) (ilutov) ++ ++- Phar: ++ . Fixed GHSA-vc5h-9ppw-p5f3 (Crash via recursive symlinks). (CVE-2026-7260) ++ (Jakub Zelenka) ++ + Backported from 8.2.32 + + - OpenSSL: +-- +2.55.0 + diff --git a/php.spec b/php.spec index 24430d7..acfd326 100644 --- a/php.spec +++ b/php.spec @@ -60,7 +60,7 @@ Summary: PHP scripting language for creating dynamic web sites Name: php Version: %{upver}%{?rcver:~%{rcver}} -Release: 5%{?dist} +Release: 6%{?dist} # All files licensed under PHP version 3.01, except # Zend is licensed under Zend # TSRM is licensed under BSD @@ -146,8 +146,13 @@ Patch233: php-cve-2026-7262.patch Patch234: php-cve-2026-6735.patch Patch235: php-cve-2026-7568.patch Patch236: php-cve-2026-7258.patch -# from 8.2.32 +# From 8.2.32 Patch237: php-cve-2026-14355.patch +# From 8.2.33 +Patch238: php-cve-2026-9672.patch +Patch239: php-cve-2026-17543.patch +Patch240: php-cve-2026-7260.patch +Patch241: php-gh22643.patch # Fixes for tests (300+) # Factory is droped from system tzdata @@ -795,6 +800,10 @@ in pure PHP. %patch -P235 -p1 -b .cve7268 %patch -P236 -p1 -b .cve7258 %patch -P237 -p1 -b .cve14355 +%patch -P238 -p1 -b .cve9672 +%patch -P239 -p1 -b .cve17543 +%patch -P240 -p1 -b .cve7260 +%patch -P241 -p1 -b .gh22643 # Fixes for tests %patch -P300 -p1 -b .datetests @@ -1584,6 +1593,13 @@ systemctl try-restart php-fpm.service >/dev/null 2>&1 || : %changelog +* Wed Aug 5 2026 Remi Collet - 7.4.33-6 +- Fix leak on double DatePeriod::__construct() call +- Fixed SQL injection via E'...' backslash breakout + CVE-2026-17543 +- Fixed GHSA-vc5h-9ppw-p5f3 Crash via recursive symlinks + CVE-2026-7260 + * Fri Jul 10 2026 Remi Collet - 7.4.33-5 - Fix Memory corruption (zend_mm_heap corrupted) in openssl_encrypt with AES-WRAP-PAD CVE-2026-14355