diff options
47 files changed, 13400 insertions, 605 deletions
@@ -1,13 +1,19 @@ -===== 7.4.28 (2022-02-17) +===== 7.4.33-24 (2025-07-03) $ grep -ar 'Tests failed' /var/lib/mock/*/build.log -/var/lib/mock/scl74el7x/build.log:Tests failed : 0 +/var/lib/mock/scl74el8a/build.log:Tests failed : 0 /var/lib/mock/scl74el8x/build.log:Tests failed : 0 +/var/lib/mock/scl74el9a/build.log:Tests failed : 0 /var/lib/mock/scl74el9x/build.log:Tests failed : 0 -/var/lib/mock/scl74fc34x/build.log:Tests failed : 0 -/var/lib/mock/scl74fc35x/build.log:Tests failed : 0 -/var/lib/mock/scl74fc36x/build.log:Tests failed : 0 +/var/lib/mock/scl74el10a/build.log:Tests failed : 0 +/var/lib/mock/scl74el10x/build.log:Tests failed : 0 +/var/lib/mock/scl80fc40a/build.log:Tests failed : 0 +/var/lib/mock/scl80fc40x/build.log:Tests failed : 0 +/var/lib/mock/scl80fc41a/build.log:Tests failed : 0 +/var/lib/mock/scl80fc41x/build.log:Tests failed : 0 +/var/lib/mock/scl74fc42a/build.log:Tests failed : 0 +/var/lib/mock/scl74fc42x/build.log:Tests failed : 0 @@ -14,3 +14,7 @@ %@SCL@__php @BINDIR@/php +%@SCL@__phpize @BINDIR@/phpize + +%@SCL@__phpconfig @BINDIR@/php-config + diff --git a/php-7.0.7-curl.patch b/php-7.0.7-curl.patch deleted file mode 100644 index 218db98..0000000 --- a/php-7.0.7-curl.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff -up php-7.0.7RC1/ext/curl/interface.c.curltls php-7.0.7RC1/ext/curl/interface.c ---- php-7.0.7RC1/ext/curl/interface.c.curltls 2016-05-10 17:28:33.000000000 +0200 -+++ php-7.0.7RC1/ext/curl/interface.c 2016-05-12 07:43:00.900419946 +0200 -@@ -1257,7 +1257,11 @@ PHP_MINIT_FUNCTION(curl) - - #if LIBCURL_VERSION_NUM >= 0x072200 /* Available since 7.34.0 */ - REGISTER_CURL_CONSTANT(CURLOPT_LOGIN_OPTIONS); -+#endif - -+#if LIBCURL_VERSION_NUM >= 0x071300 /* Available since 7.19.0 (in upstream curl 7.34) -+ backported in RHEL-7 curl-7.29.0-16.el7 rhbz#1012136 -+ backported in RHEL-6 curl-7.19.7-43.el6 rhbz#1036789 */ - REGISTER_CURL_CONSTANT(CURL_SSLVERSION_TLSv1_0); - REGISTER_CURL_CONSTANT(CURL_SSLVERSION_TLSv1_1); - REGISTER_CURL_CONSTANT(CURL_SSLVERSION_TLSv1_2); diff --git a/php-7.2.0-includedir.patch b/php-7.2.0-includedir.patch index 7a42cd6..2202026 100644 --- a/php-7.2.0-includedir.patch +++ b/php-7.2.0-includedir.patch @@ -1,6 +1,6 @@ --- php-7.2.0/configure.ac.includedir +++ php-7.2.0/configure.ac -@@ -1230,7 +1230,7 @@ +@@ -1283,7 +1283,7 @@ EXPANDED_DATADIR=$datadir EXPANDED_PHP_CONFIG_FILE_PATH=`eval echo "$PHP_CONFIG_FILE_PATH"` EXPANDED_PHP_CONFIG_FILE_SCAN_DIR=`eval echo "$PHP_CONFIG_FILE_SCAN_DIR"` diff --git a/php-7.2.0-libdb.patch b/php-7.2.0-libdb.patch index ca36d1a..11acd17 100644 --- a/php-7.2.0-libdb.patch +++ b/php-7.2.0-libdb.patch @@ -1,7 +1,7 @@ diff -up php-7.2.0alpha0/ext/dba/config.m4.libdb php-7.2.0alpha0/ext/dba/config.m4 --- php-7.2.0alpha0/ext/dba/config.m4.libdb 2017-05-29 08:56:06.000000000 +0200 +++ php-7.2.0alpha0/ext/dba/config.m4 2017-05-29 09:13:52.014823282 +0200 -@@ -346,61 +346,13 @@ if test "$PHP_DB4" != "no"; then +@@ -375,61 +375,13 @@ if test "$PHP_DB4" != "no"; then dbdp4="/usr/local/BerkeleyDB.4." dbdp5="/usr/local/BerkeleyDB.5." for i in $PHP_DB4 ${dbdp5}1 ${dbdp5}0 ${dbdp4}8 ${dbdp4}7 ${dbdp4}6 ${dbdp4}5 ${dbdp4}4 ${dbdp4}3 ${dbdp4}2 ${dbdp4}1 ${dbdp}0 /usr/local /usr; do @@ -68,7 +68,7 @@ diff -up php-7.2.0alpha0/ext/dba/config.m4.libdb php-7.2.0alpha0/ext/dba/config. diff -up php-7.2.0alpha0/ext/dba/dba.c.libdb php-7.2.0alpha0/ext/dba/dba.c --- php-7.2.0alpha0/ext/dba/dba.c.libdb 2017-05-29 09:16:15.736628202 +0200 +++ php-7.2.0alpha0/ext/dba/dba.c 2017-05-29 09:16:20.494654746 +0200 -@@ -53,6 +53,10 @@ +@@ -51,6 +51,10 @@ #include "php_tcadb.h" #include "php_lmdb.h" diff --git a/php-7.2.0-oci8conf.patch b/php-7.2.0-oci8conf.patch index 0ad16a1..3bf1c2e 100644 --- a/php-7.2.0-oci8conf.patch +++ b/php-7.2.0-oci8conf.patch @@ -1,7 +1,7 @@ diff -up ./ext/ldap/php_ldap.h.remi-oci8 ./ext/ldap/php_ldap.h --- ./ext/ldap/php_ldap.h.remi-oci8 2017-06-20 15:45:35.000000000 +0200 +++ ./ext/ldap/php_ldap.h 2017-06-20 16:55:01.640203868 +0200 -@@ -27,7 +27,7 @@ +@@ -25,7 +25,7 @@ #include <lber.h> #endif @@ -10,26 +10,4 @@ diff -up ./ext/ldap/php_ldap.h.remi-oci8 ./ext/ldap/php_ldap.h extern zend_module_entry ldap_module_entry; #define ldap_module_ptr &ldap_module_entry -diff -up ./ext/oci8/config.m4.remi-oci8 ./ext/oci8/config.m4 ---- ./ext/oci8/config.m4.remi-oci8 2017-06-20 15:45:39.000000000 +0200 -+++ ./ext/oci8/config.m4 2017-06-20 16:55:01.640203868 +0200 -@@ -372,6 +372,7 @@ if test "$PHP_OCI8" != "no"; then - - dnl Header directory for Instant Client SDK RPM install - OCISDKRPMINC=`echo "$PHP_OCI8_INSTANT_CLIENT" | $PHP_OCI8_SED -e 's!^/usr/lib/oracle/\(.*\)/client\('${PHP_OCI8_IC_LIBDIR_SUFFIX}'\)*/lib[/]*$!/usr/include/oracle/\1/client\2!'` -+ OCISDKRPMINC=`echo "$PHP_OCI8_INSTANT_CLIENT" | $PHP_OCI8_SED -e 's!^/usr/\(lib64\|lib\)/oracle/\(.*\)/\(client64\|client\)/lib[/]*$!/usr/include/oracle/\2/\3!'` - - dnl Header directory for Instant Client SDK zip file install - OCISDKZIPINC=$PHP_OCI8_INSTANT_CLIENT/sdk/include -diff -up ./ext/pdo_oci/config.m4.remi-oci8 ./ext/pdo_oci/config.m4 ---- ./ext/pdo_oci/config.m4.remi-oci8 2017-06-20 16:55:01.640203868 +0200 -+++ ./ext/pdo_oci/config.m4 2017-06-20 17:16:03.053538358 +0200 -@@ -93,7 +93,7 @@ if test "$PHP_PDO_OCI" != "no"; then - - AC_MSG_CHECKING([for oci.h]) - dnl Header directory for Instant Client SDK RPM install -- OCISDKRPMINC=`echo "$PDO_OCI_LIB_DIR" | $PHP_PDO_OCI_SED -e 's!^\(.*\)/lib/oracle/\(.*\)/\('${PDO_OCI_CLIENT_DIR}'\)/lib[/]*$!\1/include/oracle/\2/\3!'` -+ OCISDKRPMINC=`echo "$PDO_OCI_LIB_DIR" | $PHP_PDO_OCI_SED -e 's!^\(.*\)/\(lib64\|lib\)/oracle/\(.*\)/\('${PDO_OCI_CLIENT_DIR}'\)/lib[/]*$!\1/include/oracle/\3/\4!'` - - dnl Header directory for manual installation - OCISDKMANINC=`echo "$PDO_OCI_LIB_DIR" | $PHP_PDO_OCI_SED -e 's!^\(.*\)/lib[/]*$!\1/include!'` + diff --git a/php-7.3.3-systzdata-v19.patch b/php-7.3.3-systzdata-v19.patch index 866729b..9dde92f 100644 --- a/php-7.3.3-systzdata-v19.patch +++ b/php-7.3.3-systzdata-v19.patch @@ -5,6 +5,7 @@ Add support for use of the system timezone database, rather than embedding a copy. Discussed upstream but was not desired. History: +r20: fix possible buffer overflow r19: retrieve tzdata version from /usr/share/zoneinfo/tzdata.zi r18: adapt for autotool change in 7.3.3RC1 r17: adapt for timelib 2018.01 (in 7.3.2RC1) @@ -452,7 +453,7 @@ index 020da3135e..12e68ef043 100644 + size_t n; + char *data, *p; + -+ data = malloc(3 * sysdb->index_size + 7); ++ data = malloc(3 * sysdb->index_size + sizeof(FAKE_HEADER) - 1); + + p = mempcpy(data, FAKE_HEADER, sizeof(FAKE_HEADER) - 1); + diff --git a/php-7.4.26-openssl3.patch b/php-7.4.26-openssl3.patch index 9952f34..aec6b96 100644 --- a/php-7.4.26-openssl3.patch +++ b/php-7.4.26-openssl3.patch @@ -1,7 +1,7 @@ -From f7da6fd2d5d2160ef67e0bee3ad76f28d7b71983 Mon Sep 17 00:00:00 2001 +From d040474c7c9d6d94e10c6757e5f100ecacabf19f Mon Sep 17 00:00:00 2001 From: Remi Collet <remi@php.net> Date: Sun, 8 Aug 2021 17:38:30 +0200 -Subject: [PATCH 01/26] minimal fix for openssl 3.0 (#7002) +Subject: [PATCH 01/27] minimal fix for openssl 3.0 (#7002) (cherry picked from commit a0972deb0f441fc7991001cb51efc994b70a3b51) --- @@ -23,12 +23,12 @@ index aa819be422..9cb643601c 100644 REGISTER_LONG_CONSTANT("OPENSSL_PKCS1_OAEP_PADDING", RSA_PKCS1_OAEP_PADDING, CONST_CS|CONST_PERSISTENT); -- -2.31.1 +2.41.0 -From 557f613efc86158ef65200f2c994c28bad257850 Mon Sep 17 00:00:00 2001 +From ef7710bd3a3ce04ddada7221bf7ba9410d1a0fe8 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 09:41:39 +0200 -Subject: [PATCH 02/26] ignore deprecated +Subject: [PATCH 02/27] ignore deprecated --- ext/openssl/openssl.c | 2 ++ @@ -73,12 +73,12 @@ index 348831189b..b2cb6164bd 100644 --EXPECT-- bool(true) -- -2.31.1 +2.41.0 -From c83d7444d35e4b246f84c1adc1353f75fbd4b44c Mon Sep 17 00:00:00 2001 +From c421e4e98b35c1744f784c05ffd34583fbe96c37 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 09:46:07 +0200 -Subject: [PATCH 03/26] Reduce security level in some OpenSSL tests +Subject: [PATCH 03/27] Reduce security level in some OpenSSL tests This allows tests using older protocols and algorithms to work under OpenSSL 3. @@ -345,12 +345,12 @@ index c1aaa04919..84a137b5f4 100644 phpt_wait(); -- -2.31.1 +2.41.0 -From c9a9ef0d62c19bd2b3f89772c5a800781b88d53c Mon Sep 17 00:00:00 2001 +From dfbbf02d413db19dd3337b5b60c55eb974ebb2b7 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 09:57:40 +0200 -Subject: [PATCH 04/26] Adjust some tests for whitespace differences in OpenSSL +Subject: [PATCH 04/27] Adjust some tests for whitespace differences in OpenSSL 3 A trailing newline is no longer present in OpenSSL 3. @@ -453,12 +453,12 @@ index b80c1f71f1..38915157f3 100644 string(7) "CA:TRUE" } -- -2.31.1 +2.41.0 -From dabea364207985e67e138e70106b6977952c2729 Mon Sep 17 00:00:00 2001 +From a8e511110696e83f728faee9294798351c84fb85 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 11:55:47 +0200 -Subject: [PATCH 05/26] Use different cipher in openssl_seal() test +Subject: [PATCH 05/27] Use different cipher in openssl_seal() test RC4 is insecure and not supported in newer versions. @@ -518,12 +518,12 @@ index 111bf6f094..588efa707b 100644 Warning: openssl_seal(): not a public key (2th member of pubkeys) in %s on line %d bool(false) -- -2.31.1 +2.41.0 -From 55123a11413921e991929fdd3cdab3b855617d11 Mon Sep 17 00:00:00 2001 +From 54f6bd9814a09d57b80933b1cedfd4266286bb9a Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 11:58:46 +0200 -Subject: [PATCH 06/26] Don't test legacy algorithms in SPKI tests +Subject: [PATCH 06/27] Don't test legacy algorithms in SPKI tests MD4 and RMD160 may not be available on newer OpenSSL versions. @@ -659,12 +659,12 @@ index c760d0cb83..35badcda37 100644 -bool(true) -bool(false) -- -2.31.1 +2.41.0 -From dace8e9ff28889d110cc4617b91caca0d722238f Mon Sep 17 00:00:00 2001 +From 9f5fa8ab4e8d5ba1e9e12eac956ba658e2047b93 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 12:48:02 +0200 -Subject: [PATCH 07/26] Only report provided ciphers in +Subject: [PATCH 07/27] Only report provided ciphers in openssl_get_cipher_methods() With OpenSSL 3 ciphers may be registered, but not provided. Make @@ -749,12 +749,12 @@ index 7926b475e7..29d64171d9 100644 #endif -- -2.31.1 +2.41.0 -From 514a7e50e1bdc5d409c3d66c1593f0ce1a859b8e Mon Sep 17 00:00:00 2001 +From d03ccc6933b4e585980458455b17cb384a3e5ab6 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 12:05:02 +0200 -Subject: [PATCH 08/26] Avoid RC4 use in another test +Subject: [PATCH 08/27] Avoid RC4 use in another test (cherry picked from commit 503146aa87e48f075f47a093ed7868e323814a66) --- @@ -788,12 +788,12 @@ index d564bcf8e8..e19f07e7b1 100644 ?> --EXPECTF-- -- -2.31.1 +2.41.0 -From bcc416e4449c78361eefec90c6339839cc198bde Mon Sep 17 00:00:00 2001 +From cafc815c45cdc12ab559c2e9e1c1af0500ca0ca5 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Thu, 5 Aug 2021 11:50:11 +0200 -Subject: [PATCH 09/26] Relax error check +Subject: [PATCH 09/27] Relax error check The precise error is version-dependent, just check that there is some kind of error reported. @@ -823,12 +823,12 @@ index 327c916688..3f319b4b24 100644 -error:%s:key size too small +bool(true) -- -2.31.1 +2.41.0 -From 269c9b3cff4808d7cb62dde957429c26b7d2ac46 Mon Sep 17 00:00:00 2001 +From 736d5d5eac86df2e5710111f90a0196ce9335c60 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Thu, 5 Aug 2021 14:59:16 +0200 -Subject: [PATCH 10/26] Add test for openssl_dh_compute_key() +Subject: [PATCH 10/27] Add test for openssl_dh_compute_key() This function was not tested at all :( @@ -874,12 +874,12 @@ index 0000000000..8730f4b57d +--EXPECT-- +b0049944fa5d36f364dd02e675dde50f8c2d67481c5cf0fe2f248d383eec1d38c23d5ed2644fbef2676bcd6ce148361ca82619c8f93e10506cb89d0a1bdaa0f0bc6f68cef0f7cb6d97d43e8dda3c7a5c5a98ebd2342a605ce530fd46a0602d28d4afc48e92088d0bc42194ca8682a85317f812d81b86cd284eed405df2f76aae84ccd560856e8a3d0ce4f591394bca02eb8a1984ebb41bb19714fb8b579bcafd36a9051d51d075f66229893289d8a0c918bfd222f17803cc532d2cf93bb2a567953323ca409beb3237faae9c6fdfc671594324953badd07dd4770ee09fd19f90045654c5709e92aa614b83594c2f62a8bc3c7e786e54bc1259a0a737c70dd4cc -- -2.31.1 +2.41.0 -From 6f81d18232ee8e17c2f299dc3008727b420ce114 Mon Sep 17 00:00:00 2001 +From 95ede22356cdcfb4053850437eb3bb59f8190e5c Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 14:54:59 +0200 -Subject: [PATCH 11/26] Use different algorithm in pkcs7 tests +Subject: [PATCH 11/27] Use different algorithm in pkcs7 tests The default of OPENSSL_CIPHER_RC2_40 is no longer (non-legacy) supported in OpenSSL 3, specify a newer cipher instead. @@ -965,12 +965,12 @@ index f823462f9e..e38a006d0c 100644 bool(true) true -- -2.31.1 +2.41.0 -From 9f9df4446699cd09cd70046f8bee66272aca2dac Mon Sep 17 00:00:00 2001 +From 1942dc87aaa0e473ec74d5be68866b327a2dd62b Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Thu, 5 Aug 2021 17:07:44 +0200 -Subject: [PATCH 12/26] Use larger key size for DSA/DH tests +Subject: [PATCH 12/27] Use larger key size for DSA/DH tests OpenSSL 3 validates allowed sizes strictly, pick minimum sizes that are supported. @@ -1014,12 +1014,12 @@ index c5f5575e2c..7beb020a4c 100644 ?> --EXPECTF-- -- -2.31.1 +2.41.0 -From 261db4fde8b2de3d0b39cac5d376ef425aad7ef2 Mon Sep 17 00:00:00 2001 +From b8904668632df0eadb5f24b365f1b2189f6694c7 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Wed, 4 Aug 2021 13:54:26 +0200 -Subject: [PATCH 13/26] Skip some tests if cipher not available +Subject: [PATCH 13/27] Skip some tests if cipher not available (cherry picked from commit d23a8b33abc3cd7e516563877a3f698b7a94ac10) --- @@ -1084,22 +1084,20 @@ index 4175e703d2..e846b42e78 100644 +bool(true) NULL -- -2.31.1 +2.41.0 -From 93c0873333a8b257edb082d3f106fdef67495c44 Mon Sep 17 00:00:00 2001 +From 1f611e84806818b53cda70708f7eb6d1915b2887 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Fri, 6 Aug 2021 10:35:49 +0200 -Subject: [PATCH 14/26] Generate pkcs12_read test inputs on the fly +Subject: [PATCH 14/27] Generate pkcs12_read test inputs on the fly The old p12_with_extra_certs.p12 file uses an unsupported something. (cherry picked from commit 5843ba518cfb9ac6ae6d6a69629239cbf77d4cfb) --- - ext/openssl/tests/bug74022_2.phpt | 10 ++-- - .../tests/openssl_pkcs12_read_basic.phpt | 46 ++++++++++-------- - ext/openssl/tests/p12_with_extra_certs.p12 | Bin 3205 -> 0 bytes - 3 files changed, 31 insertions(+), 25 deletions(-) - delete mode 100644 ext/openssl/tests/p12_with_extra_certs.p12 + ext/openssl/tests/bug74022_2.phpt | 10 ++-- + .../tests/openssl_pkcs12_read_basic.phpt | 46 ++++++++++--------- + 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/ext/openssl/tests/bug74022_2.phpt b/ext/openssl/tests/bug74022_2.phpt index 07cb683274..4220149db2 100644 @@ -1188,14 +1186,13 @@ index b81b4d9dac..8cb2b41fd7 100644 -----END CERTIFICATE----- " } - -- -2.31.1 +2.41.0 -From 64bedf19c7caa47193c22f6fbb134574eb0cf2dd Mon Sep 17 00:00:00 2001 +From 770edaa92bbf183455a60b902b12fc33ff56e95a Mon Sep 17 00:00:00 2001 From: Jakub Zelenka <bukka@php.net> Date: Sun, 8 Aug 2021 20:54:46 +0100 -Subject: [PATCH 15/26] Make CertificateGenerator not dependent on external +Subject: [PATCH 15/27] Make CertificateGenerator not dependent on external config in OpenSSL 3.0 (cherry picked from commit c90c9c7545427d9d35cbac45c4ec896f54619744) @@ -1248,12 +1245,12 @@ index b409376058..6fe9b4e9a8 100644 file_put_contents($file, $certText . PHP_EOL . $keyText); } finally { -- -2.31.1 +2.41.0 -From f2c252b9a083c01eff3f665a406efe5b44f323a3 Mon Sep 17 00:00:00 2001 +From 1234e56683d3f040eb98f7aabf745cf7baccc0e4 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Tue, 10 Aug 2021 11:50:18 +0200 -Subject: [PATCH 16/26] Fork openssl_error_string() test for OpenSSL +Subject: [PATCH 16/27] Fork openssl_error_string() test for OpenSSL The used error code differ signficantly, so use a separate test file. @@ -1284,12 +1281,12 @@ index cdf558e9a5..f9f0e7062f 100644 <?php // helper function to check openssl errors -- -2.31.1 +2.41.0 -From dc1751ad95ebb04e756809e837feb9aac7a2fefe Mon Sep 17 00:00:00 2001 +From 49c081a3d22d621a3024d7ea4c32f0350228c60b Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Sun, 8 Aug 2021 17:39:06 +0200 -Subject: [PATCH 17/26] Use OpenSSL NCONF APIs (#7337) +Subject: [PATCH 17/27] Use OpenSSL NCONF APIs (#7337) (cherry picked from commit 94bc5fce261a4a56a545bdfb25d5c2452a07de08) --- @@ -1462,12 +1459,12 @@ index e0b3772a29..666616e7c5 100644 { php_openssl_store_errors(); -- -2.31.1 +2.41.0 -From df4e7dcc8121c444ff315e31d06182f164e686ed Mon Sep 17 00:00:00 2001 +From 95dd07c54542ac48cf7d43392f61b0423b04fe63 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka <bukka@php.net> Date: Sun, 12 Sep 2021 20:30:02 +0100 -Subject: [PATCH 18/26] Make OpenSSL tests less dependent on system config +Subject: [PATCH 18/27] Make OpenSSL tests less dependent on system config It fixes dependencies on system config if running tests with OpenSSL 3.0 @@ -1559,12 +1556,12 @@ index 41567e9b32..6c09238003 100644 $keyFailed = openssl_pkey_new($argsFailed); -- -2.31.1 +2.41.0 -From 03f65a015256933426d2c87b399a4c4620b4c85c Mon Sep 17 00:00:00 2001 +From 6167fdd70654ff63a6a759cffbbdb5468e5c517a Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Fri, 6 Aug 2021 11:15:18 +0200 -Subject: [PATCH 19/26] Do not special case export of EC keys +Subject: [PATCH 19/27] Do not special case export of EC keys All other private keys are exported in PKCS#8 format, while EC keys use traditional format. Switch them to use PKCS#8 format as @@ -1578,10 +1575,9 @@ As the OpenSSL docs say: (cherry picked from commit f2d3e75933fa155a5281c824263780dbc660ecb1) --- - UPGRADING | 4 +++ ext/openssl/openssl.c | 36 ++++--------------- .../tests/openssl_pkey_export_basic.phpt | 6 +++- - 3 files changed, 15 insertions(+), 31 deletions(-) + 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 666616e7c5..4af0942209 100644 @@ -1655,12 +1651,12 @@ index d71f8da9a3..47a82d7873 100644 bool(true) resource(%d) of type (OpenSSL key) -- -2.31.1 +2.41.0 -From 038c33feab7e6138f7977224897118dbb8059a55 Mon Sep 17 00:00:00 2001 +From 94c952911ba9b53470056f0e679c842311e601e5 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Thu, 5 Aug 2021 10:29:50 +0200 -Subject: [PATCH 20/26] Use EVP_PKEY APIs for key generation +Subject: [PATCH 20/27] Use EVP_PKEY APIs for key generation Use high level API instead of deprecated low level API. @@ -1915,12 +1911,12 @@ index 4af0942209..588aa3902f 100644 /* }}} */ -- -2.31.1 +2.41.0 -From cc5ad532e6672ac74007caa83f2fb7796f69510b Mon Sep 17 00:00:00 2001 +From 3e896d255c644a0d1c27a6c19e074b43bfc4c5ac Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Mon, 9 Aug 2021 10:26:12 +0200 -Subject: [PATCH 21/26] Extract EC key initialization +Subject: [PATCH 21/27] Extract EC key initialization (cherry picked from commit 14d7c7e9aee5ab55a92ddc626b7b81c130ea7618) --- @@ -2186,12 +2182,12 @@ index 588aa3902f..5671311508 100644 } } -- -2.31.1 +2.41.0 -From 7c3f98fb5000b95419848b3b2519b677e8852f3f Mon Sep 17 00:00:00 2001 +From 9ac7bdc3d7eb104d7d95e2b1aa4e2b631f45051b Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Mon, 9 Aug 2021 12:01:35 +0200 -Subject: [PATCH 22/26] Test calculation of EC public key from private key +Subject: [PATCH 22/27] Test calculation of EC public key from private key (cherry picked from commit 246698671f941b2034518ab04f35009b2da77bb1) --- @@ -2229,12 +2225,12 @@ index 6c09238003..ecc34a3330 100644 NULL resource(%d) of type (OpenSSL key) -- -2.31.1 +2.41.0 -From 3b17fa3a6a34fd169c34e3d1dbb315c4c691c649 Mon Sep 17 00:00:00 2001 +From d8ffb2117e6b986cb4a5b8e5c0cf5c74af8a32fc Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Mon, 9 Aug 2021 11:12:20 +0200 -Subject: [PATCH 23/26] Use param API for creating EC keys +Subject: [PATCH 23/27] Use param API for creating EC keys Rather than the deprecated low level APIs. @@ -2386,12 +2382,12 @@ index 5671311508..5a76057c5f 100644 #endif -- -2.31.1 +2.41.0 -From 76efdaf49ccfb4462ce9493c04b5542570f72907 Mon Sep 17 00:00:00 2001 +From c1047e5c4bf6919ab9600318721d4fa6cbebb40b Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Mon, 9 Aug 2021 14:19:33 +0200 -Subject: [PATCH 24/26] Extract public key portion via PEM roundtrip +Subject: [PATCH 24/27] Extract public key portion via PEM roundtrip The workaround with cloning the X509_REQ no longer works in OpenSSL 3. Instead extract the public key portion by round @@ -2476,12 +2472,12 @@ index 5a76057c5f..00ab6dc73a 100644 if (tpubkey == NULL) { -- -2.31.1 +2.41.0 -From 134c4303f6ddca2553dadfe4e56808ef00ba39dd Mon Sep 17 00:00:00 2001 +From ee274b8bb13e8f9a3df79550be2ea3e4538c6326 Mon Sep 17 00:00:00 2001 From: Nikita Popov <nikita.ppv@gmail.com> Date: Tue, 10 Aug 2021 12:17:17 +0200 -Subject: [PATCH 25/26] Switch dh_param handling to EVP_PKEY API +Subject: [PATCH 25/27] Switch dh_param handling to EVP_PKEY API (cherry picked from commit ef787bae242fdd2e72625bbce6ab4ca466b1ef59) --- @@ -2546,12 +2542,12 @@ index 9710e44a07..f130bdee66 100644 return SUCCESS; } -- -2.31.1 +2.41.0 -From 7557896fc206bd318851b3810b55bb51dc43336f Mon Sep 17 00:00:00 2001 +From 6bb3f5d83ea5a108018b22b5e5b3b7dff77a66de Mon Sep 17 00:00:00 2001 From: Remi Collet <remi@remirepo.net> Date: Thu, 18 Nov 2021 15:08:19 +0100 -Subject: [PATCH 26/26] ignore remaining warnings +Subject: [PATCH 26/27] ignore remaining warnings --- ext/openssl/openssl.c | 3 ++- @@ -2579,5 +2575,66 @@ index 00ab6dc73a..b136729cb5 100644 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); OSSL_PARAM *params = NULL; -- -2.31.1 +2.41.0 + +From 5019534853051a3cb3cce9811e98e583e568e112 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Mon, 26 Jun 2023 07:59:18 +0200 +Subject: [PATCH 27/27] don't use true + +--- + ext/openssl/openssl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c +index b136729cb5..d0fd976376 100644 +--- a/ext/openssl/openssl.c ++++ b/ext/openssl/openssl.c +@@ -4454,7 +4454,7 @@ static int php_openssl_pkey_init_legacy_ec(EC_KEY *eckey, zval *data, int *is_pr + } + + if (!EC_KEY_check_key(eckey)) { +- *is_private = true; ++ *is_private = 1; + PHP_OPENSSL_RAND_ADD_TIME(); + EC_KEY_generate_key(eckey); + php_openssl_store_errors(); +-- +2.41.0 + +From 74f75db0c3665677ec006cd379fd561feacffdc6 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Sun, 15 May 2022 13:49:17 +0100 +Subject: [PATCH] Fix bug #79589: ssl3_read_n:unexpected eof while reading +The unexpected EOF failure was introduced in OpenSSL 3.0 to prevent +truncation attack. However there are many non complaint servers and +it is causing break for many users including potential majority +of those where the truncation attack is not applicable. For that reason +we try to keep behavior consitent with older OpenSSL versions which is +also the path chosen by some other languages and web servers. + +Closes GH-8369 +--- + NEWS | 4 ++++ + ext/openssl/tests/bug79589.phpt | 21 +++++++++++++++++++++ + ext/openssl/xp_ssl.c | 5 +++++ + 3 files changed, 30 insertions(+) + create mode 100644 ext/openssl/tests/bug79589.phpt + +diff --git a/ext/openssl/xp_ssl.c b/ext/openssl/xp_ssl.c +index 918b3ca5b21df..ce23fb29f4296 100644 +--- a/ext/openssl/xp_ssl.c ++++ b/ext/openssl/xp_ssl.c +@@ -1652,6 +1652,11 @@ int php_openssl_setup_crypto(php_stream *stream, + + ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + ++#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF ++ /* Only for OpenSSL 3+ to keep OpenSSL 1.1.1 behavior */ ++ ssl_ctx_options |= SSL_OP_IGNORE_UNEXPECTED_EOF; ++#endif ++ + if (!GET_VER_OPT("disable_compression") || zend_is_true(val)) { + ssl_ctx_options |= SSL_OP_NO_COMPRESSION; + } diff --git a/php-7.4.33-gcc14.patch b/php-7.4.33-gcc14.patch new file mode 100644 index 0000000..eef34ad --- /dev/null +++ b/php-7.4.33-gcc14.patch @@ -0,0 +1,201 @@ +diff -up php-7.4.33/ext/pdo_oci/oci_statement.c.gcc14 php-7.4.33/ext/pdo_oci/oci_statement.c +--- php-7.4.33/ext/pdo_oci/oci_statement.c.gcc14 2022-10-31 11:36:05.000000000 +0100 ++++ php-7.4.33/ext/pdo_oci/oci_statement.c 2024-02-14 15:05:34.224568567 +0100 +@@ -654,7 +654,7 @@ static ssize_t oci_blob_write(php_stream + return amt; + } + +-static size_t oci_blob_read(php_stream *stream, char *buf, size_t count) ++static ssize_t oci_blob_read(php_stream *stream, char *buf, size_t count) + { + struct oci_lob_self *self = (struct oci_lob_self*)stream->abstract; + ub4 amt; +@@ -666,7 +666,7 @@ static size_t oci_blob_read(php_stream * + NULL, NULL, 0, SQLCS_IMPLICIT); + + if (r != OCI_SUCCESS && r != OCI_NEED_DATA) { +- return (size_t)-1; ++ return -1; + } + + self->offset += amt; +diff -up php-7.4.33/sapi/litespeed/lsapi_main.c.gcc14 php-7.4.33/sapi/litespeed/lsapi_main.c +--- php-7.4.33/sapi/litespeed/lsapi_main.c.gcc14 2024-02-14 15:09:59.523706463 +0100 ++++ php-7.4.33/sapi/litespeed/lsapi_main.c 2024-02-14 15:10:13.979258854 +0100 +@@ -25,6 +25,7 @@ + #include "zend.h" + #include "ext/standard/basic_functions.h" + #include "ext/standard/info.h" ++#include "ext/standard/head.h" + #include "lsapilib.h" + + #include <stdio.h> + + +Adapted for 7.4 from: + +From aeaab8ee3e52f74c042a861e394437d6554b36be Mon Sep 17 00:00:00 2001 +From: Florian Weimer <fweimer@redhat.com> +Date: Mon, 9 Sep 2019 21:29:03 +0200 +Subject: [PATCH] Port various autoconf bits to C99 compilers + +C99 no longer has implicit function declarations and implicit ints. +Current GCC versions enable them as an extension, but this will +change in a future GCC version. +--- + Zend/Zend.m4 | 2 ++ + build/libtool.m4 | 5 +---- + build/php.m4 | 17 ++++++++++------- + configure.ac | 2 ++ + ext/standard/config.m4 | 22 ++++++++++++++++++++++ + 5 files changed, 37 insertions(+), 11 deletions(-) + +diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 +index 054e2621a4057..57a12ac36ba60 100644 +--- a/Zend/Zend.m4 ++++ b/Zend/Zend.m4 +@@ -157,6 +157,7 @@ AC_MSG_CHECKING(whether double cast to long preserves least significant bits) + + AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include <limits.h> ++#include <stdlib.h> + + int main() + { +@@ -256,6 +257,7 @@ AC_MSG_CHECKING(for MM alignment and log values) + + AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include <stdio.h> ++#include <stdlib.h> + + typedef union _mm_align_test { + void *ptr; +diff --git a/build/libtool.m4 b/build/libtool.m4 +index f7f51642920f9..577dad4cbe313 100644 +--- a/build/libtool.m4 ++++ b/build/libtool.m4 +@@ -945,6 +945,7 @@ else + #endif + + #include <stdio.h> ++#include <stdlib.h> + + #ifdef RTLD_GLOBAL + # define LT_DLGLOBAL RTLD_GLOBAL +diff --git a/build/php.m4 b/build/php.m4 +index 25f5aa762b892..529876b6b67c6 100644 +--- a/build/php.m4 ++++ b/build/php.m4 +@@ -1120,7 +1120,7 @@ AC_CACHE_CHECK(for type of reentrant time-related functions, ac_cv_time_r_type,[ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include <time.h> + +-main() { ++int main() { + char buf[27]; + struct tm t; + time_t old = 0; +@@ -1136,7 +1136,7 @@ return (1); + ],[ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include <time.h> +-main() { ++int main() { + struct tm t, *s; + time_t old = 0; + char buf[27], *p; +@@ -1597,7 +1600,7 @@ AC_DEFUN([PHP_CHECK_FUNC_LIB],[ + if test "$found" = "yes"; then + ac_libs=$LIBS + LIBS="$LIBS -l$2" +- AC_RUN_IFELSE([AC_LANG_SOURCE([[main() { return (0); }]])],[found=yes],[found=no],[found=no]) ++ AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return (0); }]])],[found=yes],[found=no],[found=no]) + LIBS=$ac_libs + fi + +@@ -2285,7 +2288,7 @@ AC_DEFUN([PHP_TEST_WRITE_STDOUT],[ + + #define TEXT "This is the test message -- " + +-main() ++int main() + { + int n; + +diff --git a/configure.ac b/configure.ac +index d759b027517e5..e15b83ca25296 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -665,6 +665,8 @@ + #include <string.h> + #include <netdb.h> + #include <sys/types.h> ++#include <string.h> ++#include <stdlib.h> + #ifndef AF_INET + # include <sys/socket.h> + #endif +diff --git a/ext/standard/config.m4 b/ext/standard/config.m4 +index 9f85ec2b7080d..5b49e5d661f3c 100644 +--- a/ext/standard/config.m4 ++++ b/ext/standard/config.m4 +@@ -71,6 +71,9 @@ AC_CACHE_CHECK(for standard DES crypt, ac_cv_crypt_des,[ + #include <crypt.h> + #endif + ++#include <stdlib.h> ++#include <string.h> ++ + int main() { + #if HAVE_CRYPT + char *encrypted = crypt("rasmuslerdorf","rl"); +@@ -98,6 +101,9 @@ AC_CACHE_CHECK(for extended DES crypt, ac_cv_crypt_ext_des,[ + #include <crypt.h> + #endif + ++#include <stdlib.h> ++#include <string.h> ++ + int main() { + #if HAVE_CRYPT + char *encrypted = crypt("rasmuslerdorf","_J9..rasm"); +@@ -125,6 +131,9 @@ AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include <crypt.h> + #endif + ++#include <stdlib.h> ++#include <string.h> ++ + int main() { + #if HAVE_CRYPT + char salt[15], answer[40]; +@@ -162,6 +171,9 @@ AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include <crypt.h> + #endif + ++#include <stdlib.h> ++#include <string.h> ++ + int main() { + #if HAVE_CRYPT + char salt[30], answer[70]; +@@ -196,6 +208,9 @@ AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include <crypt.h> + #endif + ++#include <stdlib.h> ++#include <string.h> ++ + int main() { + #if HAVE_CRYPT + char salt[21], answer[21+86]; +@@ -229,6 +244,9 @@ AC_RUN_IFELSE([AC_LANG_SOURCE([[ + #include <crypt.h> + #endif + ++#include <stdlib.h> ++#include <string.h> ++ + int main() { + #if HAVE_CRYPT + char salt[21], answer[21+43]; diff --git a/php-7.4.33-icu.patch b/php-7.4.33-icu.patch new file mode 100644 index 0000000..48940fd --- /dev/null +++ b/php-7.4.33-icu.patch @@ -0,0 +1,35 @@ +From cc46a4e6b5a413bab3e264c1dcaaf7052f54fbc4 Mon Sep 17 00:00:00 2001 +From: David Carlier <devnexen@gmail.com> +Date: Sat, 17 Feb 2024 21:38:21 +0000 +Subject: [PATCH] ext/intl: level up c++ runtime std for icu 74 and onwards. + +to align with what is required to build icu 74 itself. + +Close GH-14002 +--- + NEWS | 3 +++ + ext/intl/config.m4 | 11 ++++++++++- + 2 files changed, 13 insertions(+), 1 deletion(-) + +diff --git a/ext/intl/config.m4 b/ext/intl/config.m4 +index dd687bcd97de3..48f5147ca7bbf 100644 +--- a/ext/intl/config.m4 ++++ b/ext/intl/config.m4 +@@ -83,7 +83,16 @@ if test "$PHP_INTL" != "no"; then + breakiterator/codepointiterator_methods.cpp" + + PHP_REQUIRE_CXX() +- PHP_CXX_COMPILE_STDCXX(11, mandatory, PHP_INTL_STDCXX) ++ ++ AC_MSG_CHECKING([if intl requires -std=gnu++17]) ++ AS_IF([test "$PKG_CONFIG icu-uc --atleast-version=74"],[ ++ AC_MSG_RESULT([yes]) ++ PHP_CXX_COMPILE_STDCXX(17, mandatory, PHP_INTL_STDCXX) ++ ],[ ++ AC_MSG_RESULT([no]) ++ PHP_CXX_COMPILE_STDCXX(11, mandatory, PHP_INTL_STDCXX) ++ ]) ++ + PHP_INTL_CXX_FLAGS="$INTL_COMMON_FLAGS $PHP_INTL_STDCXX $ICU_CXXFLAGS" + if test "$ext_shared" = "no"; then + PHP_ADD_SOURCES(PHP_EXT_DIR(intl), $PHP_INTL_CXX_SOURCES, $PHP_INTL_CXX_FLAGS) diff --git a/php-7.4.33-libxml212.patch b/php-7.4.33-libxml212.patch new file mode 100644 index 0000000..2de8abb --- /dev/null +++ b/php-7.4.33-libxml212.patch @@ -0,0 +1,661 @@ +From 1fa2356f4f580d2df4068809a4aba6d5356a22e6 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Fri, 17 Nov 2023 19:45:40 +0100 +Subject: [PATCH 1/4] Fix GH-12702: libxml2 2.12.0 issue building from src + +Fixes GH-12702. + +Co-authored-by: nono303 <github@nono303.net> +--- + ext/dom/document.c | 1 + + ext/libxml/php_libxml.h | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/ext/dom/document.c b/ext/dom/document.c +index b478e1a1aa..707a1fbb20 100644 +--- a/ext/dom/document.c ++++ b/ext/dom/document.c +@@ -25,6 +25,7 @@ + #if HAVE_LIBXML && HAVE_DOM + #include "php_dom.h" + #include <libxml/SAX.h> ++#include <libxml/xmlsave.h> + #ifdef LIBXML_SCHEMAS_ENABLED + #include <libxml/relaxng.h> + #include <libxml/xmlschemas.h> +diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h +index cf936e95de..9bbcbf6783 100644 +--- a/ext/libxml/php_libxml.h ++++ b/ext/libxml/php_libxml.h +@@ -37,6 +37,7 @@ extern zend_module_entry libxml_module_entry; + + #include "zend_smart_str.h" + #include <libxml/tree.h> ++#include <libxml/parser.h> + + #define LIBXML_SAVE_NOEMPTYTAG 1<<2 + +-- +2.43.0 + +From c2a134e08fe4ac4a2ed753548a18fc27da8ae2e1 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Fri, 1 Dec 2023 18:03:35 +0100 +Subject: [PATCH 2/4] Fix libxml2 2.12 build due to API breaks + +See https://github.com/php/php-src/actions/runs/7062192818/job/19225478601 +--- + ext/libxml/libxml.c | 14 ++++++++++---- + ext/soap/php_sdl.c | 2 +- + 2 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c +index d343135b98..b54ab40953 100644 +--- a/ext/libxml/libxml.c ++++ b/ext/libxml/libxml.c +@@ -530,7 +530,11 @@ static int _php_libxml_free_error(xmlErrorPtr error) + return 1; + } + +-static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg) ++#if LIBXML_VERSION >= 21200 ++static void _php_list_set_error_structure(const xmlError *error, const char *msg) ++#else ++static void _php_list_set_error_structure(xmlError *error, const char *msg) ++#endif + { + xmlError error_copy; + int ret; +@@ -782,7 +786,11 @@ PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...) + va_end(args); + } + ++#if LIBXML_VERSION >= 21200 ++PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, const xmlError *error) ++#else + PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error) ++#endif + { + _php_list_set_error_structure(error, NULL); + +@@ -1061,9 +1069,7 @@ static PHP_FUNCTION(libxml_use_internal_errors) + Retrieve last error from libxml */ + static PHP_FUNCTION(libxml_get_last_error) + { +- xmlErrorPtr error; +- +- error = xmlGetLastError(); ++ const xmlError *error = xmlGetLastError(); + + if (error) { + object_init_ex(return_value, libxmlerror_class_entry); +diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c +index 26a23f57db..3df532a2d6 100644 +--- a/ext/soap/php_sdl.c ++++ b/ext/soap/php_sdl.c +@@ -333,7 +333,7 @@ static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, int include) + sdl_restore_uri_credentials(ctx); + + if (!wsdl) { +- xmlErrorPtr xmlErrorPtr = xmlGetLastError(); ++ const xmlError *xmlErrorPtr = xmlGetLastError(); + + if (xmlErrorPtr) { + soap_error2(E_ERROR, "Parsing WSDL: Couldn't load from '%s' : %s", struri, xmlErrorPtr->message); +-- +2.43.0 + +From f9da49aa0a5b033c4b1e8072b9c0915d7672f34e Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Sat, 29 Apr 2023 21:07:50 +0200 +Subject: [PATCH 3/4] Fix GH-11160: Few tests failed building with new libxml + 2.11.0 + +It's possible to categorise the failures into 2 categories: + - Changed error message. In this case we either duplicate the test and + modify the error message. Or if the change in error message is + small, we use the EXPECTF matchers to make the test compatible with both + old and new versions of libxml2. + - Missing warnings. This is caused by a change in libxml2 where the + parser started using SAX APIs internally [1]. In this case the + error_type passed to php_libxml_internal_error_handler() changed from + PHP_LIBXML_ERROR to PHP_LIBXML_CTX_WARNING because it internally + started to use the SAX handlers instead of the generic handlers. + However, for the SAX handlers the current input stack is empty, so + nothing is actually printed. I fixed this by falling back to a + regular warning without a filename & line number reference, which + mimicks the old behaviour. Furthermore, this change now also shows + an additional warning in a test which was previously hidden. + +[1] https://gitlab.gnome.org/GNOME/libxml2/-/commit/9a82b94a94bd310db426edd453b0f38c6c8f69f5 + +Closes GH-11162. +--- + .../DOMDocument_loadXML_error2_gte2_11.phpt | 34 +++++++ + ...> DOMDocument_loadXML_error2_pre2_11.phpt} | 4 + + .../DOMDocument_load_error2_gte2_11.phpt | 34 +++++++ + ...t => DOMDocument_load_error2_pre2_11.phpt} | 4 + + ext/libxml/libxml.c | 2 + + ext/libxml/tests/bug61367-read_2.phpt | 2 +- + .../tests/libxml_disable_entity_loader_2.phpt | 2 +- + ...set_external_entity_loader_variation2.phpt | 2 + + ext/xml/tests/bug26614_libxml_gte2_11.phpt | 95 +++++++++++++++++++ + ...bxml.phpt => bug26614_libxml_pre2_11.phpt} | 1 + + 10 files changed, 178 insertions(+), 2 deletions(-) + create mode 100644 ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt + rename ext/dom/tests/{DOMDocument_loadXML_error2.phpt => DOMDocument_loadXML_error2_pre2_11.phpt} (90%) + create mode 100644 ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt + rename ext/dom/tests/{DOMDocument_load_error2.phpt => DOMDocument_load_error2_pre2_11.phpt} (90%) + create mode 100644 ext/xml/tests/bug26614_libxml_gte2_11.phpt + rename ext/xml/tests/{bug26614_libxml.phpt => bug26614_libxml_pre2_11.phpt} (96%) + +diff --git a/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt +new file mode 100644 +index 0000000000..ff5ceb3fbe +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt +@@ -0,0 +1,34 @@ ++--TEST-- ++Test DOMDocument::loadXML() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21100) die('skip libxml2 test variant for version >= 2.11'); ++?> ++--DESCRIPTION-- ++This test verifies the method detects attributes values not closed between " or ' ++Environment variables used in the test: ++- XML_FILE: the xml file to load ++- LOAD_OPTIONS: the second parameter to pass to the method ++- EXPECTED_RESULT: the expected result ++--CREDITS-- ++Antonio Diaz Ruiz <dejalatele@gmail.com> ++--INI-- ++assert.bail=true ++--EXTENSIONS-- ++dom ++--ENV-- ++XML_FILE=/not_well_formed2.xml ++LOAD_OPTIONS=0 ++EXPECTED_RESULT=0 ++--FILE_EXTERNAL-- ++domdocumentloadxml_test_method.inc ++--EXPECTF-- ++Warning: DOMDocument::loadXML(): AttValue: " or ' expected in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): internal error: xmlParseStartTag: problem parsing attributes in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): Couldn't find end of Start Tag book line 4 in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): Opening and ending tag mismatch: books line 3 and book in Entity, line: 7 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): Extra content at the end of the document in Entity, line: 8 in %s on line %d +diff --git a/ext/dom/tests/DOMDocument_loadXML_error2.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt +similarity index 90% +rename from ext/dom/tests/DOMDocument_loadXML_error2.phpt +rename to ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt +index 6d56a317ed..0e36d20905 100644 +--- a/ext/dom/tests/DOMDocument_loadXML_error2.phpt ++++ b/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt +@@ -1,5 +1,9 @@ + --TEST-- + Test DOMDocument::loadXML() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION >= 21100) die('skip libxml2 test variant for version < 2.11'); ++?> + --DESCRIPTION-- + This test verifies the method detects attributes values not closed between " or ' + Environment variables used in the test: +diff --git a/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt b/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt +new file mode 100644 +index 0000000000..32b6bf1611 +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt +@@ -0,0 +1,34 @@ ++--TEST-- ++Test DOMDocument::load() detects not-well formed ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21100) die('skip libxml2 test variant for version >= 2.11'); ++?> ++--DESCRIPTION-- ++This test verifies the method detects attributes values not closed between " or ' ++Environment variables used in the test: ++- XML_FILE: the xml file to load ++- LOAD_OPTIONS: the second parameter to pass to the method ++- EXPECTED_RESULT: the expected result ++--CREDITS-- ++Antonio Diaz Ruiz <dejalatele@gmail.com> ++--INI-- ++assert.bail=true ++--EXTENSIONS-- ++dom ++--ENV-- ++XML_FILE=/not_well_formed2.xml ++LOAD_OPTIONS=0 ++EXPECTED_RESULT=0 ++--FILE_EXTERNAL-- ++domdocumentload_test_method.inc ++--EXPECTF-- ++Warning: DOMDocument::load(): AttValue: " or ' expected in %s on line %d ++ ++Warning: DOMDocument::load(): internal error: xmlParseStartTag: problem parsing attributes in %s on line %d ++ ++Warning: DOMDocument::load(): Couldn't find end of Start Tag book line 4 in %s on line %d ++ ++Warning: DOMDocument::load(): Opening and ending tag mismatch: books line 3 and book in %s on line %d ++ ++Warning: DOMDocument::load(): Extra content at the end of the document in %s on line %d +diff --git a/ext/dom/tests/DOMDocument_load_error2.phpt b/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt +similarity index 90% +rename from ext/dom/tests/DOMDocument_load_error2.phpt +rename to ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt +index f450cf1654..b97fff9d2f 100644 +--- a/ext/dom/tests/DOMDocument_load_error2.phpt ++++ b/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt +@@ -1,5 +1,9 @@ + --TEST-- + Test DOMDocument::load() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION >= 21100) die('skip libxml2 test variant for version < 2.11'); ++?> + --DESCRIPTION-- + This test verifies the method detects attributes values not closed between " or ' + Environment variables used in the test: +diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c +index b54ab40953..7917f636a9 100644 +--- a/ext/libxml/libxml.c ++++ b/ext/libxml/libxml.c +@@ -578,6 +578,8 @@ static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg) + } else { + php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line); + } ++ } else { ++ php_error_docref(NULL, E_WARNING, "%s", msg); + } + } + +diff --git a/ext/libxml/tests/bug61367-read_2.phpt b/ext/libxml/tests/bug61367-read_2.phpt +index 8cc0b50144..12743adab1 100644 +--- a/ext/libxml/tests/bug61367-read_2.phpt ++++ b/ext/libxml/tests/bug61367-read_2.phpt +@@ -55,6 +55,6 @@ bool(true) + int(4) + bool(true) + +-Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "file:///%s/test_bug_61367-read/bad" in %s on line %d ++Warning: DOMDocument::loadXML(): %Sfailed to load external entity "file:///%s/test_bug_61367-read/bad" in %s on line %d + + Notice: Trying to get property 'nodeValue' of non-object in %s on line %d +diff --git a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt +index 845bd4bbe3..55d8e61ee0 100644 +--- a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt ++++ b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt +@@ -36,6 +36,6 @@ echo "Done\n"; + bool(true) + bool(false) + +-Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "%s" in %s on line %d ++Warning: DOMDocument::loadXML(): %Sfailed to load external entity "%s" in %s on line %d + bool(true) + Done +diff --git a/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt b/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt +index e51869cf47..0664de1ea6 100644 +--- a/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt ++++ b/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt +@@ -38,6 +38,8 @@ echo "Done.\n"; + string(10) "-//FOO/BAR" + string(%d) "%sfoobar.dtd" + ++Warning: DOMDocument::validate(): Failed to load external entity "-//FOO/BAR" in %s on line %d ++ + Warning: DOMDocument::validate(): Could not load the external subset "foobar.dtd" in %s on line %d + bool(false) + bool(true) +diff --git a/ext/xml/tests/bug26614_libxml_gte2_11.phpt b/ext/xml/tests/bug26614_libxml_gte2_11.phpt +new file mode 100644 +index 0000000000..9a81b67686 +--- /dev/null ++++ b/ext/xml/tests/bug26614_libxml_gte2_11.phpt +@@ -0,0 +1,95 @@ ++--TEST-- ++Bug #26614 (CDATA sections skipped on line count) ++--EXTENSIONS-- ++xml ++--SKIPIF-- ++<?php ++if (!defined("LIBXML_VERSION")) die('skip libxml2 test'); ++if (LIBXML_VERSION < 21100) die('skip libxml2 test variant for version >= 2.11'); ++?> ++--FILE-- ++<?php ++/* ++this test works fine with Expat but fails with libxml ++which we now use as default ++ ++further investigation has shown that not only line count ++is skipped on CDATA sections but that libxml does also ++show different column numbers and byte positions depending ++on context and in opposition to what one would expect to ++see and what good old Expat reported just fine ... ++*/ ++ ++$xmls = array(); ++ ++// Case 1: CDATA Sections ++$xmls["CDATA"] ='<?xml version="1.0" encoding="iso-8859-1" ?> ++<data> ++<![CDATA[ ++multi ++line ++CDATA ++block ++]]> ++</data>'; ++ ++// Case 2: replace some characters so that we get comments instead ++$xmls["Comment"] ='<?xml version="1.0" encoding="iso-8859-1" ?> ++<data> ++<!-- ATA[ ++multi ++line ++CDATA ++block ++--> ++</data>'; ++ ++// Case 3: replace even more characters so that only textual data is left ++$xmls["Text"] ='<?xml version="1.0" encoding="iso-8859-1" ?> ++<data> ++-!-- ATA[ ++multi ++line ++CDATA ++block ++--- ++</data>'; ++ ++function startElement($parser, $name, $attrs) { ++ printf("<$name> at line %d, col %d (byte %d)\n", ++ xml_get_current_line_number($parser), ++ xml_get_current_column_number($parser), ++ xml_get_current_byte_index($parser)); ++} ++ ++function endElement($parser, $name) { ++ printf("</$name> at line %d, col %d (byte %d)\n", ++ xml_get_current_line_number($parser), ++ xml_get_current_column_number($parser), ++ xml_get_current_byte_index($parser)); ++} ++ ++function characterData($parser, $data) { ++ // dummy ++} ++ ++foreach ($xmls as $desc => $xml) { ++ echo "$desc\n"; ++ $xml_parser = xml_parser_create(); ++ xml_set_element_handler($xml_parser, "startElement", "endElement"); ++ xml_set_character_data_handler($xml_parser, "characterData"); ++ if (!xml_parse($xml_parser, $xml, true)) ++ echo "Error: ".xml_error_string(xml_get_error_code($xml_parser))."\n"; ++ xml_parser_free($xml_parser); ++} ++?> ++--EXPECTF-- ++CDATA ++<DATA> at line 2, col %d (byte 50) ++</DATA> at line 9, col %d (byte 96) ++Comment ++<DATA> at line 2, col %d (byte 50) ++</DATA> at line 9, col %d (byte 96) ++Text ++<DATA> at line 2, col %d (byte 50) ++</DATA> at line 9, col %d (byte 96) +diff --git a/ext/xml/tests/bug26614_libxml.phpt b/ext/xml/tests/bug26614_libxml_pre2_11.phpt +similarity index 96% +rename from ext/xml/tests/bug26614_libxml.phpt +rename to ext/xml/tests/bug26614_libxml_pre2_11.phpt +index 3ddd35ed0e..afacaa1c59 100644 +--- a/ext/xml/tests/bug26614_libxml.phpt ++++ b/ext/xml/tests/bug26614_libxml_pre2_11.phpt +@@ -4,6 +4,7 @@ Bug #26614 (CDATA sections skipped on line count) + <?php + require_once("skipif.inc"); + if (!defined("LIBXML_VERSION")) die('skip libxml2 test'); ++if (LIBXML_VERSION >= 21100) die('skip libxml2 test variant for version < 2.11'); + ?> + --FILE-- + <?php +-- +2.43.0 + +From b2ac6c4fe4213258e7e9489ef50fe3afb2fdf4be Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Wed, 29 Nov 2023 20:49:29 +0100 +Subject: [PATCH 4/4] Test fixes for libxml2 2.12.0 + +--- + ext/dom/tests/DOMDocument_loadXML_error1.phpt | 4 +++ + .../DOMDocument_loadXML_error1_gte2_12.phpt | 26 ++++++++++++++++ + .../DOMDocument_loadXML_error2_gte2_11.phpt | 2 +- + .../DOMDocument_loadXML_error2_gte2_12.phpt | 30 +++++++++++++++++++ + ext/dom/tests/DOMDocument_load_error1.phpt | 4 +++ + .../DOMDocument_load_error1_gte2_12.phpt | 26 ++++++++++++++++ + .../DOMDocument_load_error2_gte2_11.phpt | 2 +- + .../DOMDocument_load_error2_gte2_12.phpt | 30 +++++++++++++++++++ + ext/xml/tests/bug81351.phpt | 4 +-- + 9 files changed, 124 insertions(+), 4 deletions(-) + create mode 100644 ext/dom/tests/DOMDocument_loadXML_error1_gte2_12.phpt + create mode 100644 ext/dom/tests/DOMDocument_loadXML_error2_gte2_12.phpt + create mode 100644 ext/dom/tests/DOMDocument_load_error1_gte2_12.phpt + create mode 100644 ext/dom/tests/DOMDocument_load_error2_gte2_12.phpt + +diff --git a/ext/dom/tests/DOMDocument_loadXML_error1.phpt b/ext/dom/tests/DOMDocument_loadXML_error1.phpt +index 4d3b81db79..0549d67630 100644 +--- a/ext/dom/tests/DOMDocument_loadXML_error1.phpt ++++ b/ext/dom/tests/DOMDocument_loadXML_error1.phpt +@@ -1,5 +1,9 @@ + --TEST-- + Test DOMDocument::loadXML() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION >= 21200) die('skip libxml2 test variant for version < 2.12'); ++?> + --DESCRIPTION-- + This test verifies the method detects an opening and ending tag mismatch + Environment variables used in the test: +diff --git a/ext/dom/tests/DOMDocument_loadXML_error1_gte2_12.phpt b/ext/dom/tests/DOMDocument_loadXML_error1_gte2_12.phpt +new file mode 100644 +index 0000000000..e1ded0ffad +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_loadXML_error1_gte2_12.phpt +@@ -0,0 +1,26 @@ ++--TEST-- ++Test DOMDocument::loadXML() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21200) die('skip libxml2 test variant for version >= 2.12'); ++?> ++--DESCRIPTION-- ++This test verifies the method detects an opening and ending tag mismatch ++Environment variables used in the test: ++- XML_FILE: the xml file to load ++- LOAD_OPTIONS: the second parameter to pass to the method ++- EXPECTED_RESULT: the expected result ++--CREDITS-- ++Antonio Diaz Ruiz <dejalatele@gmail.com> ++--EXTENSIONS-- ++dom ++--ENV-- ++XML_FILE=/not_well_formed.xml ++LOAD_OPTIONS=0 ++EXPECTED_RESULT=0 ++--FILE_EXTERNAL-- ++domdocumentloadxml_test_method.inc ++--EXPECTF-- ++Warning: DOMDocument::load%r(XML){0,1}%r(): Opening and ending tag mismatch: title line 5 and book %s ++ ++Warning: DOMDocument::load%r(XML){0,1}%r(): %rexpected '>'|Opening and ending tag mismatch: book line (4|5) and books%r %s +diff --git a/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt +index ff5ceb3fbe..f52d334813 100644 +--- a/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt ++++ b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt +@@ -2,7 +2,7 @@ + Test DOMDocument::loadXML() detects not-well formed XML + --SKIPIF-- + <?php +-if (LIBXML_VERSION < 21100) die('skip libxml2 test variant for version >= 2.11'); ++if (LIBXML_VERSION < 21100 || LIBXML_VERSION >= 21200) die('skip libxml2 test variant for version >= 2.11 && <= 2.12'); + ?> + --DESCRIPTION-- + This test verifies the method detects attributes values not closed between " or ' +diff --git a/ext/dom/tests/DOMDocument_loadXML_error2_gte2_12.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_12.phpt +new file mode 100644 +index 0000000000..6a3ff5841f +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_12.phpt +@@ -0,0 +1,30 @@ ++--TEST-- ++Test DOMDocument::loadXML() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21200) die('skip libxml2 test variant for version >= 2.12'); ++?> ++--DESCRIPTION-- ++This test verifies the method detects attributes values not closed between " or ' ++Environment variables used in the test: ++- XML_FILE: the xml file to load ++- LOAD_OPTIONS: the second parameter to pass to the method ++- EXPECTED_RESULT: the expected result ++--CREDITS-- ++Antonio Diaz Ruiz <dejalatele@gmail.com> ++--EXTENSIONS-- ++dom ++--ENV-- ++XML_FILE=/not_well_formed2.xml ++LOAD_OPTIONS=0 ++EXPECTED_RESULT=0 ++--FILE_EXTERNAL-- ++domdocumentloadxml_test_method.inc ++--EXPECTF-- ++Warning: DOMDocument::loadXML(): AttValue: " or ' expected in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): internal error: xmlParseStartTag: problem parsing attributes in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): Couldn't find end of Start Tag book line 4 in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): Opening and ending tag mismatch: books line 3 and book in Entity, line: 7 in %s on line %d +diff --git a/ext/dom/tests/DOMDocument_load_error1.phpt b/ext/dom/tests/DOMDocument_load_error1.phpt +index 8ac181d769..4416f5f6fe 100644 +--- a/ext/dom/tests/DOMDocument_load_error1.phpt ++++ b/ext/dom/tests/DOMDocument_load_error1.phpt +@@ -1,5 +1,9 @@ + --TEST-- + Test DOMDocument::load() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION >= 21200) die('skip libxml2 test variant for version < 2.12'); ++?> + --DESCRIPTION-- + This test verifies the method detects an opening and ending tag mismatch + Environment variables used in the test: +diff --git a/ext/dom/tests/DOMDocument_load_error1_gte2_12.phpt b/ext/dom/tests/DOMDocument_load_error1_gte2_12.phpt +new file mode 100644 +index 0000000000..183c8406fd +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_load_error1_gte2_12.phpt +@@ -0,0 +1,26 @@ ++--TEST-- ++Test DOMDocument::load() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21200) die('skip libxml2 test variant for version >= 2.12'); ++?> ++--DESCRIPTION-- ++This test verifies the method detects an opening and ending tag mismatch ++Environment variables used in the test: ++- XML_FILE: the xml file to load ++- LOAD_OPTIONS: the second parameter to pass to the method ++- EXPECTED_RESULT: the expected result ++--CREDITS-- ++Antonio Diaz Ruiz <dejalatele@gmail.com> ++--EXTENSIONS-- ++dom ++--ENV-- ++XML_FILE=/not_well_formed.xml ++LOAD_OPTIONS=0 ++EXPECTED_RESULT=0 ++--FILE_EXTERNAL-- ++domdocumentload_test_method.inc ++--EXPECTF-- ++Warning: DOMDocument::load%r(XML){0,1}%r(): Opening and ending tag mismatch: title line 5 and book %s ++ ++Warning: DOMDocument::load%r(XML){0,1}%r(): %rexpected '>'|Opening and ending tag mismatch: book line (4|5) and books%r %s +diff --git a/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt b/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt +index 32b6bf1611..4d9f992b3b 100644 +--- a/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt ++++ b/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt +@@ -2,7 +2,7 @@ + Test DOMDocument::load() detects not-well formed + --SKIPIF-- + <?php +-if (LIBXML_VERSION < 21100) die('skip libxml2 test variant for version >= 2.11'); ++if (LIBXML_VERSION < 21100 || LIBXML_VERSION >= 21200) die('skip libxml2 test variant for version >= 2.11 && <= 2.12'); + ?> + --DESCRIPTION-- + This test verifies the method detects attributes values not closed between " or ' +diff --git a/ext/dom/tests/DOMDocument_load_error2_gte2_12.phpt b/ext/dom/tests/DOMDocument_load_error2_gte2_12.phpt +new file mode 100644 +index 0000000000..4fadf41736 +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_load_error2_gte2_12.phpt +@@ -0,0 +1,30 @@ ++--TEST-- ++Test DOMDocument::load() detects not-well formed ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21200) die('skip libxml2 test variant for version >= 2.12'); ++?> ++--DESCRIPTION-- ++This test verifies the method detects attributes values not closed between " or ' ++Environment variables used in the test: ++- XML_FILE: the xml file to load ++- LOAD_OPTIONS: the second parameter to pass to the method ++- EXPECTED_RESULT: the expected result ++--CREDITS-- ++Antonio Diaz Ruiz <dejalatele@gmail.com> ++--EXTENSIONS-- ++dom ++--ENV-- ++XML_FILE=/not_well_formed2.xml ++LOAD_OPTIONS=0 ++EXPECTED_RESULT=0 ++--FILE_EXTERNAL-- ++domdocumentload_test_method.inc ++--EXPECTF-- ++Warning: DOMDocument::load(): AttValue: " or ' expected in %s on line %d ++ ++Warning: DOMDocument::load(): internal error: xmlParseStartTag: problem parsing attributes in %s on line %d ++ ++Warning: DOMDocument::load(): Couldn't find end of Start Tag book line 4 in %s on line %d ++ ++Warning: DOMDocument::load(): Opening and ending tag mismatch: books line 3 and book in %s on line %d +diff --git a/ext/xml/tests/bug81351.phpt b/ext/xml/tests/bug81351.phpt +index 19e4ca590b..dc934001be 100644 +--- a/ext/xml/tests/bug81351.phpt ++++ b/ext/xml/tests/bug81351.phpt +@@ -23,6 +23,6 @@ $code = xml_get_error_code($parser); + $error = xml_error_string($code); + echo "xml_parse returned $success, xml_get_error_code = $code, xml_error_string = $error\r\n"; + ?> +---EXPECT-- ++--EXPECTF-- + xml_parse returned 1, xml_get_error_code = 0, xml_error_string = No error +-xml_parse returned 0, xml_get_error_code = 5, xml_error_string = Invalid document end ++%rxml_parse returned 0, xml_get_error_code = 5, xml_error_string = Invalid document end|xml_parse returned 0, xml_get_error_code = 77, xml_error_string = Tag not finished%r +-- +2.43.0 + diff --git a/php-7.4.33-pcretests.patch b/php-7.4.33-pcretests.patch new file mode 100644 index 0000000..c226661 --- /dev/null +++ b/php-7.4.33-pcretests.patch @@ -0,0 +1,43 @@ +From c3150fcc89825f50d476b1b1971870aeb71f167d Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Wed, 12 Mar 2025 07:48:05 +0100 +Subject: [PATCH 1/2] Relax test expectation for pcre2lib 10.45 Using + e92848789acd8aa5cf32fedb519ba9378ac64e02 + +--- + ext/pcre/tests/bug75457.phpt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ext/pcre/tests/bug75457.phpt b/ext/pcre/tests/bug75457.phpt +index ee5ab162f8a6c..87dc12a1ad056 100644 +--- a/ext/pcre/tests/bug75457.phpt ++++ b/ext/pcre/tests/bug75457.phpt +@@ -6,5 +6,5 @@ $pattern = "/(((?(?C)0?=))(?!()0|.(?0)0)())/"; + var_dump(preg_match($pattern, "hello")); + ?> + --EXPECTF-- +-Warning: preg_match(): Compilation failed: assertion expected after (?( or (?(?C) at offset 8 in %sbug75457.php on line %d ++Warning: preg_match(): Compilation failed: %r(atomic|)%r assertion expected after (?( or (?(?C) at offset 8 in %sbug75457.php on line %d + bool(false) + +From 126095700a02b9aa1f33764a63c93a70e8373ad8 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@famillecollet.com> +Date: Wed, 12 Mar 2025 09:36:33 +0100 +Subject: [PATCH 2/2] Update ext/pcre/tests/bug75457.phpt + +Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +--- + ext/pcre/tests/bug75457.phpt | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ext/pcre/tests/bug75457.phpt b/ext/pcre/tests/bug75457.phpt +index 87dc12a1ad056..1401b25ff6fb7 100644 +--- a/ext/pcre/tests/bug75457.phpt ++++ b/ext/pcre/tests/bug75457.phpt +@@ -6,5 +6,5 @@ $pattern = "/(((?(?C)0?=))(?!()0|.(?0)0)())/"; + var_dump(preg_match($pattern, "hello")); + ?> + --EXPECTF-- +-Warning: preg_match(): Compilation failed: %r(atomic|)%r assertion expected after (?( or (?(?C) at offset 8 in %sbug75457.php on line %d ++Warning: preg_match(): Compilation failed:%r( atomic|)%r assertion expected after (?( or (?(?C) at offset 8 in %sbug75457.php on line %d + bool(false) diff --git a/php-7.4.33-proto.patch b/php-7.4.33-proto.patch new file mode 100644 index 0000000..2e20717 --- /dev/null +++ b/php-7.4.33-proto.patch @@ -0,0 +1,392 @@ +From f566cba0bb6bd53b1d44d5097e68201412b00f7a Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@php.net> +Date: Thu, 25 Nov 2021 13:16:26 +0100 +Subject: [PATCH] fix [-Wstrict-prototypes] build warnings in ext/gd + +--- + ext/gd/config.m4 | 2 -- + ext/gd/gd.c | 58 ++++++++++++++++++++++++------------------------ + 2 files changed, 29 insertions(+), 31 deletions(-) + +diff -up a/ext/gd/gd.c.proto b/ext/gd/gd.c +--- a/ext/gd/gd.c.proto 2022-10-31 11:36:07.000000000 +0100 ++++ b/ext/gd/gd.c 2025-02-13 12:04:07.860118321 +0100 +@@ -138,9 +138,9 @@ static void php_image_filter_pixelate(IN + static void php_image_filter_scatter(INTERNAL_FUNCTION_PARAMETERS); + + /* End Section filters declarations */ +-static gdImagePtr _php_image_create_from_string (zval *Data, char *tn, gdImagePtr (*ioctx_func_p)()); +-static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, gdImagePtr (*func_p)(), gdImagePtr (*ioctx_func_p)()); +-static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)()); ++static gdImagePtr _php_image_create_from_string (zval *Data, char *tn, gdImagePtr (*ioctx_func_p)(gdIOCtxPtr)); ++static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, gdImagePtr (*func_p)(FILE *), gdImagePtr (*ioctx_func_p)(gdIOCtxPtr)); ++static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn); + static int _php_image_type(char data[12]); + static void _php_image_convert(INTERNAL_FUNCTION_PARAMETERS, int image_type); + +@@ -2330,7 +2330,7 @@ static int _php_image_type (char data[12 + + /* {{{ _php_image_create_from_string + */ +-gdImagePtr _php_image_create_from_string(zval *data, char *tn, gdImagePtr (*ioctx_func_p)()) ++gdImagePtr _php_image_create_from_string(zval *data, char *tn, gdImagePtr (*ioctx_func_p)(gdIOCtxPtr)) + { + gdImagePtr im; + gdIOCtx *io_ctx; +@@ -2440,7 +2440,7 @@ PHP_FUNCTION(imagecreatefromstring) + + /* {{{ _php_image_create_from + */ +-static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, gdImagePtr (*func_p)(), gdImagePtr (*ioctx_func_p)()) ++static void _php_image_create_from(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, gdImagePtr (*func_p)(FILE *), gdImagePtr (*ioctx_func_p)(gdIOCtxPtr)) + { + char *file; + size_t file_len; +@@ -2477,7 +2477,7 @@ static void _php_image_create_from(INTER + if (FAILURE == php_stream_cast(stream, PHP_STREAM_AS_STDIO, (void**)&fp, REPORT_ERRORS)) { + goto out_err; + } +- } else if (ioctx_func_p) { ++ } else if (ioctx_func_p || image_type == PHP_GDIMG_TYPE_GD2PART) { + /* we can create an io context */ + gdIOCtx* io_ctx; + zend_string *buff; +@@ -2501,7 +2501,7 @@ static void _php_image_create_from(INTER + } + + if (image_type == PHP_GDIMG_TYPE_GD2PART) { +- im = (*ioctx_func_p)(io_ctx, srcx, srcy, width, height); ++ im = gdImageCreateFromGd2PartCtx(io_ctx, srcx, srcy, width, height); + } else { + im = (*ioctx_func_p)(io_ctx); + } +@@ -2519,7 +2519,7 @@ static void _php_image_create_from(INTER + if (!im && fp) { + switch (image_type) { + case PHP_GDIMG_TYPE_GD2PART: +- im = (*func_p)(fp, srcx, srcy, width, height); ++ im = gdImageCreateFromGd2Part(fp, srcx, srcy, width, height); + break; + #if defined(HAVE_GD_XPM) + case PHP_GDIMG_TYPE_XPM: +@@ -2608,7 +2608,7 @@ PHP_FUNCTION(imagecreatefromxbm) + Create a new image from XPM file or URL */ + PHP_FUNCTION(imagecreatefromxpm) + { +- _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XPM, "XPM", gdImageCreateFromXpm, NULL); ++ _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XPM, "XPM", NULL, NULL); + } + /* }}} */ + #endif +@@ -2641,7 +2641,7 @@ PHP_FUNCTION(imagecreatefromgd2) + Create a new image from a given part of GD2 file or URL */ + PHP_FUNCTION(imagecreatefromgd2part) + { +- _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2PART, "GD2", gdImageCreateFromGd2Part, gdImageCreateFromGd2PartCtx); ++ _php_image_create_from(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2PART, "GD2", NULL, NULL); + } + /* }}} */ + +@@ -2667,7 +2667,7 @@ PHP_FUNCTION(imagecreatefromtga) + + /* {{{ _php_image_output + */ +-static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)()) ++static void _php_image_output(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn) + { + zval *imgind; + char *file = NULL; +@@ -2720,13 +2720,13 @@ static void _php_image_output(INTERNAL_F + gdImageWBMP(im, q, fp); + break; + case PHP_GDIMG_TYPE_GD: +- (*func_p)(im, fp); ++ gdImageGd(im, fp); + break; + case PHP_GDIMG_TYPE_GD2: + if (q == -1) { + q = 128; + } +- (*func_p)(im, fp, q, t); ++ gdImageGd2(im, fp, q, t); + break; + default: + ZEND_ASSERT(0); +@@ -2756,13 +2756,13 @@ static void _php_image_output(INTERNAL_F + gdImageWBMP(im, q, tmp); + break; + case PHP_GDIMG_TYPE_GD: +- (*func_p)(im, tmp); ++ gdImageGd(im, tmp); + break; + case PHP_GDIMG_TYPE_GD2: + if (q == -1) { + q = 128; + } +- (*func_p)(im, tmp, q, t); ++ gdImageGd2(im, tmp, q, t); + break; + default: + ZEND_ASSERT(0); +@@ -2786,7 +2786,7 @@ static void _php_image_output(INTERNAL_F + Output XBM image to browser or file */ + PHP_FUNCTION(imagexbm) + { +- _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XBM, "XBM", gdImageXbmCtx); ++ _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_XBM, "XBM"); + } + /* }}} */ + +@@ -2794,7 +2794,7 @@ PHP_FUNCTION(imagexbm) + Output GIF image to browser or file */ + PHP_FUNCTION(imagegif) + { +- _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GIF, "GIF", gdImageGifCtx); ++ _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GIF, "GIF"); + } + /* }}} */ + +@@ -2803,7 +2803,7 @@ PHP_FUNCTION(imagegif) + Output PNG image to browser or file */ + PHP_FUNCTION(imagepng) + { +- _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_PNG, "PNG", gdImagePngCtxEx); ++ _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_PNG, "PNG"); + } + /* }}} */ + #endif /* HAVE_GD_PNG */ +@@ -2814,7 +2814,7 @@ PHP_FUNCTION(imagepng) + Output WEBP image to browser or file */ + PHP_FUNCTION(imagewebp) + { +- _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WEBP, "WEBP", gdImageWebpCtx); ++ _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WEBP, "WEBP"); + } + /* }}} */ + #endif /* HAVE_GD_WEBP */ +@@ -2825,7 +2825,7 @@ PHP_FUNCTION(imagewebp) + Output JPEG image to browser or file */ + PHP_FUNCTION(imagejpeg) + { +- _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_JPG, "JPEG", gdImageJpegCtx); ++ _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_JPG, "JPEG"); + } + /* }}} */ + #endif /* HAVE_GD_JPG */ +@@ -2834,7 +2834,7 @@ PHP_FUNCTION(imagejpeg) + Output WBMP image to browser or file */ + PHP_FUNCTION(imagewbmp) + { +- _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WBM, "WBMP", gdImageWBMPCtx); ++ _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_WBM, "WBMP"); + } + /* }}} */ + +@@ -2842,7 +2842,7 @@ PHP_FUNCTION(imagewbmp) + Output GD image to browser or file */ + PHP_FUNCTION(imagegd) + { +- _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD, "GD", gdImageGd); ++ _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD, "GD"); + } + /* }}} */ + +@@ -2850,7 +2850,7 @@ PHP_FUNCTION(imagegd) + Output GD2 image to browser or file */ + PHP_FUNCTION(imagegd2) + { +- _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2, "GD2", gdImageGd2); ++ _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_GD2, "GD2"); + } + /* }}} */ + +@@ -2859,7 +2859,7 @@ PHP_FUNCTION(imagegd2) + Output BMP image to browser or file */ + PHP_FUNCTION(imagebmp) + { +- _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_BMP, "BMP", gdImageBmpCtx); ++ _php_image_output_ctx(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_TYPE_BMP, "BMP"); + } + /* }}} */ + #endif +@@ -4146,7 +4146,7 @@ static void php_imagettftext_common(INTE + Output WBMP image to browser or file */ + PHP_FUNCTION(image2wbmp) + { +- _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_CONVERT_WBM, "WBMP", NULL); ++ _php_image_output(INTERNAL_FUNCTION_PARAM_PASSTHRU, PHP_GDIMG_CONVERT_WBM, "WBMP"); + } + /* }}} */ + +diff -up a/ext/gd/gd_ctx.c.proto b/ext/gd/gd_ctx.c +--- a/ext/gd/gd_ctx.c.proto 2025-02-13 11:42:48.478248591 +0100 ++++ b/ext/gd/gd_ctx.c 2025-02-13 11:52:48.325740296 +0100 +@@ -77,7 +77,7 @@ static void _php_image_stream_ctxfreeand + } /* }}} */ + + /* {{{ _php_image_output_ctx */ +-static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn, void (*func_p)()) ++static void _php_image_output_ctx(INTERNAL_FUNCTION_PARAMETERS, int image_type, char *tn) + { + zval *imgind; + char *file = NULL; +@@ -177,16 +177,20 @@ static void _php_image_output_ctx(INTERN + + switch(image_type) { + case PHP_GDIMG_TYPE_JPG: +- (*func_p)(im, ctx, q); ++ gdImageJpegCtx(im, ctx, q); + break; + case PHP_GDIMG_TYPE_WEBP: + if (q == -1) { + q = 80; + } +- (*func_p)(im, ctx, q); ++ gdImageWebpCtx(im, ctx, q); + break; + case PHP_GDIMG_TYPE_PNG: +- (*func_p)(im, ctx, q, f); ++#ifdef HAVE_GD_BUNDLED ++ gdImagePngCtxEx(im, ctx, q, f); ++#else ++ gdImagePngCtxEx(im, ctx, q); ++#endif + break; + case PHP_GDIMG_TYPE_XBM: + case PHP_GDIMG_TYPE_WBM: +@@ -197,16 +201,16 @@ static void _php_image_output_ctx(INTERN + q = i; + } + if (image_type == PHP_GDIMG_TYPE_XBM) { +- (*func_p)(im, file ? file : "", q, ctx); ++ gdImageXbmCtx(im, file ? file : "", q, ctx); + } else { +- (*func_p)(im, q, ctx); ++ gdImageWBMPCtx(im, q, ctx); + } + break; + case PHP_GDIMG_TYPE_BMP: +- (*func_p)(im, ctx, (int) compressed); ++ gdImageBmpCtx(im, ctx, (int) compressed); + break; +- default: +- (*func_p)(im, ctx); ++ case PHP_GDIMG_TYPE_GIF: ++ gdImageGifCtx(im, ctx); + break; + } + +From b7356692f69f4ac0a07ea54e83debdd04b426dcb Mon Sep 17 00:00:00 2001 +From: George Peter Banyard <girgias@php.net> +Date: Wed, 12 May 2021 14:41:11 +0100 +Subject: [PATCH] Specify function pointer signature for scanf implementation + +Fix [-Wstrict-prototypes] warnings in standard/scanf.c +--- + ext/standard/scanf.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/ext/standard/scanf.c b/ext/standard/scanf.c +index f58b4195cc599..78ecc1642cf92 100644 +--- a/ext/standard/scanf.c ++++ b/ext/standard/scanf.c +@@ -108,6 +108,8 @@ typedef struct CharSet { + } *ranges; + } CharSet; + ++typedef zend_long (*int_string_formater)(const char*, char**, int); ++ + /* + * Declarations for functions used only in this file. + */ +@@ -585,7 +587,7 @@ PHPAPI int php_sscanf_internal( char *string, char *format, + int base = 0; + int underflow = 0; + size_t width; +- zend_long (*fn)() = NULL; ++ int_string_formater fn = NULL; + char *ch, sch; + int flags; + char buf[64]; /* Temporary buffer to hold scanned number +@@ -750,29 +752,29 @@ PHPAPI int php_sscanf_internal( char *string, char *format, + case 'D': + op = 'i'; + base = 10; +- fn = (zend_long (*)())ZEND_STRTOL_PTR; ++ fn = (int_string_formater)ZEND_STRTOL_PTR; + break; + case 'i': + op = 'i'; + base = 0; +- fn = (zend_long (*)())ZEND_STRTOL_PTR; ++ fn = (int_string_formater)ZEND_STRTOL_PTR; + break; + case 'o': + op = 'i'; + base = 8; +- fn = (zend_long (*)())ZEND_STRTOL_PTR; ++ fn = (int_string_formater)ZEND_STRTOL_PTR; + break; + case 'x': + case 'X': + op = 'i'; + base = 16; +- fn = (zend_long (*)())ZEND_STRTOL_PTR; ++ fn = (int_string_formater)ZEND_STRTOL_PTR; + break; + case 'u': + op = 'i'; + base = 10; + flags |= SCAN_UNSIGNED; +- fn = (zend_long (*)())ZEND_STRTOUL_PTR; ++ fn = (int_string_formater)ZEND_STRTOUL_PTR; + break; + + case 'f': +From 2068d230d981d7b06b41b87ebc37ab2581b79852 Mon Sep 17 00:00:00 2001 +From: George Peter Banyard <girgias@php.net> +Date: Wed, 12 May 2021 18:54:57 +0100 +Subject: [PATCH] Fix [-Wstrict-prototypes] warning in PCNTL extension + +To achieve this we need to introduce a new wrapper function with +dummy arguments which calls pcntl_signal_dispatch() to respect +the function pointer signature for a tick function. +--- + ext/pcntl/pcntl.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c +index 1e8690ae75144..c116eff7d034a 100644 +--- a/ext/pcntl/pcntl.c ++++ b/ext/pcntl/pcntl.c +@@ -252,7 +252,8 @@ static void pcntl_siginfo_to_zval(int, s + #else + static void pcntl_signal_handler(int); + #endif +-static void pcntl_signal_dispatch(); ++static void pcntl_signal_dispatch(void); ++static void pcntl_signal_dispatch_tick_function(int dummy_int, void *dummy_pointer); + static void pcntl_interrupt_function(zend_execute_data *execute_data); + + void php_register_signal_constants(INIT_FUNC_ARGS) +@@ -587,7 +588,7 @@ static PHP_GINIT_FUNCTION(pcntl) + + PHP_RINIT_FUNCTION(pcntl) + { +- php_add_tick_function(pcntl_signal_dispatch, NULL); ++ php_add_tick_function(pcntl_signal_dispatch_tick_function, NULL); + zend_hash_init(&PCNTL_G(php_signal_table), 16, NULL, ZVAL_PTR_DTOR, 0); + PCNTL_G(head) = PCNTL_G(tail) = PCNTL_G(spares) = NULL; + PCNTL_G(async_signals) = 0; +@@ -1549,6 +1550,11 @@ void pcntl_signal_dispatch() + sigprocmask(SIG_SETMASK, &old_mask, NULL); + } + ++static void pcntl_signal_dispatch_tick_function(int dummy_int, void *dummy_pointer) ++{ ++ return pcntl_signal_dispatch(); ++} ++ + /* {{{ proto bool pcntl_async_signals([bool on[) + Enable/disable asynchronous signal handling and return the old setting. */ + PHP_FUNCTION(pcntl_async_signals) diff --git a/php-7.4.33-tests.patch b/php-7.4.33-tests.patch new file mode 100644 index 0000000..aa8f1d9 --- /dev/null +++ b/php-7.4.33-tests.patch @@ -0,0 +1,26 @@ +From 6974372fbbea4279e1e4f5c24425581208f5553a Mon Sep 17 00:00:00 2001 +From: Nikita Popov <nikita.ppv@gmail.com> +Date: Mon, 18 Oct 2021 12:45:26 +0200 +Subject: [PATCH] Fix bug #81510 + +Make the used arrays larger, because the previous sizes were not +slow enough on some hardware. +--- + Zend/tests/bug74093.phpt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/Zend/tests/bug74093.phpt b/Zend/tests/bug74093.phpt +index 7f20285805bf..c5fc9da8cbbf 100644 +--- a/Zend/tests/bug74093.phpt ++++ b/Zend/tests/bug74093.phpt +@@ -12,8 +12,8 @@ max_execution_time=1 + hard_timeout=1 + --FILE-- + <?php +-$a1 = range(1, 1000000); +-$a2 = range(100000, 1999999); ++$a1 = range(1, 2000000); ++$a2 = range(100000, 2999999); + array_intersect($a1, $a2); + ?> + --EXPECTF-- diff --git a/php-7.4.33-zlib-tests.patch b/php-7.4.33-zlib-tests.patch new file mode 100644 index 0000000..5f2e3fe --- /dev/null +++ b/php-7.4.33-zlib-tests.patch @@ -0,0 +1,462 @@ +Adapted for 7.4 from + + +From 5e12844d4d0053f3c3f1b20635efdb6f30e85190 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 29 Feb 2024 10:52:54 +0100 +Subject: [PATCH] Fix GH-13217 Test failure with zlib-ng + +As compressed result may be different +- drop or relax expectations on compressed data +- rely on uncompressed result of compressed data +--- + ext/zlib/tests/bug48725_2.phpt | 4 +- + ext/zlib/tests/bug71417.phpt | 2 +- + ext/zlib/tests/gzcompress_basic1.phpt | 29 +--------- + ext/zlib/tests/gzcompress_variation1.phpt | 10 ++-- + ext/zlib/tests/gzdeflate_basic1.phpt | 29 +--------- + ext/zlib/tests/gzdeflate_variation1.phpt | 10 ++-- + ext/zlib/tests/gzencode_basic1.phpt | 67 ++++++++++------------- + ext/zlib/tests/gzencode_variation1.phpt | 4 +- + ext/zlib/tests/gzinflate-bug42663.phpt | 2 +- + ext/zlib/tests/zlib_filter_deflate.phpt | 4 +- + 10 files changed, 54 insertions(+), 107 deletions(-) + + +diff --git a/ext/zlib/tests/bug48725_2.phpt b/ext/zlib/tests/bug48725_2.phpt +index 168afd8d3d..6e92a43115 100644 +--- a/ext/zlib/tests/bug48725_2.phpt ++++ b/ext/zlib/tests/bug48725_2.phpt +@@ -9,7 +9,7 @@ if (!extension_loaded('zlib')) die('skip zlib extension not available'); + $stream = fopen('data://text/plain;base64,' . base64_encode('Foo bar baz'), + 'r'); + stream_filter_append($stream, 'zlib.deflate', STREAM_FILTER_READ); +-print bin2hex(stream_get_contents($stream)); ++print gzinflate(stream_get_contents($stream)); + ?> + --EXPECT-- +-72cbcf57484a2c02e22a00000000ffff0300 ++Foo bar baz +diff --git a/ext/zlib/tests/bug71417.phpt b/ext/zlib/tests/bug71417.phpt +index ae1390134e..53c2d82443 100644 +--- a/ext/zlib/tests/bug71417.phpt ++++ b/ext/zlib/tests/bug71417.phpt +@@ -73,7 +73,7 @@ read: bool(false) + gzdecode(): + Warning: gzdecode(): data error in %s on line %d + +-read: string(32) "The quick brown fox jumps over t" ++read: string(3%d) "The quick brown fox jumps over%s" + gzdecode(): + Warning: gzdecode(): data error in %s on line %d + +diff --git a/ext/zlib/tests/gzcompress_basic1.phpt b/ext/zlib/tests/gzcompress_basic1.phpt +index 882fd87fad..5115ff86aa 100644 +--- a/ext/zlib/tests/gzcompress_basic1.phpt ++++ b/ext/zlib/tests/gzcompress_basic1.phpt +@@ -33,7 +33,6 @@ $smallstring = "A small string to compress\n"; + for($i = -1; $i < 10; $i++) { + echo "-- Compression level $i --\n"; + $output = gzcompress($data, $i); +- var_dump(md5($output)); + var_dump(strcmp(gzuncompress($output), $data)); + } + +@@ -41,85 +40,63 @@ for($i = -1; $i < 10; $i++) { + for($i = -1; $i < 10; $i++) { + echo "-- Compression level $i --\n"; + $output = gzcompress($smallstring, $i); +- var_dump(bin2hex($output)); + var_dump(strcmp(gzuncompress($output), $smallstring)); + } + + // Calling gzcompress() with mandatory arguments + echo "\n-- Testing with no specified compression level --\n"; +-var_dump( bin2hex(gzcompress($smallstring) )); ++$output = gzcompress($smallstring); ++var_dump(strcmp(gzuncompress($output), $smallstring)); + + ?> + ===Done=== + --EXPECT-- + *** Testing gzcompress() : basic functionality *** + -- Compression level -1 -- +-string(32) "764809aef15bb34cb73ad49ecb600d99" + int(0) + -- Compression level 0 -- +-string(32) "d0136b3fb5424142c0eb26dfec8f56fe" + int(0) + -- Compression level 1 -- +-string(32) "c2e070f4320d1f674965eaab95b53d9c" + int(0) + -- Compression level 2 -- +-string(32) "36922f486410d08209d0d0d21b26030e" + int(0) + -- Compression level 3 -- +-string(32) "a441a2f5169bb303cd45b860a5a9dbf9" + int(0) + -- Compression level 4 -- +-string(32) "d5b7451e9de2864beccc9de1fc55eb87" + int(0) + -- Compression level 5 -- +-string(32) "32ba4a01120449ec25508cabfad41f56" + int(0) + -- Compression level 6 -- +-string(32) "764809aef15bb34cb73ad49ecb600d99" + int(0) + -- Compression level 7 -- +-string(32) "e083e7e8d05471fed3c2182b9cd0d9eb" + int(0) + -- Compression level 8 -- +-string(32) "e083e7e8d05471fed3c2182b9cd0d9eb" + int(0) + -- Compression level 9 -- +-string(32) "e083e7e8d05471fed3c2182b9cd0d9eb" + int(0) + -- Compression level -1 -- +-string(70) "789c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 0 -- +-string(76) "7801011b00e4ff4120736d616c6c20737472696e6720746f20636f6d70726573730a87a509cb" + int(0) + -- Compression level 1 -- +-string(70) "7801735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 2 -- +-string(70) "785e735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 3 -- +-string(70) "785e735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 4 -- +-string(70) "785e735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 5 -- +-string(70) "785e735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 6 -- +-string(70) "789c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 7 -- +-string(70) "78da735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 8 -- +-string(70) "78da735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + -- Compression level 9 -- +-string(70) "78da735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" + int(0) + + -- Testing with no specified compression level -- +-string(70) "789c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee6020087a509cb" ++int(0) + ===Done=== +diff --git a/ext/zlib/tests/gzcompress_variation1.phpt b/ext/zlib/tests/gzcompress_variation1.phpt +index c4bc0a1ff5..818c8443b2 100644 +--- a/ext/zlib/tests/gzcompress_variation1.phpt ++++ b/ext/zlib/tests/gzcompress_variation1.phpt +@@ -20,15 +20,15 @@ echo "*** Testing gzcompress() : variation ***\n"; + + echo "\n-- Testing multiple compression --\n"; + $output = gzcompress($data); +-var_dump( md5($output)); +-var_dump(md5(gzcompress($output))); ++var_dump(strlen($output)); ++var_dump(strlen(gzcompress($output))); + + ?> + ===Done=== +---EXPECT-- ++--EXPECTF-- + *** Testing gzcompress() : variation *** + + -- Testing multiple compression -- +-string(32) "764809aef15bb34cb73ad49ecb600d99" +-string(32) "eba942bc2061f23ea8688cc5101872a4" ++int(1%d) ++int(1%d) + ===Done=== +diff --git a/ext/zlib/tests/gzdeflate_basic1.phpt b/ext/zlib/tests/gzdeflate_basic1.phpt +index 8ab0ee6243..59de23603e 100644 +--- a/ext/zlib/tests/gzdeflate_basic1.phpt ++++ b/ext/zlib/tests/gzdeflate_basic1.phpt +@@ -33,7 +33,6 @@ $smallstring = "A small string to compress\n"; + for($i = -1; $i < 10; $i++) { + echo "-- Compression level $i --\n"; + $output = gzdeflate($data, $i); +- var_dump(md5($output)); + var_dump(strcmp(gzinflate($output), $data)); + } + +@@ -41,85 +40,63 @@ for($i = -1; $i < 10; $i++) { + for($i = -1; $i < 10; $i++) { + echo "-- Compression level $i --\n"; + $output = gzdeflate($smallstring, $i); +- var_dump(bin2hex($output)); + var_dump(strcmp(gzinflate($output), $smallstring)); + } + + // Calling gzdeflate() with just mandatory arguments + echo "\n-- Testing with no specified compression level --\n"; +-var_dump( bin2hex(gzdeflate($smallstring) )); ++$output = gzdeflate($smallstring); ++var_dump(strcmp(gzinflate($output), $smallstring)); + + ?> + ===Done=== + --EXPECT-- + *** Testing gzdeflate() : basic functionality *** + -- Compression level -1 -- +-string(32) "078554fe65e06f6ff01eab51cfc7ae9b" + int(0) + -- Compression level 0 -- +-string(32) "a71e54d2499aff9e48643cb1c260b60c" + int(0) + -- Compression level 1 -- +-string(32) "05e80f4dc0d422e1f333cbed555d381f" + int(0) + -- Compression level 2 -- +-string(32) "0fb33656e4ed0750f977df83246fce7a" + int(0) + -- Compression level 3 -- +-string(32) "bc6e9c1dccc3e951e006315ee669ee08" + int(0) + -- Compression level 4 -- +-string(32) "a61727d7a28c634470eb6e97a4a81b24" + int(0) + -- Compression level 5 -- +-string(32) "a2a1a14b7542c82e8943200d093d5f27" + int(0) + -- Compression level 6 -- +-string(32) "078554fe65e06f6ff01eab51cfc7ae9b" + int(0) + -- Compression level 7 -- +-string(32) "078554fe65e06f6ff01eab51cfc7ae9b" + int(0) + -- Compression level 8 -- +-string(32) "078554fe65e06f6ff01eab51cfc7ae9b" + int(0) + -- Compression level 9 -- +-string(32) "078554fe65e06f6ff01eab51cfc7ae9b" + int(0) + -- Compression level -1 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 0 -- +-string(64) "011b00e4ff4120736d616c6c20737472696e6720746f20636f6d70726573730a" + int(0) + -- Compression level 1 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 2 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 3 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 4 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 5 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 6 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 7 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 8 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + -- Compression level 9 -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" + int(0) + + -- Testing with no specified compression level -- +-string(58) "735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200" ++int(0) + ===Done=== +diff --git a/ext/zlib/tests/gzdeflate_variation1.phpt b/ext/zlib/tests/gzdeflate_variation1.phpt +index 7993abb529..42bfaa1033 100644 +--- a/ext/zlib/tests/gzdeflate_variation1.phpt ++++ b/ext/zlib/tests/gzdeflate_variation1.phpt +@@ -22,15 +22,15 @@ echo "*** Testing gzdeflate() : variation ***\n"; + + echo "\n-- Testing multiple compression --\n"; + $output = gzdeflate($data); +-var_dump( md5($output)); +-var_dump(md5(gzdeflate($output))); ++var_dump(strlen($output)); ++var_dump(strlen(gzdeflate($output))); + + ?> + ===Done=== +---EXPECT-- ++--EXPECTF-- + *** Testing gzdeflate() : variation *** + + -- Testing multiple compression -- +-string(32) "078554fe65e06f6ff01eab51cfc7ae9b" +-string(32) "86b9f895ef1377da5269ec3cb2729f71" ++int(17%d) ++int(17%d) + ===Done=== +diff --git a/ext/zlib/tests/gzencode_basic1.phpt b/ext/zlib/tests/gzencode_basic1.phpt +index dfb09cc2ed..fe1ec593bd 100644 +--- a/ext/zlib/tests/gzencode_basic1.phpt ++++ b/ext/zlib/tests/gzencode_basic1.phpt +@@ -34,10 +34,7 @@ for($i = -1; $i < 10; $i++) { + echo "-- Compression level $i --\n"; + $output = gzencode($data, $i); + +- // Clear OS byte before encode +- $output[9] = "\x00"; +- +- var_dump(md5($output)); ++ var_dump(strcmp(gzdecode($output), $data)===0); + } + + // Compressing a smaller string +@@ -45,71 +42,70 @@ for($i = -1; $i < 10; $i++) { + echo "-- Compression level $i --\n"; + $output = gzencode($smallstring, $i); + +- // Clear OS byte before encode +- $output[9] = "\x00"; +- +- var_dump(md5($output)); ++ var_dump(strcmp(gzdecode($output), $smallstring)===0); + } + + // Calling gzencode() with mandatory arguments + echo "\n-- Testing with no specified compression level --\n"; +-var_dump(bin2hex(gzencode($smallstring))); ++$output = gzencode($smallstring); ++var_dump(strcmp(gzdecode($output), $smallstring)===0); + + echo "\n-- Testing gzencode with mode specified --\n"; +-var_dump(bin2hex(gzencode($smallstring, -1, FORCE_GZIP))); ++$outupt = gzencode($smallstring, -1, FORCE_GZIP); ++var_dump(strcmp(gzdecode($output), $smallstring)===0); + + ?> + ===Done=== + --EXPECTF-- + *** Testing gzencode() : basic functionality *** + -- Compression level -1 -- +-string(32) "d9ede02415ce91d21e5a94274e2b9c42" ++bool(true) + -- Compression level 0 -- +-string(32) "bbf32d5508e5f1f4e6d42790489dae15" ++bool(true) + -- Compression level 1 -- +-string(32) "0bfaaa7a5a57f8fb533074fca6c85eeb" ++bool(true) + -- Compression level 2 -- +-string(32) "7ddbfed63a76c42808722b66f1c133fc" ++bool(true) + -- Compression level 3 -- +-string(32) "ca2b85d194dfa2a4e8a162b646c99265" ++bool(true) + -- Compression level 4 -- +-string(32) "cfe28033eaf260bc33ddc04b53d3ba39" ++bool(true) + -- Compression level 5 -- +-string(32) "ae357fada2b515422f8bea0aa3bcc48f" ++bool(true) + -- Compression level 6 -- +-string(32) "d9ede02415ce91d21e5a94274e2b9c42" ++bool(true) + -- Compression level 7 -- +-string(32) "d9ede02415ce91d21e5a94274e2b9c42" ++bool(true) + -- Compression level 8 -- +-string(32) "d9ede02415ce91d21e5a94274e2b9c42" ++bool(true) + -- Compression level 9 -- +-string(32) "0f220a09e9895bcb3a1308d2bc99cfdf" ++bool(true) + -- Compression level -1 -- +-string(32) "f77bd31e1e4dd11d12828fb661a08010" ++bool(true) + -- Compression level 0 -- +-string(32) "9c5005db88490d6fe102ea2c233b2872" ++bool(true) + -- Compression level 1 -- +-string(32) "d24ff7c4c20cef69b9c3abd603368db9" ++bool(true) + -- Compression level 2 -- +-string(32) "f77bd31e1e4dd11d12828fb661a08010" ++bool(true) + -- Compression level 3 -- +-string(32) "f77bd31e1e4dd11d12828fb661a08010" ++bool(true) + -- Compression level 4 -- +-string(32) "f77bd31e1e4dd11d12828fb661a08010" ++bool(true) + -- Compression level 5 -- +-string(32) "f77bd31e1e4dd11d12828fb661a08010" ++bool(true) + -- Compression level 6 -- +-string(32) "f77bd31e1e4dd11d12828fb661a08010" ++bool(true) + -- Compression level 7 -- +-string(32) "f77bd31e1e4dd11d12828fb661a08010" ++bool(true) + -- Compression level 8 -- +-string(32) "f77bd31e1e4dd11d12828fb661a08010" ++bool(true) + -- Compression level 9 -- +-string(32) "8849e9a1543c04b3f882b5ce20839ed2" ++bool(true) + + -- Testing with no specified compression level -- +-string(94) "1f8b08000000000000%c%c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200edc4e40b1b000000" ++bool(true) + + -- Testing gzencode with mode specified -- +-string(94) "1f8b08000000000000%c%c735428ce4dccc951282e29cacc4b5728c95748cecf2d284a2d2ee60200edc4e40b1b000000" ++bool(true) + ===Done=== +diff --git a/ext/zlib/tests/gzencode_variation1.phpt b/ext/zlib/tests/gzencode_variation1.phpt +index 2f953168fa..4cd7f61849 100644 +--- a/ext/zlib/tests/gzencode_variation1.phpt ++++ b/ext/zlib/tests/gzencode_variation1.phpt +@@ -33,9 +33,9 @@ var_dump(bin2hex(gzencode($output))); + + ?> + ===Done=== +---EXPECT-- ++--EXPECTF-- + *** Testing gzencode() : variation *** + + -- Testing multiple compression -- +-string(3658) "1f8b0800000000000003010e07f1f81f8b08000000000000036d574d6fe4c80dbdeb57d4ad2f3dfe01eb83e1ec22980e309b4562c067b64449159754dafab0b6e7d7e73d96da1e4c72184c4b2ab2c8f7c847fa25baabba98dc1a8b2b7c38bb324b713ee37f757f56cdc5c7f5b17b9d152f923b157c5ae335e0b75fedd0e2d781c6b98ea3a6ee05affe1dfc3a6527f8f09c52dcb38ba38bb5249934d6ecfe1e53a9ab76ff4c342cf2a64ed2028349fc9a8b139755685352acb82b9fbb67f8bade5cdcb698e1fcec94b7ceba3cb897e806cfc8114350dd1ebbdfa35b62d2478b0056d23ed809b9b95d696d91ce2aa97c911e3fa539c43f84c887554a4d125c9e63ff96711cc08c0866263cb37a0bbe2122ae8f6baecb2284abfb4ddf916db8354cddeef37c1afe5fa02fc7afb3db34f5b3acbdf2eb905490d8f38d7468d253a323d5ebb903760d7944d3b2024e834a99ddce77669bdd823cfbb8e899d4ad4c799677452e6029e80023a03b2374005590641f7d3877df2ad09f3c0e82a54d6a5644fd63049a37ed4bc362016fd9f51264f1e5c630727421ae930b7ed416e93e47b7c71a400390361ffbecb7561bb98f69b5da289e91becc27f08b3b724cb8704f9144d366431d0cb870c56b205deaa2e17636063761a911039fb7e4bf9f06c4f0aecd2ec80e8b41831ca7515e31286166458ea3ef71f2ce7cde2ae269c96d60525724a9c9170b713ed5750758f3cd2a361fc8b288fc92358ce884692e8ea0fe59bd969a0da2eed5831b715749eaae7178f3ebd30fb88c92105f367cce2c882955dc6bf8eca0d5d57540b3092894743ba0fd5b2dad021836191f1afc0bba14dde1642cb0b1aa6879c38907dcefa0720082b801bec61417469219175267dfa047df35b0bd1332001c28cdfafd3bcabe91e74368cdd8d8478e494c190e7ee90c67f2bde288e68ab6b15e883c995be4f8feb6c6dda4278e4f38578ddbdc7be36788daf0c3cb1d1819c73822f7000a0d1813fa94153b572315e51343b536bc64977dff163cebfd8418773261f524017e251fccc60ae29a5770ae097594d52e9c1229d87ce967a36401c46b69945afb249d101c9d420ffa9a123e232c20e76467d5d169202a2dd4c582949e013e745df7958d4b0cc4fd4377a737cd4feea7974070000f314d423e0634cb9a618fdf5dc64fd422181fd59c9230c9f6f9d18dc8fc23e9cccbc7188733b04aa57de83ebea0be3633cff5fa1ff83269be7f44f5a8d84550cc703255fd345dd402034d0b3e11a73ec6e3d4a77f4f685b614329f1b3132ae7af33d02e1e55e291fa6574b758d1f0200e7423dbc852211818043a7c9ce80aa9d59fce0401959f5ea2cf71fde90824f8c9192dbe9d329db143794675ddcf257dd7755273b67340414e3ccad12e3f661f8aad9cf9957dc1275d10a51d3934fa81e68dc6768fb8ee23e373936c8e13feab8b0f50d227f7af76f561fb0950f3d099bbc316c3892a42fb36806d8660e800fa4f43fd4b962d2097d71933a54b77ff948677848eb17bb3a88b621682cfb3bbb49cf42fed6b3944124ad8358ca688aa44dd5f2144c7c9ab16f25b9aca9654ef357ec9ad55c40d324d6cc3d9e3920b863c231d31a95d937fb5520f9c816c79b7dcecc593fb9593cc05a51ebb1eeddd5b49eb437769738d0f64adc579d372b8b7f7c0208487ee3915ebf5766e148ebd77cf4e01f3ec285047011e55838968b6494d517fe29224777b24dd3ddf933101695b102e87db805eef291b74dcfd91628fb2a53f93dbd2968ef2e598746c9204f89fba1f0246fc671610a0591806e46a1346f77c40d910a47c5e20ffb23f003c04b648327a4ed98032c1965bd35bb0044f5344248f56fdb99aa61d6451d68e33489a83bffbe6573541b2da5f64681ea12090f778b2075374778810f73965fa3626a9d41f4df2f83f7c34658cec921b5a9bde49dd5007ec882b02adc514f81aa85898b5cc98e1b137733c0a8789b7f5648d2d231b80bf74978f25d61ce08a8abd11801fd8f995e066676307192ff7641f1cc6e0dee68565b8b22ac3889cd067bf732754a6b270af1044c6a8776811a4f6d8bd0477a9f516064201b920b92d7cd4dc7eee13e6b3eb3528a82f9abb3f388ebe6a8f871393461b73816ec54c99d604174bc5a6801de13908f86aea6a7d0fea107d682bcf1ec348b83872e6b8a316ecd02eb8f8dc86a609bf59a2dd03f1dfa4079436d55e24617be1a2854d008b2b2b1705e2078a7f3946318df1c24f6bf70d4b456eca286ec2b585b28262cc048a098c3e2d5f325a92bb36f691afdc14c822da1b116c9c1c07bb362eb0a04b78834c812134230ebf2044ac2e3c0e3ad00f848dc5010f3bf917ec2fc700b7bf26dacea8440620e04f90f4d97d6dd77cfde8a05c7d3930f1e5811fb8ec5c70964dcc8187ec90e32fdd6b64eec7586413b7d55bed65c4cce39a9b6c15e70e9da94e53fc904e6286f01f5b5562c94211befbc23507e01b2a3865e2f45b5d7b591f290087a5605b82495b4e393f31aa5b37211ec40241a746d903c5eebf117a4d3ddb0d00007b64cbc70e070000" ++string(36%d) "%s" + ===Done=== +diff --git a/ext/zlib/tests/gzinflate-bug42663.phpt b/ext/zlib/tests/gzinflate-bug42663.phpt +index 4f0ca9fa7f..7127eccfdb 100644 +--- a/ext/zlib/tests/gzinflate-bug42663.phpt ++++ b/ext/zlib/tests/gzinflate-bug42663.phpt +@@ -19,7 +19,7 @@ var_dump(gzinflate($truncated)); + ?> + --EXPECTF-- + int(168890) +-int(66743) ++int(667%d) + int(65535) + + Warning: gzinflate(): data error in %s on line %d +diff --git a/ext/zlib/tests/zlib_filter_deflate.phpt b/ext/zlib/tests/zlib_filter_deflate.phpt +index c8e54cb551..9cfa6bb889 100644 +--- a/ext/zlib/tests/zlib_filter_deflate.phpt ++++ b/ext/zlib/tests/zlib_filter_deflate.phpt +@@ -13,5 +13,5 @@ fwrite($fp, $text); + fclose($fp); + + ?> +---EXPECT-- +-HctBDoAgDETRq8zOjfEeHKOGATG0TRpC4u1Vdn/xX4IoxkVMxgP1zA4vkJVhULk9UGkM6TvSNolmxUNlNLePVQ45O3eINf0fsQxtCxwv ++--EXPECTF-- ++HctB%s diff --git a/php-7.4.8-phpinfo.patch b/php-7.4.8-phpinfo.patch index 9b7175e..fa0fff0 100644 --- a/php-7.4.8-phpinfo.patch +++ b/php-7.4.8-phpinfo.patch @@ -6,7 +6,7 @@ The available extensions are not related to this command. diff -up a/ext/standard/info.c.phpinfo v/ext/standard/info.c --- a/ext/standard/info.c.phpinfo 2015-08-18 23:39:24.000000000 +0200 +++ b/ext/standard/info.c 2015-08-22 07:56:18.344761928 +0200 -@@ -809,9 +809,6 @@ PHPAPI void php_print_info(int flag) +@@ -810,9 +810,6 @@ PHPAPI void php_print_info(int flag) #ifdef ARCHITECTURE php_info_print_table_row(2, "Architecture", ARCHITECTURE); #endif @@ -45,7 +45,7 @@ diff --git a/configure.ac b/configure.ac index d9e6329314a3..77f12a55569a 100644 --- a/configure.ac +++ b/configure.ac -@@ -1328,6 +1328,11 @@ PHP_UNAME=${PHP_UNAME:-$UNAME} +@@ -1329,6 +1329,11 @@ PHP_UNAME=${PHP_UNAME:-$UNAME} AC_DEFINE_UNQUOTED(PHP_UNAME,"$PHP_UNAME",[uname -a output]) PHP_OS=`uname | xargs` AC_DEFINE_UNQUOTED(PHP_OS,"$PHP_OS",[uname output]) @@ -61,7 +61,7 @@ diff --git a/ext/standard/info.c b/ext/standard/info.c index 262e95ae2731..f652efd23657 100644 --- a/ext/standard/info.c +++ b/ext/standard/info.c -@@ -803,6 +803,12 @@ PHPAPI ZEND_COLD void php_print_info(int flag) +@@ -804,6 +804,12 @@ PHPAPI ZEND_COLD void php_print_info(int flag) php_info_print_table_start(); php_info_print_table_row(2, "System", ZSTR_VAL(php_uname)); php_info_print_table_row(2, "Build Date", __DATE__ " " __TIME__); diff --git a/php-cve-2022-31631.patch b/php-cve-2022-31631.patch new file mode 100644 index 0000000..4826efc --- /dev/null +++ b/php-cve-2022-31631.patch @@ -0,0 +1,84 @@ +From 7cb160efe19d3dfb8b92629805733ea186b55050 Mon Sep 17 00:00:00 2001 +From: "Christoph M. Becker" <cmbecker69@gmx.de> +Date: Mon, 31 Oct 2022 17:20:23 +0100 +Subject: [PATCH 1/2] Fix #81740: PDO::quote() may return unquoted string + +`sqlite3_snprintf()` expects its first parameter to be `int`; we need +to avoid overflow. + +(cherry picked from commit 921b6813da3237a83e908998483f46ae3d8bacba) +--- + ext/pdo_sqlite/sqlite_driver.c | 3 +++ + ext/pdo_sqlite/tests/bug81740.phpt | 17 +++++++++++++++++ + 2 files changed, 20 insertions(+) + create mode 100644 ext/pdo_sqlite/tests/bug81740.phpt + +diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c +index 0595bd09feb..54f9d05e1e2 100644 +--- a/ext/pdo_sqlite/sqlite_driver.c ++++ b/ext/pdo_sqlite/sqlite_driver.c +@@ -233,6 +233,9 @@ static char *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const char *name, size_t + /* NB: doesn't handle binary strings... use prepared stmts for that */ + static int sqlite_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, char **quoted, size_t *quotedlen, enum pdo_param_type paramtype ) + { ++ if (unquotedlen > (INT_MAX - 3) / 2) { ++ return 0; ++ } + *quoted = safe_emalloc(2, unquotedlen, 3); + sqlite3_snprintf(2*unquotedlen + 3, *quoted, "'%q'", unquoted); + *quotedlen = strlen(*quoted); +diff --git a/ext/pdo_sqlite/tests/bug81740.phpt b/ext/pdo_sqlite/tests/bug81740.phpt +new file mode 100644 +index 00000000000..99fb07c3048 +--- /dev/null ++++ b/ext/pdo_sqlite/tests/bug81740.phpt +@@ -0,0 +1,17 @@ ++--TEST-- ++Bug #81740 (PDO::quote() may return unquoted string) ++--SKIPIF-- ++<?php ++if (!extension_loaded('pdo_sqlite')) print 'skip not loaded'; ++if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); ++?> ++--INI-- ++memory_limit=-1 ++--FILE-- ++<?php ++$pdo = new PDO("sqlite::memory:"); ++$string = str_repeat("a", 0x80000000); ++var_dump($pdo->quote($string)); ++?> ++--EXPECT-- ++bool(false) +-- +2.38.1 + +From 7328f3a0344806b846bd05657bdce96e47810bf0 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Mon, 19 Dec 2022 09:24:02 +0100 +Subject: [PATCH 2/2] NEWS + +--- + NEWS | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/NEWS b/NEWS +index 8a8c0c9285d..03e8c839c77 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,5 +1,12 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ++ ++Backported from 8.0.27 ++ ++- PDO/SQLite: ++ . Fixed bug #81740 (PDO::quote() may return unquoted string). ++ (CVE-2022-31631) (cmb) ++ + 03 Nov 2022, PHP 7.4.33 + + - GD: +-- +2.38.1 + diff --git a/php-cve-2023-0567.patch b/php-cve-2023-0567.patch new file mode 100644 index 0000000..62296ce --- /dev/null +++ b/php-cve-2023-0567.patch @@ -0,0 +1,188 @@ +From 7437aaae38cf4b3357e7580f9e22fd4a403b6c23 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= <tim@bastelstu.be> +Date: Mon, 23 Jan 2023 21:15:24 +0100 +Subject: [PATCH 1/7] crypt: Fix validation of malformed BCrypt hashes +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +PHP’s implementation of crypt_blowfish differs from the upstream Openwall +version by adding a “PHP Hack”, which allows one to cut short the BCrypt salt +by including a `$` character within the characters that represent the salt. + +Hashes that are affected by the “PHP Hack” may erroneously validate any +password as valid when used with `password_verify` and when comparing the +return value of `crypt()` against the input. + +The PHP Hack exists since the first version of PHP’s own crypt_blowfish +implementation that was added in 1e820eca02dcf322b41fd2fe4ed2a6b8309f8ab5. + +No clear reason is given for the PHP Hack’s existence. This commit removes it, +because BCrypt hashes containing a `$` character in their salt are not valid +BCrypt hashes. + +(cherry picked from commit c840f71524067aa474c00c3eacfb83bd860bfc8a) +--- + ext/standard/crypt_blowfish.c | 8 -- + .../tests/crypt/bcrypt_salt_dollar.phpt | 82 +++++++++++++++++++ + 2 files changed, 82 insertions(+), 8 deletions(-) + create mode 100644 ext/standard/tests/crypt/bcrypt_salt_dollar.phpt + +diff --git a/ext/standard/crypt_blowfish.c b/ext/standard/crypt_blowfish.c +index c1f945f29ed..aa7e1bc2e68 100644 +--- a/ext/standard/crypt_blowfish.c ++++ b/ext/standard/crypt_blowfish.c +@@ -376,7 +376,6 @@ static unsigned char BF_atoi64[0x60] = { + #define BF_safe_atoi64(dst, src) \ + { \ + tmp = (unsigned char)(src); \ +- if (tmp == '$') break; /* PHP hack */ \ + if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \ + tmp = BF_atoi64[tmp]; \ + if (tmp > 63) return -1; \ +@@ -404,13 +403,6 @@ static int BF_decode(BF_word *dst, const char *src, int size) + *dptr++ = ((c3 & 0x03) << 6) | c4; + } while (dptr < end); + +- if (end - dptr == size) { +- return -1; +- } +- +- while (dptr < end) /* PHP hack */ +- *dptr++ = 0; +- + return 0; + } + +diff --git a/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt b/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt +new file mode 100644 +index 00000000000..32e335f4b08 +--- /dev/null ++++ b/ext/standard/tests/crypt/bcrypt_salt_dollar.phpt +@@ -0,0 +1,82 @@ ++--TEST-- ++bcrypt correctly rejects salts containing $ ++--FILE-- ++<?php ++for ($i = 0; $i < 23; $i++) { ++ $salt = '$2y$04$' . str_repeat('0', $i) . '$'; ++ $result = crypt("foo", $salt); ++ var_dump($salt); ++ var_dump($result); ++ var_dump($result === $salt); ++} ++?> ++--EXPECT-- ++string(8) "$2y$04$$" ++string(2) "*0" ++bool(false) ++string(9) "$2y$04$0$" ++string(2) "*0" ++bool(false) ++string(10) "$2y$04$00$" ++string(2) "*0" ++bool(false) ++string(11) "$2y$04$000$" ++string(2) "*0" ++bool(false) ++string(12) "$2y$04$0000$" ++string(2) "*0" ++bool(false) ++string(13) "$2y$04$00000$" ++string(2) "*0" ++bool(false) ++string(14) "$2y$04$000000$" ++string(2) "*0" ++bool(false) ++string(15) "$2y$04$0000000$" ++string(2) "*0" ++bool(false) ++string(16) "$2y$04$00000000$" ++string(2) "*0" ++bool(false) ++string(17) "$2y$04$000000000$" ++string(2) "*0" ++bool(false) ++string(18) "$2y$04$0000000000$" ++string(2) "*0" ++bool(false) ++string(19) "$2y$04$00000000000$" ++string(2) "*0" ++bool(false) ++string(20) "$2y$04$000000000000$" ++string(2) "*0" ++bool(false) ++string(21) "$2y$04$0000000000000$" ++string(2) "*0" ++bool(false) ++string(22) "$2y$04$00000000000000$" ++string(2) "*0" ++bool(false) ++string(23) "$2y$04$000000000000000$" ++string(2) "*0" ++bool(false) ++string(24) "$2y$04$0000000000000000$" ++string(2) "*0" ++bool(false) ++string(25) "$2y$04$00000000000000000$" ++string(2) "*0" ++bool(false) ++string(26) "$2y$04$000000000000000000$" ++string(2) "*0" ++bool(false) ++string(27) "$2y$04$0000000000000000000$" ++string(2) "*0" ++bool(false) ++string(28) "$2y$04$00000000000000000000$" ++string(2) "*0" ++bool(false) ++string(29) "$2y$04$000000000000000000000$" ++string(2) "*0" ++bool(false) ++string(30) "$2y$04$0000000000000000000000$" ++string(60) "$2y$04$000000000000000000000u2a2UpVexIt9k3FMJeAVr3c04F5tcI8K" ++bool(false) +-- +2.39.1 + +From ed0281b588a6840cb95f3134a4e68847a3be5bb7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= <tim@bastelstu.be> +Date: Mon, 23 Jan 2023 22:13:57 +0100 +Subject: [PATCH 2/7] crypt: Fix possible buffer overread in php_crypt() + +(cherry picked from commit a92acbad873a05470af1a47cb785a18eadd827b5) +--- + ext/standard/crypt.c | 1 + + ext/standard/tests/password/password_bcrypt_short.phpt | 8 ++++++++ + 2 files changed, 9 insertions(+) + create mode 100644 ext/standard/tests/password/password_bcrypt_short.phpt + +diff --git a/ext/standard/crypt.c b/ext/standard/crypt.c +index 92430b69f77..04487f3fe5a 100644 +--- a/ext/standard/crypt.c ++++ b/ext/standard/crypt.c +@@ -151,6 +151,7 @@ PHPAPI zend_string *php_crypt(const char *password, const int pass_len, const ch + } else if ( + salt[0] == '$' && + salt[1] == '2' && ++ salt[2] != 0 && + salt[3] == '$') { + char output[PHP_MAX_SALT_LEN + 1]; + +diff --git a/ext/standard/tests/password/password_bcrypt_short.phpt b/ext/standard/tests/password/password_bcrypt_short.phpt +new file mode 100644 +index 00000000000..085bc8a2390 +--- /dev/null ++++ b/ext/standard/tests/password/password_bcrypt_short.phpt +@@ -0,0 +1,8 @@ ++--TEST-- ++Test that password_hash() does not overread buffers when a short hash is passed ++--FILE-- ++<?php ++var_dump(password_verify("foo", '$2')); ++?> ++--EXPECT-- ++bool(false) +-- +2.39.1 + diff --git a/php-cve-2023-0568.patch b/php-cve-2023-0568.patch new file mode 100644 index 0000000..6e2ba19 --- /dev/null +++ b/php-cve-2023-0568.patch @@ -0,0 +1,98 @@ +From 887cd0710ad856a0d22c329b6ea6c71ebd8621ae Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Fri, 27 Jan 2023 19:28:27 +0100 +Subject: [PATCH 3/7] Fix array overrun when appending slash to paths + +Fix it by extending the array sizes by one character. As the input is +limited to the maximum path length, there will always be place to append +the slash. As the php_check_specific_open_basedir() simply uses the +strings to compare against each other, no new failures related to too +long paths are introduced. +We'll let the DOM and XML case handle a potentially too long path in the +library code. + +(cherry picked from commit ec10b28d64decbc54aa1e585dce580f0bd7a5953) +--- + ext/dom/document.c | 2 +- + ext/xmlreader/php_xmlreader.c | 2 +- + main/fopen_wrappers.c | 6 +++--- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/ext/dom/document.c b/ext/dom/document.c +index b478e1a1aab..e683eb8f701 100644 +--- a/ext/dom/document.c ++++ b/ext/dom/document.c +@@ -1380,7 +1380,7 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so + int validate, recover, resolve_externals, keep_blanks, substitute_ent; + int resolved_path_len; + int old_error_reporting = 0; +- char *directory=NULL, resolved_path[MAXPATHLEN]; ++ char *directory=NULL, resolved_path[MAXPATHLEN + 1]; + + if (id != NULL) { + intern = Z_DOMOBJ_P(id); +diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c +index 06f569949ce..ecc81ad1470 100644 +--- a/ext/xmlreader/php_xmlreader.c ++++ b/ext/xmlreader/php_xmlreader.c +@@ -1038,7 +1038,7 @@ PHP_METHOD(xmlreader, XML) + xmlreader_object *intern = NULL; + char *source, *uri = NULL, *encoding = NULL; + int resolved_path_len, ret = 0; +- char *directory=NULL, resolved_path[MAXPATHLEN]; ++ char *directory=NULL, resolved_path[MAXPATHLEN + 1]; + xmlParserInputBufferPtr inputbfr; + xmlTextReaderPtr reader; + +diff --git a/main/fopen_wrappers.c b/main/fopen_wrappers.c +index 27135020fa3..90de040a218 100644 +--- a/main/fopen_wrappers.c ++++ b/main/fopen_wrappers.c +@@ -138,10 +138,10 @@ PHPAPI ZEND_INI_MH(OnUpdateBaseDir) + */ + PHPAPI int php_check_specific_open_basedir(const char *basedir, const char *path) + { +- char resolved_name[MAXPATHLEN]; +- char resolved_basedir[MAXPATHLEN]; ++ char resolved_name[MAXPATHLEN + 1]; ++ char resolved_basedir[MAXPATHLEN + 1]; + char local_open_basedir[MAXPATHLEN]; +- char path_tmp[MAXPATHLEN]; ++ char path_tmp[MAXPATHLEN + 1]; + char *path_file; + size_t resolved_basedir_len; + size_t resolved_name_len; +-- +2.39.1 + +From 614468ce4056c0ef93aae09532dcffdf65b594b5 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Mon, 13 Feb 2023 11:46:47 +0100 +Subject: [PATCH 4/7] NEWS + +--- + NEWS | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/NEWS b/NEWS +index 03e8c839c77..8157a20d4b3 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,14 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.0.28 ++ ++- Core: ++ . Fixed bug #81744 (Password_verify() always return true with some hash). ++ (CVE-2023-0567). (Tim Düsterhus) ++ . Fixed bug #81746 (1-byte array overrun in common path resolve code). ++ (CVE-2023-0568). (Niels Dossche) ++ + Backported from 8.0.27 + + - PDO/SQLite: +-- +2.39.1 + diff --git a/php-cve-2023-0662.patch b/php-cve-2023-0662.patch new file mode 100644 index 0000000..0a18a88 --- /dev/null +++ b/php-cve-2023-0662.patch @@ -0,0 +1,143 @@ +From 3a2fdef1ae38881110006616ee1f0534b082ca45 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Thu, 19 Jan 2023 14:11:18 +0000 +Subject: [PATCH 5/7] Fix repeated warning for file uploads limit exceeding + +--- + main/rfc1867.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/main/rfc1867.c b/main/rfc1867.c +index edef19c16d6..4931b9aeefb 100644 +--- a/main/rfc1867.c ++++ b/main/rfc1867.c +@@ -922,7 +922,10 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ + skip_upload = 1; + } else if (upload_cnt <= 0) { + skip_upload = 1; +- sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded"); ++ if (upload_cnt == 0) { ++ --upload_cnt; ++ sapi_module.sapi_error(E_WARNING, "Maximum number of allowable file uploads has been exceeded"); ++ } + } + + /* Return with an error if the posted data is garbled */ +-- +2.39.1 + +From 8ec78d28d20c82c75c4747f44c52601cfdb22516 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Thu, 19 Jan 2023 14:31:25 +0000 +Subject: [PATCH 6/7] Introduce max_multipart_body_parts INI + +This fixes GHSA-54hq-v5wp-fqgv DOS vulnerabality by limitting number of +parsed multipart body parts as currently all parts were always parsed. +--- + main/main.c | 1 + + main/rfc1867.c | 11 +++++++++++ + 2 files changed, 12 insertions(+) + +diff --git a/main/main.c b/main/main.c +index 0b33b2b56c9..d8c465988cc 100644 +--- a/main/main.c ++++ b/main/main.c +@@ -836,6 +836,7 @@ PHP_INI_BEGIN() + PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL) + PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL) ++ PHP_INI_ENTRY("max_multipart_body_parts", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL) + + STD_PHP_INI_BOOLEAN("allow_url_fopen", "1", PHP_INI_SYSTEM, OnUpdateBool, allow_url_fopen, php_core_globals, core_globals) + STD_PHP_INI_BOOLEAN("allow_url_include", "0", PHP_INI_SYSTEM, OnUpdateBool, allow_url_include, php_core_globals, core_globals) +diff --git a/main/rfc1867.c b/main/rfc1867.c +index 4931b9aeefb..1b212c93325 100644 +--- a/main/rfc1867.c ++++ b/main/rfc1867.c +@@ -694,6 +694,7 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ + void *event_extra_data = NULL; + unsigned int llen = 0; + int upload_cnt = INI_INT("max_file_uploads"); ++ int body_parts_cnt = INI_INT("max_multipart_body_parts"); + const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding(); + php_rfc1867_getword_t getword; + php_rfc1867_getword_conf_t getword_conf; +@@ -715,6 +716,11 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ + return; + } + ++ if (body_parts_cnt < 0) { ++ body_parts_cnt = PG(max_input_vars) + upload_cnt; ++ } ++ int body_parts_limit = body_parts_cnt; ++ + /* Get the boundary */ + boundary = strstr(content_type_dup, "boundary"); + if (!boundary) { +@@ -799,6 +805,11 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ + char *pair = NULL; + int end = 0; + ++ if (--body_parts_cnt < 0) { ++ php_error_docref(NULL, E_WARNING, "Multipart body parts limit exceeded %d. To increase the limit change max_multipart_body_parts in php.ini.", body_parts_limit); ++ goto fileupload_done; ++ } ++ + while (isspace(*cd)) { + ++cd; + } +-- +2.39.1 + +From 472db3ee3a00ac00d36019eee0b3b7362334481c Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 14 Feb 2023 09:14:47 +0100 +Subject: [PATCH 7/7] NEWS + +--- + NEWS | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/NEWS b/NEWS +index 8157a20d4b3..c1668368818 100644 +--- a/NEWS ++++ b/NEWS +@@ -9,6 +9,10 @@ Backported from 8.0.28 + . Fixed bug #81746 (1-byte array overrun in common path resolve code). + (CVE-2023-0568). (Niels Dossche) + ++- FPM: ++ . Fixed bug GHSA-54hq-v5wp-fqgv (DOS vulnerability when parsing multipart ++ request body). (CVE-2023-0662) (Jakub Zelenka) ++ + Backported from 8.0.27 + + - PDO/SQLite: +-- +2.39.1 + +From c04f310440a906fc4ca885f4ecf6e3e4cd36edc7 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 14 Feb 2023 11:47:22 +0100 +Subject: [PATCH] fix NEWS, not FPM specific + +--- + NEWS | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/NEWS b/NEWS +index c1668368818..3f8739eae78 100644 +--- a/NEWS ++++ b/NEWS +@@ -8,8 +8,6 @@ Backported from 8.0.28 + (CVE-2023-0567). (Tim Düsterhus) + . Fixed bug #81746 (1-byte array overrun in common path resolve code). + (CVE-2023-0568). (Niels Dossche) +- +-- FPM: + . Fixed bug GHSA-54hq-v5wp-fqgv (DOS vulnerability when parsing multipart + request body). (CVE-2023-0662) (Jakub Zelenka) + +-- +2.39.1 + diff --git a/php-cve-2023-3247.patch b/php-cve-2023-3247.patch new file mode 100644 index 0000000..e23aebf --- /dev/null +++ b/php-cve-2023-3247.patch @@ -0,0 +1,152 @@ +From 0cfca9aa1395271833848daec0bace51d965531d Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Sun, 16 Apr 2023 15:05:03 +0200 +Subject: [PATCH] Fix missing randomness check and insufficient random bytes + for SOAP HTTP Digest +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If php_random_bytes_throw fails, the nonce will be uninitialized, but +still sent to the server. The client nonce is intended to protect +against a malicious server. See section 5.10 and 5.12 of RFC 7616 [1], +and bullet point 2 below. + +Tim pointed out that even though it's the MD5 of the nonce that gets sent, +enumerating 31 bits is trivial. So we have still a stack information leak +of 31 bits. + +Furthermore, Tim found the following issues: +* The small size of cnonce might cause the server to erroneously reject + a request due to a repeated (cnonce, nc) pair. As per the birthday + problem 31 bits of randomness will return a duplication with 50% + chance after less than 55000 requests and nc always starts counting at 1. +* The cnonce is intended to protect the client and password against a + malicious server that returns a constant server nonce where the server + precomputed a rainbow table between passwords and correct client response. + As storage is fairly cheap, a server could precompute the client responses + for (a subset of) client nonces and still have a chance of reversing the + client response with the same probability as the cnonce duplication. + + Precomputing the rainbow table for all 2^31 cnonces increases the rainbow + table size by factor 2 billion, which is infeasible. But precomputing it + for 2^14 cnonces only increases the table size by factor 16k and the server + would still have a 10% chance of successfully reversing a password with a + single client request. + +This patch fixes the issues by increasing the nonce size, and checking +the return value of php_random_bytes_throw(). In the process we also get +rid of the MD5 hashing of the nonce. + +[1] RFC 7616: https://www.rfc-editor.org/rfc/rfc7616 + +Co-authored-by: Tim Düsterhus <timwolla@php.net> +(cherry picked from commit 126d517ce240e9f638d9a5eaa509eaca49ef562a) +--- + NEWS | 6 ++++++ + ext/soap/php_http.c | 21 +++++++++++++-------- + 2 files changed, 19 insertions(+), 8 deletions(-) + +diff --git a/NEWS b/NEWS +index 3f8739eae7..7c07635cad 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,12 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.0.29 ++ ++- Soap: ++ . Fixed bug GHSA-76gg-c692-v2mw (Missing error check and insufficient random ++ bytes in HTTP Digest authentication for SOAP). (nielsdos, timwolla) ++ + Backported from 8.0.28 + + - Core: +diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c +index ee3dcbdc9a..e3a9afdbe9 100644 +--- a/ext/soap/php_http.c ++++ b/ext/soap/php_http.c +@@ -666,18 +666,23 @@ int make_http_soap_request(zval *this_ptr, + if ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) != NULL) { + if (Z_TYPE_P(digest) == IS_ARRAY) { + char HA1[33], HA2[33], response[33], cnonce[33], nc[9]; +- zend_long nonce; ++ unsigned char nonce[16]; + PHP_MD5_CTX md5ctx; + unsigned char hash[16]; + +- php_random_bytes_throw(&nonce, sizeof(nonce)); +- nonce &= 0x7fffffff; ++ if (UNEXPECTED(php_random_bytes_throw(&nonce, sizeof(nonce)) != SUCCESS)) { ++ ZEND_ASSERT(EG(exception)); ++ php_stream_close(stream); ++ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpurl", sizeof("httpurl")-1); ++ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "httpsocket", sizeof("httpsocket")-1); ++ zend_hash_str_del(Z_OBJPROP_P(this_ptr), "_use_proxy", sizeof("_use_proxy")-1); ++ smart_str_free(&soap_headers_z); ++ smart_str_free(&soap_headers); ++ return FALSE; ++ } + +- PHP_MD5Init(&md5ctx); +- snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, nonce); +- PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce)); +- PHP_MD5Final(hash, &md5ctx); +- make_digest(cnonce, hash); ++ php_hash_bin2hex(cnonce, nonce, sizeof(nonce)); ++ cnonce[32] = 0; + + if ((tmp = zend_hash_str_find(Z_ARRVAL_P(digest), "nc", sizeof("nc")-1)) != NULL && + Z_TYPE_P(tmp) == IS_LONG) { +From 40439039c224bb8cdebd1b7b3d03b8cc11e7cce7 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 6 Jun 2023 18:05:22 +0200 +Subject: [PATCH] Fix GH-11382 add missing hash header for bin2hex + +--- + ext/soap/php_http.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c +index e3a9afdbe9f..912b8e341d8 100644 +--- a/ext/soap/php_http.c ++++ b/ext/soap/php_http.c +@@ -22,6 +22,7 @@ + #include "ext/standard/base64.h" + #include "ext/standard/md5.h" + #include "ext/standard/php_random.h" ++#include "ext/hash/php_hash.h" + + static char *get_http_header_value_nodup(char *headers, char *type, size_t *len); + static char *get_http_header_value(char *headers, char *type); +-- +2.40.1 + +From f3021d66d7bb42d2578530cc94f9bde47e58eb10 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 15 Jun 2023 08:47:55 +0200 +Subject: [PATCH] add cve + +--- + NEWS | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/NEWS b/NEWS +index 7c07635cade..899644b3d63 100644 +--- a/NEWS ++++ b/NEWS +@@ -5,7 +5,8 @@ Backported from 8.0.29 + + - Soap: + . Fixed bug GHSA-76gg-c692-v2mw (Missing error check and insufficient random +- bytes in HTTP Digest authentication for SOAP). (nielsdos, timwolla) ++ bytes in HTTP Digest authentication for SOAP). ++ (CVE-2023-3247) (nielsdos, timwolla) + + Backported from 8.0.28 + +-- +2.40.1 + diff --git a/php-cve-2023-3823.patch b/php-cve-2023-3823.patch new file mode 100644 index 0000000..a795564 --- /dev/null +++ b/php-cve-2023-3823.patch @@ -0,0 +1,89 @@ +From c398fe98c044c8e7c23135acdc38d4ef7bedc983 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Mon, 10 Jul 2023 13:25:34 +0200 +Subject: [PATCH 1/4] Fix buffer mismanagement in phar_dir_read() + +Fixes GHSA-jqcx-ccgc-xwhv. + +(cherry picked from commit 80316123f3e9dcce8ac419bd9dd43546e2ccb5ef) +--- + ext/phar/dirstream.c | 15 ++++++++------ + ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt | 27 +++++++++++++++++++++++++ + 2 files changed, 36 insertions(+), 6 deletions(-) + create mode 100644 ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt + +diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c +index 4710703c70e..490b14528f1 100644 +--- a/ext/phar/dirstream.c ++++ b/ext/phar/dirstream.c +@@ -91,25 +91,28 @@ static int phar_dir_seek(php_stream *stream, zend_off_t offset, int whence, zend + */ + static ssize_t phar_dir_read(php_stream *stream, char *buf, size_t count) /* {{{ */ + { +- size_t to_read; + HashTable *data = (HashTable *)stream->abstract; + zend_string *str_key; + zend_ulong unused; + ++ if (count != sizeof(php_stream_dirent)) { ++ return -1; ++ } ++ + if (HASH_KEY_NON_EXISTENT == zend_hash_get_current_key(data, &str_key, &unused)) { + return 0; + } + + zend_hash_move_forward(data); +- to_read = MIN(ZSTR_LEN(str_key), count); + +- if (to_read == 0 || count < ZSTR_LEN(str_key)) { ++ php_stream_dirent *dirent = (php_stream_dirent *) buf; ++ ++ if (sizeof(dirent->d_name) <= ZSTR_LEN(str_key)) { + return 0; + } + +- memset(buf, 0, sizeof(php_stream_dirent)); +- memcpy(((php_stream_dirent *) buf)->d_name, ZSTR_VAL(str_key), to_read); +- ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0'; ++ memset(dirent, 0, sizeof(php_stream_dirent)); ++ PHP_STRLCPY(dirent->d_name, ZSTR_VAL(str_key), sizeof(dirent->d_name), ZSTR_LEN(str_key)); + + return sizeof(php_stream_dirent); + } +diff --git a/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt b/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt +new file mode 100644 +index 00000000000..4e12f05fb62 +--- /dev/null ++++ b/ext/phar/tests/GHSA-jqcx-ccgc-xwhv.phpt +@@ -0,0 +1,27 @@ ++--TEST-- ++GHSA-jqcx-ccgc-xwhv (Buffer overflow and overread in phar_dir_read()) ++--SKIPIF-- ++<?php if (!extension_loaded("phar")) die("skip"); ?> ++--INI-- ++phar.readonly=0 ++--FILE-- ++<?php ++$phar = new Phar(__DIR__. '/GHSA-jqcx-ccgc-xwhv.phar'); ++$phar->startBuffering(); ++$phar->addFromString(str_repeat('A', PHP_MAXPATHLEN - 1), 'This is the content of file 1.'); ++$phar->addFromString(str_repeat('B', PHP_MAXPATHLEN - 1).'C', 'This is the content of file 2.'); ++$phar->stopBuffering(); ++ ++$handle = opendir('phar://' . __DIR__ . '/GHSA-jqcx-ccgc-xwhv.phar'); ++var_dump(strlen(readdir($handle))); ++// Must not be a string of length PHP_MAXPATHLEN+1 ++var_dump(readdir($handle)); ++closedir($handle); ++?> ++--CLEAN-- ++<?php ++unlink(__DIR__. '/GHSA-jqcx-ccgc-xwhv.phar'); ++?> ++--EXPECTF-- ++int(%d) ++bool(false) +-- +2.41.0 + diff --git a/php-cve-2023-3824.patch b/php-cve-2023-3824.patch new file mode 100644 index 0000000..9b8b9c4 --- /dev/null +++ b/php-cve-2023-3824.patch @@ -0,0 +1,644 @@ +From b3758bd21223b97c042cae7bd26a66cde081ea98 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Sat, 15 Jul 2023 17:33:52 +0200 +Subject: [PATCH 2/4] Sanitize libxml2 globals before parsing + +Fixes GHSA-3qrf-m4j2-pcrr. + +To parse a document with libxml2, you first need to create a parsing context. +The parsing context contains parsing options (e.g. XML_NOENT to substitute +entities) that the application (in this case PHP) can set. +Unfortunately, libxml2 also supports providing default set options. +For example, if you call xmlSubstituteEntitiesDefault(1) then the XML_NOENT +option will be added to the parsing options every time you create a parsing +context **even if the application never requested XML_NOENT**. + +Third party extensions can override these globals, in particular the +substitute entity global. This causes entity substitution to be +unexpectedly active. + +Fix it by setting the parsing options to a sane known value. +For API calls that depend on global state we introduce +PHP_LIBXML_SANITIZE_GLOBALS() and PHP_LIBXML_RESTORE_GLOBALS(). +For other APIs that work directly with a context we introduce +php_libxml_sanitize_parse_ctxt_options(). + +(cherry picked from commit c283c3ab0ba45d21b2b8745c1f9c7cbfe771c975) +--- + ext/dom/document.c | 15 ++++++++ + ext/dom/documentfragment.c | 2 ++ + ...xml_global_state_entity_loader_bypass.phpt | 36 +++++++++++++++++++ + ext/libxml/php_libxml.h | 36 +++++++++++++++++++ + ext/simplexml/simplexml.c | 6 ++++ + ...xml_global_state_entity_loader_bypass.phpt | 36 +++++++++++++++++++ + ext/soap/php_xml.c | 2 ++ + ext/xml/compat.c | 2 ++ + ext/xmlreader/php_xmlreader.c | 9 +++++ + ...xml_global_state_entity_loader_bypass.phpt | 35 ++++++++++++++++++ + ext/xsl/xsltprocessor.c | 9 +++-- + 11 files changed, 183 insertions(+), 5 deletions(-) + create mode 100644 ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt + create mode 100644 ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt + create mode 100644 ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt + +diff --git a/ext/dom/document.c b/ext/dom/document.c +index e683eb8f701..989b5b3dd24 100644 +--- a/ext/dom/document.c ++++ b/ext/dom/document.c +@@ -1459,6 +1459,7 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so + options |= XML_PARSE_NOBLANKS; + } + ++ php_libxml_sanitize_parse_ctxt_options(ctxt); + xmlCtxtUseOptions(ctxt, options); + + ctxt->recovery = recover; +@@ -1759,7 +1760,9 @@ PHP_FUNCTION(dom_document_xinclude) + + DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + ++ PHP_LIBXML_SANITIZE_GLOBALS(xinclude); + err = xmlXIncludeProcessFlags(docp, (int)flags); ++ PHP_LIBXML_RESTORE_GLOBALS(xinclude); + + /* XML_XINCLUDE_START and XML_XINCLUDE_END nodes need to be removed as these + are added via xmlXIncludeProcess to mark beginning and ending of xincluded document +@@ -1799,6 +1802,7 @@ PHP_FUNCTION(dom_document_validate) + + DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + ++ PHP_LIBXML_SANITIZE_GLOBALS(validate); + cvp = xmlNewValidCtxt(); + + cvp->userData = NULL; +@@ -1810,6 +1814,7 @@ PHP_FUNCTION(dom_document_validate) + } else { + RETVAL_FALSE; + } ++ PHP_LIBXML_RESTORE_GLOBALS(validate); + + xmlFreeValidCtxt(cvp); + +@@ -1844,14 +1849,18 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type + + DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + ++ PHP_LIBXML_SANITIZE_GLOBALS(new_parser_ctxt); ++ + switch (type) { + case DOM_LOAD_FILE: + if (CHECK_NULL_PATH(source, source_len)) { ++ PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt); + php_error_docref(NULL, E_WARNING, "Invalid Schema file source"); + RETURN_FALSE; + } + valid_file = _dom_get_valid_file_path(source, resolved_path, MAXPATHLEN); + if (!valid_file) { ++ PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt); + php_error_docref(NULL, E_WARNING, "Invalid Schema file source"); + RETURN_FALSE; + } +@@ -1872,6 +1881,7 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type + parser); + sptr = xmlSchemaParse(parser); + xmlSchemaFreeParserCtxt(parser); ++ PHP_LIBXML_RESTORE_GLOBALS(new_parser_ctxt); + if (!sptr) { + php_error_docref(NULL, E_WARNING, "Invalid Schema"); + RETURN_FALSE; +@@ -1890,11 +1900,13 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type + valid_opts |= XML_SCHEMA_VAL_VC_I_CREATE; + } + ++ PHP_LIBXML_SANITIZE_GLOBALS(validate); + xmlSchemaSetValidOptions(vptr, valid_opts); + xmlSchemaSetValidErrors(vptr, php_libxml_error_handler, php_libxml_error_handler, vptr); + is_valid = xmlSchemaValidateDoc(vptr, docp); + xmlSchemaFree(sptr); + xmlSchemaFreeValidCtxt(vptr); ++ PHP_LIBXML_RESTORE_GLOBALS(validate); + + if (is_valid == 0) { + RETURN_TRUE; +@@ -1965,12 +1977,14 @@ static void _dom_document_relaxNG_validate(INTERNAL_FUNCTION_PARAMETERS, int typ + return; + } + ++ PHP_LIBXML_SANITIZE_GLOBALS(parse); + xmlRelaxNGSetParserErrors(parser, + (xmlRelaxNGValidityErrorFunc) php_libxml_error_handler, + (xmlRelaxNGValidityWarningFunc) php_libxml_error_handler, + parser); + sptr = xmlRelaxNGParse(parser); + xmlRelaxNGFreeParserCtxt(parser); ++ PHP_LIBXML_RESTORE_GLOBALS(parse); + if (!sptr) { + php_error_docref(NULL, E_WARNING, "Invalid RelaxNG"); + RETURN_FALSE; +@@ -2069,6 +2083,7 @@ static void dom_load_html(INTERNAL_FUNCTION_PARAMETERS, int mode) /* {{{ */ + ctxt->sax->error = php_libxml_ctx_error; + ctxt->sax->warning = php_libxml_ctx_warning; + } ++ php_libxml_sanitize_parse_ctxt_options(ctxt); + if (options) { + htmlCtxtUseOptions(ctxt, (int)options); + } +diff --git a/ext/dom/documentfragment.c b/ext/dom/documentfragment.c +index 9b222586ac5..711c42f939d 100644 +--- a/ext/dom/documentfragment.c ++++ b/ext/dom/documentfragment.c +@@ -131,7 +131,9 @@ PHP_METHOD(domdocumentfragment, appendXML) { + } + + if (data) { ++ PHP_LIBXML_SANITIZE_GLOBALS(parse); + err = xmlParseBalancedChunkMemory(nodep->doc, NULL, NULL, 0, (xmlChar *) data, &lst); ++ PHP_LIBXML_RESTORE_GLOBALS(parse); + if (err != 0) { + RETURN_FALSE; + } +diff --git a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt +new file mode 100644 +index 00000000000..b28afd4694e +--- /dev/null ++++ b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt +@@ -0,0 +1,36 @@ ++--TEST-- ++GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) ++--SKIPIF-- ++<?php ++if (!extension_loaded('libxml')) die('skip libxml extension not available'); ++if (!extension_loaded('dom')) die('skip dom extension not available'); ++if (!extension_loaded('zend-test')) die('skip zend-test extension not available'); ++?> ++--FILE-- ++<?php ++ ++$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>"; ++ ++libxml_use_internal_errors(true); ++ ++function parseXML($xml) { ++ $doc = new DOMDocument(); ++ @$doc->loadXML($xml); ++ $doc->createDocumentFragment()->appendXML("&bork;"); ++ foreach (libxml_get_errors() as $error) { ++ var_dump(trim($error->message)); ++ } ++} ++ ++parseXML($xml); ++zend_test_override_libxml_global_state(); ++parseXML($xml); ++ ++echo "Done\n"; ++ ++?> ++--EXPECT-- ++string(25) "Entity 'bork' not defined" ++string(25) "Entity 'bork' not defined" ++string(25) "Entity 'bork' not defined" ++Done +diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h +index cf936e95de1..92028d5703e 100644 +--- a/ext/libxml/php_libxml.h ++++ b/ext/libxml/php_libxml.h +@@ -121,6 +121,42 @@ PHP_LIBXML_API void php_libxml_shutdown(void); + ZEND_TSRMLS_CACHE_EXTERN() + #endif + ++/* Other extension may override the global state options, these global options ++ * are copied initially to ctxt->options. Set the options to a known good value. ++ * See libxml2 globals.c and parserInternals.c. ++ * The unique_name argument allows multiple sanitizes and restores within the ++ * same function, even nested is necessary. */ ++#define PHP_LIBXML_SANITIZE_GLOBALS(unique_name) \ ++ int xml_old_loadsubset_##unique_name = xmlLoadExtDtdDefaultValue; \ ++ xmlLoadExtDtdDefaultValue = 0; \ ++ int xml_old_validate_##unique_name = xmlDoValidityCheckingDefaultValue; \ ++ xmlDoValidityCheckingDefaultValue = 0; \ ++ int xml_old_pedantic_##unique_name = xmlPedanticParserDefault(0); \ ++ int xml_old_substitute_##unique_name = xmlSubstituteEntitiesDefault(0); \ ++ int xml_old_linenrs_##unique_name = xmlLineNumbersDefault(0); \ ++ int xml_old_blanks_##unique_name = xmlKeepBlanksDefault(1); ++ ++#define PHP_LIBXML_RESTORE_GLOBALS(unique_name) \ ++ xmlLoadExtDtdDefaultValue = xml_old_loadsubset_##unique_name; \ ++ xmlDoValidityCheckingDefaultValue = xml_old_validate_##unique_name; \ ++ (void) xmlPedanticParserDefault(xml_old_pedantic_##unique_name); \ ++ (void) xmlSubstituteEntitiesDefault(xml_old_substitute_##unique_name); \ ++ (void) xmlLineNumbersDefault(xml_old_linenrs_##unique_name); \ ++ (void) xmlKeepBlanksDefault(xml_old_blanks_##unique_name); ++ ++/* Alternative for above, working directly on the context and not setting globals. ++ * Generally faster because no locking is involved, and this has the advantage that it sets the options to a known good value. */ ++static zend_always_inline void php_libxml_sanitize_parse_ctxt_options(xmlParserCtxtPtr ctxt) ++{ ++ ctxt->loadsubset = 0; ++ ctxt->validate = 0; ++ ctxt->pedantic = 0; ++ ctxt->replaceEntities = 0; ++ ctxt->linenumbers = 0; ++ ctxt->keepBlanks = 1; ++ ctxt->options = 0; ++} ++ + #else /* HAVE_LIBXML */ + #define libxml_module_ptr NULL + #endif +diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c +index 2cdff0e648d..101a9d8fd8c 100644 +--- a/ext/simplexml/simplexml.c ++++ b/ext/simplexml/simplexml.c +@@ -2194,7 +2194,9 @@ PHP_FUNCTION(simplexml_load_file) + RETURN_FALSE; + } + ++ PHP_LIBXML_SANITIZE_GLOBALS(read_file); + docp = xmlReadFile(filename, NULL, (int)options); ++ PHP_LIBXML_RESTORE_GLOBALS(read_file); + + if (!docp) { + RETURN_FALSE; +@@ -2248,7 +2250,9 @@ PHP_FUNCTION(simplexml_load_string) + RETURN_FALSE; + } + ++ PHP_LIBXML_SANITIZE_GLOBALS(read_memory); + docp = xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options); ++ PHP_LIBXML_RESTORE_GLOBALS(read_memory); + + if (!docp) { + RETURN_FALSE; +@@ -2298,7 +2302,9 @@ SXE_METHOD(__construct) + return; + } + ++ PHP_LIBXML_SANITIZE_GLOBALS(read_file_or_memory); + docp = is_url ? xmlReadFile(data, NULL, (int)options) : xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options); ++ PHP_LIBXML_RESTORE_GLOBALS(read_file_or_memory); + + if (!docp) { + ((php_libxml_node_object *)sxe)->document = NULL; +diff --git a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt +new file mode 100644 +index 00000000000..2152e012328 +--- /dev/null ++++ b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt +@@ -0,0 +1,36 @@ ++--TEST-- ++GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) ++--SKIPIF-- ++<?php ++if (!extension_loaded('libxml')) die('skip libxml extension not available'); ++if (!extension_loaded('simplexml')) die('skip simplexml extension not available'); ++if (!extension_loaded('zend-test')) die('skip zend-test extension not available'); ++?> ++--FILE-- ++<?php ++ ++$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>"; ++ ++libxml_use_internal_errors(true); ++zend_test_override_libxml_global_state(); ++ ++echo "--- String test ---\n"; ++simplexml_load_string($xml); ++echo "--- Constructor test ---\n"; ++new SimpleXMLElement($xml); ++echo "--- File test ---\n"; ++file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml); ++simplexml_load_file("libxml_global_state_entity_loader_bypass.tmp"); ++ ++echo "Done\n"; ++ ++?> ++--CLEAN-- ++<?php ++@unlink("libxml_global_state_entity_loader_bypass.tmp"); ++?> ++--EXPECT-- ++--- String test --- ++--- Constructor test --- ++--- File test --- ++Done +diff --git a/ext/soap/php_xml.c b/ext/soap/php_xml.c +index 18a266179b7..1bb7fa00a37 100644 +--- a/ext/soap/php_xml.c ++++ b/ext/soap/php_xml.c +@@ -93,6 +93,7 @@ xmlDocPtr soap_xmlParseFile(const char *filename) + if (ctxt) { + zend_bool old; + ++ php_libxml_sanitize_parse_ctxt_options(ctxt); + ctxt->keepBlanks = 0; + ctxt->sax->ignorableWhitespace = soap_ignorableWhitespace; + ctxt->sax->comment = soap_Comment; +@@ -141,6 +142,7 @@ xmlDocPtr soap_xmlParseMemory(const void *buf, size_t buf_size) + if (ctxt) { + zend_bool old; + ++ php_libxml_sanitize_parse_ctxt_options(ctxt); + ctxt->sax->ignorableWhitespace = soap_ignorableWhitespace; + ctxt->sax->comment = soap_Comment; + ctxt->sax->warning = NULL; +diff --git a/ext/xml/compat.c b/ext/xml/compat.c +index fc4525650fc..57eb00dd429 100644 +--- a/ext/xml/compat.c ++++ b/ext/xml/compat.c +@@ -19,6 +19,7 @@ + #include "php.h" + #if defined(HAVE_LIBXML) && (defined(HAVE_XML) || defined(HAVE_XMLRPC)) && !defined(HAVE_LIBEXPAT) + #include "expat_compat.h" ++#include "ext/libxml/php_libxml.h" + + typedef struct _php_xml_ns { + xmlNsPtr nsptr; +@@ -471,6 +472,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *m + return NULL; + } + ++ php_libxml_sanitize_parse_ctxt_options(parser->parser); + xmlCtxtUseOptions(parser->parser, XML_PARSE_OLDSAX); + + parser->parser->replaceEntities = 1; +diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c +index ecc81ad1470..51d6bb9c9f2 100644 +--- a/ext/xmlreader/php_xmlreader.c ++++ b/ext/xmlreader/php_xmlreader.c +@@ -304,6 +304,7 @@ static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, size_t source_len, siz + return NULL; + } + ++ PHP_LIBXML_SANITIZE_GLOBALS(parse); + if (error_func || warn_func) { + xmlRelaxNGSetParserErrors(parser, + (xmlRelaxNGValidityErrorFunc) error_func, +@@ -312,6 +313,7 @@ static xmlRelaxNGPtr _xmlreader_get_relaxNG(char *source, size_t source_len, siz + } + sptr = xmlRelaxNGParse(parser); + xmlRelaxNGFreeParserCtxt(parser); ++ PHP_LIBXML_RESTORE_GLOBALS(parse); + + return sptr; + } +@@ -881,7 +883,9 @@ PHP_METHOD(xmlreader, open) + valid_file = _xmlreader_get_valid_file_path(source, resolved_path, MAXPATHLEN ); + + if (valid_file) { ++ PHP_LIBXML_SANITIZE_GLOBALS(reader_for_file); + reader = xmlReaderForFile(valid_file, encoding, options); ++ PHP_LIBXML_RESTORE_GLOBALS(reader_for_file); + } + + if (reader == NULL) { +@@ -958,7 +962,9 @@ PHP_METHOD(xmlreader, setSchema) + + intern = Z_XMLREADER_P(id); + if (intern && intern->ptr) { ++ PHP_LIBXML_SANITIZE_GLOBALS(schema); + retval = xmlTextReaderSchemaValidate(intern->ptr, source); ++ PHP_LIBXML_RESTORE_GLOBALS(schema); + + if (retval == 0) { + RETURN_TRUE; +@@ -1082,6 +1088,7 @@ PHP_METHOD(xmlreader, XML) + } + uri = (char *) xmlCanonicPath((const xmlChar *) resolved_path); + } ++ PHP_LIBXML_SANITIZE_GLOBALS(text_reader); + reader = xmlNewTextReader(inputbfr, uri); + + if (reader != NULL) { +@@ -1100,9 +1107,11 @@ PHP_METHOD(xmlreader, XML) + xmlFree(uri); + } + ++ PHP_LIBXML_RESTORE_GLOBALS(text_reader); + return; + } + } ++ PHP_LIBXML_RESTORE_GLOBALS(text_reader); + } + + if (uri) { +diff --git a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt +new file mode 100644 +index 00000000000..e9ffb04c2bb +--- /dev/null ++++ b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt +@@ -0,0 +1,35 @@ ++--TEST-- ++GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) ++--SKIPIF-- ++<?php ++if (!extension_loaded('libxml')) die('skip libxml extension not available'); ++if (!extension_loaded('xmlreader')) die('skip xmlreader extension not available'); ++if (!extension_loaded('zend-test')) die('skip zend-test extension not available'); ++?> ++--FILE-- ++<?php ++ ++$xml = "<?xml version='1.0'?><!DOCTYPE root [<!ENTITY % bork SYSTEM \"php://nope\"> %bork;]><nothing/>"; ++ ++libxml_use_internal_errors(true); ++zend_test_override_libxml_global_state(); ++ ++echo "--- String test ---\n"; ++$reader = XMLReader::xml($xml); ++$reader->read(); ++echo "--- File test ---\n"; ++file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml); ++$reader = XMLReader::open("libxml_global_state_entity_loader_bypass.tmp"); ++$reader->read(); ++ ++echo "Done\n"; ++ ++?> ++--CLEAN-- ++<?php ++@unlink("libxml_global_state_entity_loader_bypass.tmp"); ++?> ++--EXPECT-- ++--- String test --- ++--- File test --- ++Done +diff --git a/ext/xsl/xsltprocessor.c b/ext/xsl/xsltprocessor.c +index 079920d0ffa..2d95b2ff4bb 100644 +--- a/ext/xsl/xsltprocessor.c ++++ b/ext/xsl/xsltprocessor.c +@@ -398,7 +398,7 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) + xmlDoc *doc = NULL, *newdoc = NULL; + xsltStylesheetPtr sheetp, oldsheetp; + xsl_object *intern; +- int prevSubstValue, prevExtDtdValue, clone_docu = 0; ++ int clone_docu = 0; + xmlNode *nodep = NULL; + zval *cloneDocu, member, rv; + +@@ -421,13 +421,12 @@ PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet) + stylesheet document otherwise the node proxies will be a mess */ + newdoc = xmlCopyDoc(doc, 1); + xmlNodeSetBase((xmlNodePtr) newdoc, (xmlChar *)doc->URL); +- prevSubstValue = xmlSubstituteEntitiesDefault(1); +- prevExtDtdValue = xmlLoadExtDtdDefaultValue; ++ PHP_LIBXML_SANITIZE_GLOBALS(parse); ++ xmlSubstituteEntitiesDefault(1); + xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS; + + sheetp = xsltParseStylesheetDoc(newdoc); +- xmlSubstituteEntitiesDefault(prevSubstValue); +- xmlLoadExtDtdDefaultValue = prevExtDtdValue; ++ PHP_LIBXML_RESTORE_GLOBALS(parse); + + if (!sheetp) { + xmlFreeDoc(newdoc); +-- +2.41.0 + +From ef1d507acf7be23d7624dc3c891683b2218feb51 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 1 Aug 2023 07:22:33 +0200 +Subject: [PATCH 3/4] NEWS + +--- + NEWS | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/NEWS b/NEWS +index 899644b3d63..4f88029a7d6 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,16 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.0.30 ++ ++- Libxml: ++ . Fixed bug GHSA-3qrf-m4j2-pcrr (Security issue with external entity loading ++ in XML without enabling it). (CVE-2023-3823) (nielsdos, ilutov) ++ ++- Phar: ++ . Fixed bug GHSA-jqcx-ccgc-xwhv (Buffer mismanagement in phar_dir_read()). ++ (CVE-2023-3824) (nielsdos) ++ + Backported from 8.0.29 + + - Soap: +-- +2.41.0 + +From 24e669e790e6aebd219c9a9fa19017455c8646b4 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 1 Aug 2023 07:37:25 +0200 +Subject: [PATCH 4/4] backport zend_test changes + (zend_test_override_libxml_global_state) + +--- + ...xml_global_state_entity_loader_bypass.phpt | 1 + + ...xml_global_state_entity_loader_bypass.phpt | 1 + + ...xml_global_state_entity_loader_bypass.phpt | 5 +++-- + ext/zend_test/test.c | 22 +++++++++++++++++++ + 4 files changed, 27 insertions(+), 2 deletions(-) + +diff --git a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt +index b28afd4694e..7fc2a249ac7 100644 +--- a/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt ++++ b/ext/dom/tests/libxml_global_state_entity_loader_bypass.phpt +@@ -5,6 +5,7 @@ GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) + if (!extension_loaded('libxml')) die('skip libxml extension not available'); + if (!extension_loaded('dom')) die('skip dom extension not available'); + if (!extension_loaded('zend-test')) die('skip zend-test extension not available'); ++if (!function_exists('zend_test_override_libxml_global_state')) die('skip not for Windows'); + ?> + --FILE-- + <?php +diff --git a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt +index 2152e012328..54f9d4941eb 100644 +--- a/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt ++++ b/ext/simplexml/tests/libxml_global_state_entity_loader_bypass.phpt +@@ -5,6 +5,7 @@ GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) + if (!extension_loaded('libxml')) die('skip libxml extension not available'); + if (!extension_loaded('simplexml')) die('skip simplexml extension not available'); + if (!extension_loaded('zend-test')) die('skip zend-test extension not available'); ++if (!function_exists('zend_test_override_libxml_global_state')) die('skip not for Windows'); + ?> + --FILE-- + <?php +diff --git a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt +index e9ffb04c2bb..b0120b325ef 100644 +--- a/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt ++++ b/ext/xmlreader/tests/libxml_global_state_entity_loader_bypass.phpt +@@ -5,6 +5,7 @@ GHSA-3qrf-m4j2-pcrr (libxml global state entity loader bypass) + if (!extension_loaded('libxml')) die('skip libxml extension not available'); + if (!extension_loaded('xmlreader')) die('skip xmlreader extension not available'); + if (!extension_loaded('zend-test')) die('skip zend-test extension not available'); ++if (!function_exists('zend_test_override_libxml_global_state')) die('skip not for Windows'); + ?> + --FILE-- + <?php +@@ -15,11 +16,11 @@ libxml_use_internal_errors(true); + zend_test_override_libxml_global_state(); + + echo "--- String test ---\n"; +-$reader = XMLReader::xml($xml); ++$reader = @XMLReader::xml($xml); + $reader->read(); + echo "--- File test ---\n"; + file_put_contents("libxml_global_state_entity_loader_bypass.tmp", $xml); +-$reader = XMLReader::open("libxml_global_state_entity_loader_bypass.tmp"); ++$reader = @XMLReader::open("libxml_global_state_entity_loader_bypass.tmp"); + $reader->read(); + + echo "Done\n"; +diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c +index 4f81adc6ac1..cdfc15571c0 100644 +--- a/ext/zend_test/test.c ++++ b/ext/zend_test/test.c +@@ -25,6 +25,11 @@ + #include "ext/standard/info.h" + #include "php_test.h" + ++#if defined(HAVE_LIBXML) && !defined(PHP_WIN32) ++# include <libxml/globals.h> ++# include <libxml/parser.h> ++#endif ++ + static zend_class_entry *zend_test_interface; + static zend_class_entry *zend_test_class; + static zend_class_entry *zend_test_child_class; +@@ -48,6 +53,20 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_leak_variable, 0, 0, 1) + ZEND_ARG_INFO(0, variable) + ZEND_END_ARG_INFO() + ++#if defined(HAVE_LIBXML) && !defined(PHP_WIN32) ++static ZEND_FUNCTION(zend_test_override_libxml_global_state) ++{ ++ ZEND_PARSE_PARAMETERS_NONE(); ++ ++ xmlLoadExtDtdDefaultValue = 1; ++ xmlDoValidityCheckingDefaultValue = 1; ++ (void) xmlPedanticParserDefault(1); ++ (void) xmlSubstituteEntitiesDefault(1); ++ (void) xmlLineNumbersDefault(1); ++ (void) xmlKeepBlanksDefault(0); ++} ++#endif ++ + ZEND_FUNCTION(zend_test_func) + { + /* dummy */ +@@ -297,6 +316,9 @@ static const zend_function_entry zend_test_functions[] = { + ZEND_FE(zend_terminate_string, arginfo_zend_terminate_string) + ZEND_FE(zend_leak_bytes, NULL) + ZEND_FE(zend_leak_variable, arginfo_zend_leak_variable) ++#if defined(HAVE_LIBXML) && !defined(PHP_WIN32) ++ ZEND_FE(zend_test_override_libxml_global_state, NULL) ++#endif + ZEND_FE_END + }; + +-- +2.41.0 + diff --git a/php-cve-2024-11233.patch b/php-cve-2024-11233.patch new file mode 100644 index 0000000..d6c29ae --- /dev/null +++ b/php-cve-2024-11233.patch @@ -0,0 +1,68 @@ +From 44a5975f83a02eb8169d12af912e6222b28216d0 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Fri, 8 Nov 2024 22:04:21 +0100 +Subject: [PATCH 5/7] Fix GHSA-r977-prxv-hc43 + +Move the bound check upwards. Since this doesn't generate output we can +check the bound first. + +(cherry picked from commit 81030c9bbb5cd2e740b8398bb7212df9709f0274) +(cherry picked from commit 2cee10a1206f5bc7724232d3988be2cfcb0bc9df) +--- + ext/standard/filters.c | 7 ++++--- + ext/standard/tests/filters/ghsa-r977-prxv-hc43.phpt | 12 ++++++++++++ + 2 files changed, 16 insertions(+), 3 deletions(-) + create mode 100644 ext/standard/tests/filters/ghsa-r977-prxv-hc43.phpt + +diff --git a/ext/standard/filters.c b/ext/standard/filters.c +index 018270c730d..5d5745c6bec 100644 +--- a/ext/standard/filters.c ++++ b/ext/standard/filters.c +@@ -1128,6 +1128,9 @@ static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *ins + } break; + + case 5: { ++ if (icnt == 0) { ++ goto out; ++ } + if (!inst->lbchars && lb_cnt == 1 && *ps == '\n') { + /* auto-detect soft line breaks, found network line break */ + lb_cnt = lb_ptr = 0; +@@ -1141,15 +1144,13 @@ static php_conv_err_t php_conv_qprint_decode_convert(php_conv_qprint_decode *ins + /* soft line break */ + lb_cnt = lb_ptr = 0; + scan_stat = 0; +- } else if (icnt > 0) { ++ } else { + if (*ps == (unsigned char)inst->lbchars[lb_cnt]) { + lb_cnt++; + ps++, icnt--; + } else { + scan_stat = 6; /* no break for short-cut */ + } +- } else { +- goto out; + } + } break; + +diff --git a/ext/standard/tests/filters/ghsa-r977-prxv-hc43.phpt b/ext/standard/tests/filters/ghsa-r977-prxv-hc43.phpt +new file mode 100644 +index 00000000000..8fdcce8ff22 +--- /dev/null ++++ b/ext/standard/tests/filters/ghsa-r977-prxv-hc43.phpt +@@ -0,0 +1,12 @@ ++--TEST-- ++GHSA-r977-prxv-hc43: Single byte overread with convert.quoted-printable-decode filter ++--FILE-- ++<?php ++ ++$input_data = str_repeat('A', 8189)."X=\r"; ++$filter_url = "php://filter/convert.quoted-printable-decode/resource=data:," . urlencode($input_data); ++var_dump(file_get_contents($filter_url)); ++ ++?> ++--EXPECT-- ++string(8190) "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAX" +-- +2.47.0 + diff --git a/php-cve-2024-11234.patch b/php-cve-2024-11234.patch new file mode 100644 index 0000000..0fd31f3 --- /dev/null +++ b/php-cve-2024-11234.patch @@ -0,0 +1,95 @@ +From 494de65139592da0e5e5b6fdf198c2f9c762f4d6 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Fri, 8 Nov 2024 23:43:47 +0100 +Subject: [PATCH 3/7] Fix GHSA-c5f2-jwm7-mmq2: stream HTTP fulluri CRLF + injection + +(cherry picked from commit 426a6d4539ebee34879ac5de857036bb6ff0e732) +(cherry picked from commit bc1f192102dd8cbda028e40aa31604c4885d387c) +(cherry picked from commit 8d130e16fbfda7d154fedfa0f1ff1d5ad5e26815) +--- + ext/standard/http_fopen_wrapper.c | 18 ++++++++---- + .../tests/http/ghsa-c5f2-jwm7-mmq2.phpt | 28 +++++++++++++++++++ + 2 files changed, 40 insertions(+), 6 deletions(-) + create mode 100644 ext/standard/tests/http/ghsa-c5f2-jwm7-mmq2.phpt + +diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c +index 4d918b21e65..aeeb438f0f9 100644 +--- a/ext/standard/http_fopen_wrapper.c ++++ b/ext/standard/http_fopen_wrapper.c +@@ -186,6 +186,11 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, + return NULL; + } + ++ /* Should we send the entire path in the request line, default to no. */ ++ if (context && (tmpzval = php_stream_context_get_option(context, "http", "request_fulluri")) != NULL) { ++ request_fulluri = zend_is_true(tmpzval); ++ } ++ + use_ssl = resource->scheme && (ZSTR_LEN(resource->scheme) > 4) && ZSTR_VAL(resource->scheme)[4] == 's'; + /* choose default ports */ + if (use_ssl && resource->port == 0) +@@ -205,6 +210,13 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, + } + } + ++ if (request_fulluri && (strchr(path, '\n') != NULL || strchr(path, '\r') != NULL)) { ++ php_stream_wrapper_log_error(wrapper, options, "HTTP wrapper full URI path does not allow CR or LF characters"); ++ php_url_free(resource); ++ efree(transport_string); ++ return NULL; ++ } ++ + if (context && (tmpzval = php_stream_context_get_option(context, wrapper->wops->label, "timeout")) != NULL) { + double d = zval_get_double(tmpzval); + #ifndef PHP_WIN32 +@@ -385,12 +397,6 @@ finish: + smart_str_appends(&req_buf, "GET "); + } + +- /* Should we send the entire path in the request line, default to no. */ +- if (!request_fulluri && context && +- (tmpzval = php_stream_context_get_option(context, "http", "request_fulluri")) != NULL) { +- request_fulluri = zend_is_true(tmpzval); +- } +- + if (request_fulluri) { + /* Ask for everything */ + smart_str_appends(&req_buf, path); +diff --git a/ext/standard/tests/http/ghsa-c5f2-jwm7-mmq2.phpt b/ext/standard/tests/http/ghsa-c5f2-jwm7-mmq2.phpt +new file mode 100644 +index 00000000000..5b2e04f94f2 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-c5f2-jwm7-mmq2.phpt +@@ -0,0 +1,28 @@ ++--TEST-- ++GHSA-c5f2-jwm7-mmq2 (Configuring a proxy in a stream context might allow for CRLF injection in URIs) ++--INI-- ++allow_url_fopen=1 ++--CONFLICTS-- ++server ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++echo $_SERVER['REQUEST_URI']; ++CODE; ++ ++include __DIR__."/../../../../sapi/cli/tests/php_cli_server.inc"; ++php_cli_server_start($serverCode, null, []); ++ ++$host = PHP_CLI_SERVER_ADDRESS; ++$userinput = "index.php HTTP/1.1\r\nHost: $host\r\n\r\nGET /index2.php HTTP/1.1\r\nHost: $host\r\n\r\nGET /index.php"; ++$context = stream_context_create(['http' => ['proxy' => 'tcp://' . $host, 'request_fulluri' => true]]); ++echo file_get_contents("http://$host/$userinput", false, $context); ++?> ++--EXPECTF-- ++Warning: file_get_contents(http://localhost:%d/index.php HTTP/1.1 ++Host: localhost:%d ++ ++GET /index2.php HTTP/1.1 ++Host: localhost:%d ++ ++GET /index.php): failed to open stream: HTTP wrapper full URI path does not allow CR or LF characters in %s on line %d +-- +2.47.0 + diff --git a/php-cve-2024-11236.patch b/php-cve-2024-11236.patch new file mode 100644 index 0000000..e917cfc --- /dev/null +++ b/php-cve-2024-11236.patch @@ -0,0 +1,119 @@ +From 97546df8d6900b115536c17af9213f1da837b82e Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Thu, 24 Oct 2024 22:02:17 +0200 +Subject: [PATCH 1/7] Fix GHSA-5hqh-c84r-qjcv: Integer overflow in the dblib + quoter causing OOB writes + +(cherry picked from commit d9baa9fed8c3ba692a36b388c0c7762e5102e2e0) +(cherry picked from commit 5d9e54065ed18c51e4f25d8900635f90810c7394) +--- + ext/pdo_dblib/dblib_driver.c | 8 ++++++- + ext/pdo_dblib/tests/GHSA-5hqh-c84r-qjcv.phpt | 24 ++++++++++++++++++++ + 2 files changed, 31 insertions(+), 1 deletion(-) + create mode 100644 ext/pdo_dblib/tests/GHSA-5hqh-c84r-qjcv.phpt + +diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c +index f36451afeeb..1dc75a4d2e3 100644 +--- a/ext/pdo_dblib/dblib_driver.c ++++ b/ext/pdo_dblib/dblib_driver.c +@@ -154,6 +154,7 @@ static int dblib_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unqu + + size_t i; + char * q; ++ size_t extralen = 0; + *quotedlen = 0; + + if (H->assume_national_character_set_strings) { +@@ -168,7 +169,7 @@ static int dblib_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unqu + + /* Detect quoted length, adding extra char for doubled single quotes */ + for (i = 0; i < unquotedlen; i++) { +- if (unquoted[i] == '\'') ++*quotedlen; ++ if (unquoted[i] == '\'') ++extralen; + ++*quotedlen; + } + +@@ -176,6 +177,11 @@ static int dblib_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unqu + if (use_national_character_set) { + ++*quotedlen; /* N prefix */ + } ++ if (UNEXPECTED(*quotedlen > ZSTR_MAX_LEN - extralen)) { ++ return 0; ++ } ++ ++ *quotedlen += extralen; + q = *quoted = emalloc(*quotedlen + 1); /* Add byte for terminal null */ + if (use_national_character_set) { + *q++ = 'N'; +diff --git a/ext/pdo_dblib/tests/GHSA-5hqh-c84r-qjcv.phpt b/ext/pdo_dblib/tests/GHSA-5hqh-c84r-qjcv.phpt +new file mode 100644 +index 00000000000..431c61951ee +--- /dev/null ++++ b/ext/pdo_dblib/tests/GHSA-5hqh-c84r-qjcv.phpt +@@ -0,0 +1,24 @@ ++--TEST-- ++GHSA-5hqh-c84r-qjcv (Integer overflow in the dblib quoter causing OOB writes) ++--EXTENSIONS-- ++pdo_dblib ++--SKIPIF-- ++<?php ++if (PHP_INT_SIZE != 4) die("skip for 32bit platforms only"); ++if (PHP_OS_FAMILY === "Windows") die("skip not for Windows because the virtual address space for application is only 2GiB"); ++if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); ++require __DIR__ . '/config.inc'; ++getDbConnection(); ++?> ++--INI-- ++memory_limit=-1 ++--FILE-- ++<?php ++ ++require __DIR__ . '/config.inc'; ++$db = getDbConnection(); ++var_dump($db->quote(str_repeat("'", 2147483646))); ++ ++?> ++--EXPECT-- ++bool(false) +-- +2.47.0 + +From 0530cbfe5c3044537de52d8382eba5d69dbac726 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Thu, 24 Oct 2024 22:02:36 +0200 +Subject: [PATCH 2/7] Fix GHSA-5hqh-c84r-qjcv: Integer overflow in the firebird + quoter causing OOB writes + +(cherry picked from commit 69c5f68fdc3deed9ebce2cc44b4bf5e0c47cd28f) +(cherry picked from commit b4f73be75dbdde970a18cc7a636898b10400fb3f) +--- + ext/pdo_firebird/firebird_driver.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c +index 3e403afd368..5b74290abcc 100644 +--- a/ext/pdo_firebird/firebird_driver.c ++++ b/ext/pdo_firebird/firebird_driver.c +@@ -243,7 +243,7 @@ free_statement: + static int firebird_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unquotedlen, /* {{{ */ + char **quoted, size_t *quotedlen, enum pdo_param_type paramtype) + { +- int qcount = 0; ++ size_t qcount = 0; + char const *co, *l, *r; + char *c; + +@@ -258,6 +258,10 @@ static int firebird_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t u + /* count the number of ' characters */ + for (co = unquoted; (co = strchr(co,'\'')); qcount++, co++); + ++ if (UNEXPECTED(unquotedlen + 2 > ZSTR_MAX_LEN - qcount)) { ++ return 0; ++ } ++ + *quotedlen = unquotedlen + qcount + 2; + *quoted = c = emalloc(*quotedlen+1); + *c++ = '\''; +-- +2.47.0 + diff --git a/php-cve-2024-2756.patch b/php-cve-2024-2756.patch new file mode 100644 index 0000000..d927518 --- /dev/null +++ b/php-cve-2024-2756.patch @@ -0,0 +1,193 @@ +From a6c1c62a25ac23b08a86af11d68f0e2eaafc102b Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Sun, 17 Mar 2024 21:04:47 +0100 +Subject: [PATCH 1/4] Fix GHSA-wpj3-hf5j-x4v4: __Host-/__Secure- cookie bypass + due to partial CVE-2022-31629 fix + +The check happened too early as later code paths may perform more +mangling rules. Move the check downwards right before adding the actual +variable. + +(cherry picked from commit 093c08af25fb323efa0c8e6154aa9fdeae3d3b53) +(cherry picked from commit 2e07a3acd7a6b53c55325b94bed97748d7697b53) +--- + ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt | 63 +++++++++++++++++++++ + main/php_variables.c | 41 +++++++++----- + 2 files changed, 90 insertions(+), 14 deletions(-) + create mode 100644 ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt + +diff --git a/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt b/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt +new file mode 100644 +index 00000000000..77fcb680894 +--- /dev/null ++++ b/ext/standard/tests/ghsa-wpj3-hf5j-x4v4.phpt +@@ -0,0 +1,63 @@ ++--TEST-- ++ghsa-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to partial CVE-2022-31629 fix) ++--COOKIE-- ++..Host-test=ignore_1; ++._Host-test=ignore_2; ++.[Host-test=ignore_3; ++_.Host-test=ignore_4; ++__Host-test=ignore_5; ++_[Host-test=ignore_6; ++[.Host-test=ignore_7; ++[_Host-test=ignore_8; ++[[Host-test=ignore_9; ++..Host-test[]=ignore_10; ++._Host-test[]=ignore_11; ++.[Host-test[]=ignore_12; ++_.Host-test[]=ignore_13; ++__Host-test[]=legitimate_14; ++_[Host-test[]=legitimate_15; ++[.Host-test[]=ignore_16; ++[_Host-test[]=ignore_17; ++[[Host-test[]=ignore_18; ++..Secure-test=ignore_1; ++._Secure-test=ignore_2; ++.[Secure-test=ignore_3; ++_.Secure-test=ignore_4; ++__Secure-test=ignore_5; ++_[Secure-test=ignore_6; ++[.Secure-test=ignore_7; ++[_Secure-test=ignore_8; ++[[Secure-test=ignore_9; ++..Secure-test[]=ignore_10; ++._Secure-test[]=ignore_11; ++.[Secure-test[]=ignore_12; ++_.Secure-test[]=ignore_13; ++__Secure-test[]=legitimate_14; ++_[Secure-test[]=legitimate_15; ++[.Secure-test[]=ignore_16; ++[_Secure-test[]=ignore_17; ++[[Secure-test[]=ignore_18; ++--FILE-- ++<?php ++var_dump($_COOKIE); ++?> ++--EXPECT-- ++array(3) { ++ ["__Host-test"]=> ++ array(1) { ++ [0]=> ++ string(13) "legitimate_14" ++ } ++ ["_"]=> ++ array(2) { ++ ["Host-test["]=> ++ string(13) "legitimate_15" ++ ["Secure-test["]=> ++ string(13) "legitimate_15" ++ } ++ ["__Secure-test"]=> ++ array(1) { ++ [0]=> ++ string(13) "legitimate_14" ++ } ++} +diff --git a/main/php_variables.c b/main/php_variables.c +index 18f6b65a6c5..e971d497337 100644 +--- a/main/php_variables.c ++++ b/main/php_variables.c +@@ -65,6 +65,21 @@ static zend_always_inline void php_register_variable_quick(const char *name, siz + zend_string_release_ex(key, 0); + } + ++/* Discard variable if mangling made it start with __Host-, where pre-mangling it did not start with __Host- ++ * Discard variable if mangling made it start with __Secure-, where pre-mangling it did not start with __Secure- */ ++static zend_bool php_is_forbidden_variable_name(const char *mangled_name, size_t mangled_name_len, const char *pre_mangled_name) ++{ ++ if (mangled_name_len >= sizeof("__Host-")-1 && strncmp(mangled_name, "__Host-", sizeof("__Host-")-1) == 0 && strncmp(pre_mangled_name, "__Host-", sizeof("__Host-")-1) != 0) { ++ return 1; ++ } ++ ++ if (mangled_name_len >= sizeof("__Secure-")-1 && strncmp(mangled_name, "__Secure-", sizeof("__Secure-")-1) == 0 && strncmp(pre_mangled_name, "__Secure-", sizeof("__Secure-")-1) != 0) { ++ return 1; ++ } ++ ++ return 0; ++} ++ + PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars_array) + { + char *p = NULL; +@@ -115,20 +130,6 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars + } + var_len = p - var; + +- /* Discard variable if mangling made it start with __Host-, where pre-mangling it did not start with __Host- */ +- if (strncmp(var, "__Host-", sizeof("__Host-")-1) == 0 && strncmp(var_name, "__Host-", sizeof("__Host-")-1) != 0) { +- zval_ptr_dtor_nogc(val); +- free_alloca(var_orig, use_heap); +- return; +- } +- +- /* Discard variable if mangling made it start with __Secure-, where pre-mangling it did not start with __Secure- */ +- if (strncmp(var, "__Secure-", sizeof("__Secure-")-1) == 0 && strncmp(var_name, "__Secure-", sizeof("__Secure-")-1) != 0) { +- zval_ptr_dtor_nogc(val); +- free_alloca(var_orig, use_heap); +- return; +- } +- + if (var_len==0) { /* empty variable name, or variable name with a space in it */ + zval_ptr_dtor_nogc(val); + free_alloca(var_orig, use_heap); +@@ -226,6 +227,12 @@ PHPAPI void php_register_variable_ex(char *var_name, zval *val, zval *track_vars + return; + } + } else { ++ if (php_is_forbidden_variable_name(index, index_len, var_name)) { ++ zval_ptr_dtor_nogc(val); ++ free_alloca(var_orig, use_heap); ++ return; ++ } ++ + gpc_element_p = zend_symtable_str_find(symtable1, index, index_len); + if (!gpc_element_p) { + zval tmp; +@@ -263,6 +270,12 @@ plain_var: + zval_ptr_dtor_nogc(val); + } + } else { ++ if (php_is_forbidden_variable_name(index, index_len, var_name)) { ++ zval_ptr_dtor_nogc(val); ++ free_alloca(var_orig, use_heap); ++ return; ++ } ++ + zend_ulong idx; + + /* +-- +2.44.0 + +From dcdd49ef3bfbd8ccc778850d6a0f9b98adf625d4 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Wed, 10 Apr 2024 08:59:32 +0200 +Subject: [PATCH 2/4] NEWS + +(cherry picked from commit 366cc249b7d54707572beb7096e8f6c65ee79719) +--- + NEWS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/NEWS b/NEWS +index 4f88029a7d6..d63aadc6851 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,12 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.1.28 ++ ++- Standard: ++ . Fixed bug GHSA-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to ++ partial CVE-2022-31629 fix). (CVE-2024-2756) (nielsdos) ++ + Backported from 8.0.30 + + - Libxml: +-- +2.44.0 + diff --git a/php-cve-2024-3096.patch b/php-cve-2024-3096.patch new file mode 100644 index 0000000..5a1f20a --- /dev/null +++ b/php-cve-2024-3096.patch @@ -0,0 +1,81 @@ +From 4a7ceb9d6427f8d368f1a8739267b1f8310ec201 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Fri, 29 Mar 2024 15:27:59 +0000 +Subject: [PATCH 3/4] Fix bug GHSA-q6x7-frmf-grcw: password_verify can + erroneously return true + +Disallow null character in bcrypt password + +(cherry picked from commit 0ba5229a3f7572846e91c8f5382e87785f543826) +(cherry picked from commit 81794c73068d9a44bf109bbcc9793e7b56a1c051) +--- + ext/standard/password.c | 5 +++++ + ext/standard/tests/password/password_bcrypt_errors.phpt | 6 ++++++ + 2 files changed, 11 insertions(+) + +diff --git a/ext/standard/password.c b/ext/standard/password.c +index 9fe7fb1a422..af80670246a 100644 +--- a/ext/standard/password.c ++++ b/ext/standard/password.c +@@ -260,6 +260,11 @@ static zend_string* php_password_bcrypt_hash(const zend_string *password, zend_a + zval *zcost; + zend_long cost = PHP_PASSWORD_BCRYPT_COST; + ++ if (memchr(ZSTR_VAL(password), '\0', ZSTR_LEN(password))) { ++ php_error_docref(NULL, E_WARNING, "Bcrypt password must not contain null character"); ++ return NULL; ++ } ++ + if (options && (zcost = zend_hash_str_find(options, "cost", sizeof("cost")-1)) != NULL) { + cost = zval_get_long(zcost); + } +diff --git a/ext/standard/tests/password/password_bcrypt_errors.phpt b/ext/standard/tests/password/password_bcrypt_errors.phpt +index a0826080e62..f95b72670ae 100644 +--- a/ext/standard/tests/password/password_bcrypt_errors.phpt ++++ b/ext/standard/tests/password/password_bcrypt_errors.phpt +@@ -16,6 +16,8 @@ var_dump(password_hash("foo", PASSWORD_BCRYPT, array("salt" => 123))); + + var_dump(password_hash("foo", PASSWORD_BCRYPT, array("cost" => "foo"))); + ++var_dump(password_hash("null\0password", PASSWORD_BCRYPT)); ++ + ?> + --EXPECTF-- + Warning: password_hash(): Invalid bcrypt cost parameter specified: 3 in %s on line %d +@@ -41,3 +43,7 @@ NULL + + Warning: password_hash(): Invalid bcrypt cost parameter specified: 0 in %s on line %d + NULL ++ ++Warning: password_hash(): Bcrypt password must not contain null character in %s on line %d ++NULL ++ +-- +2.44.0 + +From 027bdbc636632be49ecfad8d4191509faacb34ac Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Wed, 10 Apr 2024 09:01:09 +0200 +Subject: [PATCH 4/4] NEWS + +(cherry picked from commit 24f77904ee2259d722559f129f96a1f145a2367b) +--- + NEWS | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/NEWS b/NEWS +index d63aadc6851..96a33c21637 100644 +--- a/NEWS ++++ b/NEWS +@@ -6,6 +6,8 @@ Backported from 8.1.28 + - Standard: + . Fixed bug GHSA-wpj3-hf5j-x4v4 (__Host-/__Secure- cookie bypass due to + partial CVE-2022-31629 fix). (CVE-2024-2756) (nielsdos) ++ . Fixed bug GHSA-h746-cjrr-wfmr (password_verify can erroneously return true, ++ opening ATO risk). (CVE-2024-3096) (Jakub Zelenka) + + Backported from 8.0.30 + +-- +2.44.0 + diff --git a/php-cve-2024-5458.patch b/php-cve-2024-5458.patch new file mode 100644 index 0000000..64a1a78 --- /dev/null +++ b/php-cve-2024-5458.patch @@ -0,0 +1,180 @@ +From 08be64e40197fc12dca5f802d16748d9c3cb4cb4 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Wed, 22 May 2024 22:25:02 +0200 +Subject: [PATCH 1/2] Fix GHSA-w8qr-v226-r27w + +We should not early-out with success status if we found an ipv6 +hostname, we should keep checking the rest of the conditions. +Because integrating the if-check of the ipv6 hostname in the +"Validate domain" if-check made the code hard to read, I extracted the +condition out to a separate function. This also required to make +a few pointers const in order to have some clean code. + +(cherry picked from commit 4066610b47e22c24cbee91be434a94357056a479) +--- + ext/filter/logical_filters.c | 35 ++++++++++--------- + ext/filter/tests/ghsa-w8qr-v226-r27w.phpt | 41 +++++++++++++++++++++++ + 2 files changed, 61 insertions(+), 15 deletions(-) + create mode 100644 ext/filter/tests/ghsa-w8qr-v226-r27w.phpt + +diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c +index e5e87c01568..9c86ad072cc 100644 +--- a/ext/filter/logical_filters.c ++++ b/ext/filter/logical_filters.c +@@ -91,7 +91,7 @@ + #define FORMAT_IPV4 4 + #define FORMAT_IPV6 6 + +-static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]); ++static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]); + + static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret) { /* {{{ */ + zend_long ctx_value; +@@ -571,6 +571,14 @@ static int is_userinfo_valid(zend_string *str) + return 1; + } + ++static zend_bool php_filter_is_valid_ipv6_hostname(const char *s, size_t l) ++{ ++ const char *e = s + l; ++ const char *t = e - 1; ++ ++ return *s == '[' && *t == ']' && _php_filter_validate_ipv6(s + 1, l - 2, NULL); ++} ++ + void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ + { + php_url *url; +@@ -596,7 +604,7 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ + + if (url->scheme != NULL && + (zend_string_equals_literal_ci(url->scheme, "http") || zend_string_equals_literal_ci(url->scheme, "https"))) { +- char *e, *s, *t; ++ const char *s; + size_t l; + + if (url->host == NULL) { +@@ -605,17 +613,14 @@ void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ + + s = ZSTR_VAL(url->host); + l = ZSTR_LEN(url->host); +- e = s + l; +- t = e - 1; +- +- /* An IPv6 enclosed by square brackets is a valid hostname */ +- if (*s == '[' && *t == ']' && _php_filter_validate_ipv6((s + 1), l - 2, NULL)) { +- php_url_free(url); +- return; +- } + +- // Validate domain +- if (!_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME)) { ++ if ( ++ /* An IPv6 enclosed by square brackets is a valid hostname.*/ ++ !php_filter_is_valid_ipv6_hostname(s, l) && ++ /* Validate domain. ++ * This includes a loose check for an IPv4 address. */ ++ !_php_filter_validate_domain(ZSTR_VAL(url->host), l, FILTER_FLAG_HOSTNAME) ++ ) { + php_url_free(url); + RETURN_VALIDATION_FAILED + } +@@ -749,15 +754,15 @@ static int _php_filter_validate_ipv4(char *str, size_t str_len, int *ip) /* {{{ + } + /* }}} */ + +-static int _php_filter_validate_ipv6(char *str, size_t str_len, int ip[8]) /* {{{ */ ++static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) /* {{{ */ + { + int compressed_pos = -1; + int blocks = 0; + int num, n, i; + char *ipv4; +- char *end; ++ const char *end; + int ip4elm[4]; +- char *s = str; ++ const char *s = str; + + if (!memchr(str, ':', str_len)) { + return 0; +diff --git a/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt b/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt +new file mode 100644 +index 00000000000..0092408ee5a +--- /dev/null ++++ b/ext/filter/tests/ghsa-w8qr-v226-r27w.phpt +@@ -0,0 +1,41 @@ ++--TEST-- ++GHSA-w8qr-v226-r27w ++--EXTENSIONS-- ++filter ++--FILE-- ++<?php ++ ++function test(string $input) { ++ var_dump(filter_var($input, FILTER_VALIDATE_URL)); ++} ++ ++echo "--- These ones should fail ---\n"; ++test("http://t[est@127.0.0.1"); ++test("http://t[est@[::1]"); ++test("http://t[est@[::1"); ++test("http://t[est@::1]"); ++test("http://php.net\\@aliyun.com/aaa.do"); ++test("http://test[@2001:db8:3333:4444:5555:6666:1.2.3.4]"); ++test("http://te[st@2001:db8:3333:4444:5555:6666:1.2.3.4]"); ++test("http://te[st@2001:db8:3333:4444:5555:6666:1.2.3.4"); ++ ++echo "--- These ones should work ---\n"; ++test("http://test@127.0.0.1"); ++test("http://test@[2001:db8:3333:4444:5555:6666:1.2.3.4]"); ++test("http://test@[::1]"); ++ ++?> ++--EXPECT-- ++--- These ones should fail --- ++bool(false) ++bool(false) ++bool(false) ++bool(false) ++bool(false) ++bool(false) ++bool(false) ++bool(false) ++--- These ones should work --- ++string(21) "http://test@127.0.0.1" ++string(50) "http://test@[2001:db8:3333:4444:5555:6666:1.2.3.4]" ++string(17) "http://test@[::1]" +-- +2.45.1 + +From ec1d5e6468479e64acc7fb8cb955f053b64ea9a0 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 4 Jun 2024 16:48:08 +0200 +Subject: [PATCH 2/2] NEWS + +(cherry picked from commit a1ff81b786bd519597e770795be114f5171f0648) +--- + NEWS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/NEWS b/NEWS +index 8058eff0256..34ad33cf5c4 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,12 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.1.29 ++ ++- Filter: ++ . Fixed bug GHSA-w8qr-v226-r27w (Filter bypass in filter_var FILTER_VALIDATE_URL). ++ (CVE-2024-5458) (nielsdos) ++ + Backported from 8.1.28 + + - Standard: +-- +2.45.1 + diff --git a/php-cve-2024-8925.patch b/php-cve-2024-8925.patch new file mode 100644 index 0000000..f219a24 --- /dev/null +++ b/php-cve-2024-8925.patch @@ -0,0 +1,227 @@ +From a24ac172f52e75101913f3946cfa5515f723c99f Mon Sep 17 00:00:00 2001 +From: Arnaud Le Blanc <arnaud.lb@gmail.com> +Date: Mon, 9 Sep 2024 15:22:07 +0200 +Subject: [PATCH 04/11] Fix GHSA-9pqp-7h25-4f32 + +multipart/form-data boundaries larger than the read buffer result in erroneous +parsing, which violates data integrity. + +Limit boundary size, as allowed by RFC 1521: + + Encapsulation boundaries [...] must be no longer than 70 characters, not + counting the two leading hyphens. + +We correctly parse payloads with boundaries of length up to +FILLUNIT-strlen("\r\n--") bytes, so allow this for BC. + +(cherry picked from commit 19b49258d0c5a61398d395d8afde1123e8d161e0) +(cherry picked from commit 2b0daf421c162376892832588eccdfa9a286ed09) +--- + main/rfc1867.c | 7 ++ + tests/basic/GHSA-9pqp-7h25-4f32.inc | 3 + + tests/basic/GHSA-9pqp-7h25-4f32.phpt | 100 +++++++++++++++++++++++++++ + 3 files changed, 110 insertions(+) + create mode 100644 tests/basic/GHSA-9pqp-7h25-4f32.inc + create mode 100644 tests/basic/GHSA-9pqp-7h25-4f32.phpt + +diff --git a/main/rfc1867.c b/main/rfc1867.c +index 1b212c93325..43ccce120c3 100644 +--- a/main/rfc1867.c ++++ b/main/rfc1867.c +@@ -759,6 +759,13 @@ SAPI_API SAPI_POST_HANDLER_FUNC(rfc1867_post_handler) /* {{{ */ + boundary_len = boundary_end-boundary; + } + ++ /* Boundaries larger than FILLUNIT-strlen("\r\n--") characters lead to ++ * erroneous parsing */ ++ if (boundary_len > FILLUNIT-strlen("\r\n--")) { ++ sapi_module.sapi_error(E_WARNING, "Boundary too large in multipart/form-data POST data"); ++ return; ++ } ++ + /* Initialize the buffer */ + if (!(mbuff = multipart_buffer_new(boundary, boundary_len))) { + sapi_module.sapi_error(E_WARNING, "Unable to initialize the input buffer"); +diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.inc b/tests/basic/GHSA-9pqp-7h25-4f32.inc +new file mode 100644 +index 00000000000..adf72a361a2 +--- /dev/null ++++ b/tests/basic/GHSA-9pqp-7h25-4f32.inc +@@ -0,0 +1,3 @@ ++<?php ++print "Hello world\n"; ++var_dump($_POST); +diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt +new file mode 100644 +index 00000000000..af819163705 +--- /dev/null ++++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt +@@ -0,0 +1,100 @@ ++--TEST-- ++GHSA-9pqp-7h25-4f32 ++--SKIPIF-- ++<?php ++if (!getenv('TEST_PHP_CGI_EXECUTABLE')) { ++ die("skip php-cgi not available"); ++} ++?> ++--FILE-- ++<?php ++ ++const FILLUNIT = 5 * 1024; ++ ++function test($boundaryLen) { ++ printf("Boundary len: %d\n", $boundaryLen); ++ ++ $cmd = [ ++ getenv('TEST_PHP_CGI_EXECUTABLE'), ++ '-C', ++ '-n', ++ __DIR__ . '/GHSA-9pqp-7h25-4f32.inc', ++ ]; ++ ++ $boundary = str_repeat('A', $boundaryLen); ++ $body = "" ++ . "--$boundary\r\n" ++ . "Content-Disposition: form-data; name=\"koko\"\r\n" ++ . "\r\n" ++ . "BBB\r\n--" . substr($boundary, 0, -1) . "CCC\r\n" ++ . "--$boundary--\r\n" ++ ; ++ ++ $env = array_merge($_ENV, [ ++ 'REDIRECT_STATUS' => '1', ++ 'CONTENT_TYPE' => "multipart/form-data; boundary=$boundary", ++ 'CONTENT_LENGTH' => strlen($body), ++ 'REQUEST_METHOD' => 'POST', ++ 'SCRIPT_FILENAME' => __DIR__ . '/GHSA-9pqp-7h25-4f32.inc', ++ ]); ++ ++ $spec = [ ++ 0 => ['pipe', 'r'], ++ 1 => STDOUT, ++ 2 => STDOUT, ++ ]; ++ ++ $pipes = []; ++ ++ print "Starting...\n"; ++ ++ $handle = proc_open($cmd, $spec, $pipes, getcwd(), $env); ++ ++ fwrite($pipes[0], $body); ++ ++ $status = proc_close($handle); ++ ++ print "\n"; ++} ++ ++for ($offset = -1; $offset <= 1; $offset++) { ++ test(FILLUNIT - strlen("\r\n--") + $offset); ++} ++ ++?> ++--EXPECTF-- ++Boundary len: 5115 ++Starting... ++X-Powered-By: %s ++Content-type: text/html; charset=UTF-8 ++ ++Hello world ++array(1) { ++ ["koko"]=> ++ string(5124) "BBB ++--AAA%sCCC" ++} ++ ++Boundary len: 5116 ++Starting... ++X-Powered-By: %s ++Content-type: text/html; charset=UTF-8 ++ ++Hello world ++array(1) { ++ ["koko"]=> ++ string(5125) "BBB ++--AAA%sCCC" ++} ++ ++Boundary len: 5117 ++Starting... ++X-Powered-By: %s ++Content-type: text/html; charset=UTF-8 ++ ++<br /> ++<b>Warning</b>: Boundary too large in multipart/form-data POST data in <b>Unknown</b> on line <b>0</b><br /> ++Hello world ++array(0) { ++} ++ +-- +2.46.1 + +From 2fd1b83817d20523e72bef3ad524cd5797f51acf Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Mon, 23 Sep 2024 18:54:31 +0100 +Subject: [PATCH 08/11] Skip GHSA-9pqp-7h25-4f32 test on Windows + +(cherry picked from commit c70e25630832fa10d421328eed2b8e1a36af7a64) +(cherry picked from commit c75683864f6e4188439e8ca2adbb05824918be12) +--- + tests/basic/GHSA-9pqp-7h25-4f32.phpt | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt +index af819163705..29bcb6557d5 100644 +--- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt ++++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt +@@ -5,6 +5,9 @@ GHSA-9pqp-7h25-4f32 + if (!getenv('TEST_PHP_CGI_EXECUTABLE')) { + die("skip php-cgi not available"); + } ++if (substr(PHP_OS, 0, 3) == 'WIN') { ++ die("skip not for Windows in CI - probably resource issue"); ++} + ?> + --FILE-- + <?php +-- +2.46.1 + +From 29065f33f37f99ba33254cb23c941647bcd7372c Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 26 Sep 2024 15:49:03 +0200 +Subject: [PATCH 11/11] adapt GHSA-9pqp-7h25-4f32 test for 7.x + +--- + tests/basic/GHSA-9pqp-7h25-4f32.phpt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt +index 29bcb6557d5..a1ead918ff3 100644 +--- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt ++++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt +@@ -21,6 +21,7 @@ function test($boundaryLen) { + getenv('TEST_PHP_CGI_EXECUTABLE'), + '-C', + '-n', ++ '-dlog_errors=1', + __DIR__ . '/GHSA-9pqp-7h25-4f32.inc', + ]; + +@@ -92,11 +93,10 @@ array(1) { + + Boundary len: 5117 + Starting... ++PHP Warning: Boundary too large in multipart/form-data POST data in Unknown on line 0 + X-Powered-By: %s + Content-type: text/html; charset=UTF-8 + +-<br /> +-<b>Warning</b>: Boundary too large in multipart/form-data POST data in <b>Unknown</b> on line <b>0</b><br /> + Hello world + array(0) { + } +-- +2.46.1 + diff --git a/php-cve-2024-8926.patch b/php-cve-2024-8926.patch new file mode 100644 index 0000000..b5baaa8 --- /dev/null +++ b/php-cve-2024-8926.patch @@ -0,0 +1,210 @@ +From fb718aa6f2117933566bb7bb2f70b2b0d9a9c08f Mon Sep 17 00:00:00 2001 +From: Jan Ehrhardt <github@ehrhardt.nl> +Date: Wed, 5 Jun 2024 20:24:52 +0200 +Subject: [PATCH 01/11] Fix GHSA-3qgc-jrrr-25jv + +--- + sapi/cgi/cgi_main.c | 23 ++++++++++++++- + sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt | 38 +++++++++++++++++++++++++ + 2 files changed, 60 insertions(+), 1 deletion(-) + create mode 100644 sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt + +diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c +index a36f426d266..8d1342727dc 100644 +--- a/sapi/cgi/cgi_main.c ++++ b/sapi/cgi/cgi_main.c +@@ -1827,8 +1827,13 @@ int main(int argc, char *argv[]) + } + } + ++ /* Apache CGI will pass the query string to the command line if it doesn't contain a '='. ++ * This can create an issue where a malicious request can pass command line arguments to ++ * the executable. Ideally we skip argument parsing when we're in cgi or fastcgi mode, ++ * but that breaks PHP scripts on Linux with a hashbang: `#!/php-cgi -d option=value`. ++ * Therefore, this code only prevents passing arguments if the query string starts with a '-'. ++ * Similarly, scripts spawned in subprocesses on Windows may have the same issue. */ + if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) { +- /* we've got query string that has no = - apache CGI will pass it to command line */ + unsigned char *p; + decoded_query_string = strdup(query_string); + php_url_decode(decoded_query_string, strlen(decoded_query_string)); +@@ -1838,6 +1843,22 @@ int main(int argc, char *argv[]) + if(*p == '-') { + skip_getopt = 1; + } ++ ++ /* On Windows we have to take into account the "best fit" mapping behaviour. */ ++#ifdef PHP_WIN32 ++ if (*p >= 0x80) { ++ wchar_t wide_buf[1]; ++ wide_buf[0] = *p; ++ char char_buf[4]; ++ size_t wide_buf_len = sizeof(wide_buf) / sizeof(wide_buf[0]); ++ size_t char_buf_len = sizeof(char_buf) / sizeof(char_buf[0]); ++ if (WideCharToMultiByte(CP_ACP, 0, wide_buf, wide_buf_len, char_buf, char_buf_len, NULL, NULL) == 0 ++ || char_buf[0] == '-') { ++ skip_getopt = 1; ++ } ++ } ++#endif ++ + free(decoded_query_string); + } + +diff --git a/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt b/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt +new file mode 100644 +index 00000000000..fd2fcdfbf89 +--- /dev/null ++++ b/sapi/cgi/tests/ghsa-3qgc-jrrr-25jv.phpt +@@ -0,0 +1,38 @@ ++--TEST-- ++GHSA-3qgc-jrrr-25jv ++--SKIPIF-- ++<?php ++include 'skipif.inc'; ++if (PHP_OS_FAMILY !== "Windows") die("skip Only for Windows"); ++ ++$codepage = trim(shell_exec("powershell Get-ItemPropertyValue HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Nls\\CodePage ACP")); ++if ($codepage !== '932' && $codepage !== '936' && $codepage !== '950') die("skip Wrong codepage"); ++?> ++--FILE-- ++<?php ++include 'include.inc'; ++ ++$filename = __DIR__."/GHSA-3qgc-jrrr-25jv_tmp.php"; ++$script = '<?php echo "hello "; echo "world"; ?>'; ++file_put_contents($filename, $script); ++ ++$php = get_cgi_path(); ++reset_env_vars(); ++ ++putenv("SERVER_NAME=Test"); ++putenv("SCRIPT_FILENAME=$filename"); ++putenv("QUERY_STRING=%ads"); ++putenv("REDIRECT_STATUS=1"); ++ ++passthru("$php -s"); ++ ++?> ++--CLEAN-- ++<?php ++@unlink(__DIR__."/GHSA-3qgc-jrrr-25jv_tmp.php"); ++?> ++--EXPECTF-- ++X-Powered-By: PHP/%s ++Content-type: %s ++ ++hello world +-- +2.46.1 + +From a634d3f5169c884715d9e26ac213ecf2a25c3666 Mon Sep 17 00:00:00 2001 +From: Jan Ehrhardt <github@ehrhardt.nl> +Date: Sun, 9 Jun 2024 20:09:02 +0200 +Subject: [PATCH 03/11] NEWS: Add backports from 8.1.29 + +--- + NEWS | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/NEWS b/NEWS +index 34ad33cf5c4..a96518695fb 100644 +--- a/NEWS ++++ b/NEWS +@@ -3,10 +3,18 @@ PHP NEWS + + Backported from 8.1.29 + ++- CGI: ++ . Fixed bug GHSA-3qgc-jrrr-25jv (Bypass of CVE-2012-1823, Argument Injection ++ in PHP-CGI). (CVE-2024-4577) (nielsdos) ++ + - Filter: + . Fixed bug GHSA-w8qr-v226-r27w (Filter bypass in filter_var FILTER_VALIDATE_URL). + (CVE-2024-5458) (nielsdos) + ++- Standard: ++ . Fixed bug GHSA-9fcc-425m-g385 (Bypass of CVE-2024-1874). ++ (CVE-2024-5585) (nielsdos) ++ + Backported from 8.1.28 + + - Standard: +-- +2.46.1 + +From 1158d06f0b20532ab7309cb20f0be843f9662e3c Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Fri, 14 Jun 2024 19:49:22 +0200 +Subject: [PATCH 05/11] Fix GHSA-p99j-rfp4-xqvq + +It's no use trying to work around whatever the operating system and Apache +do because we'll be fighting that until eternity. +Change the skip_getopt condition such that when we're running in +CGI or FastCGI mode we always skip the argument parsing. +This is a BC break, but this seems to be the only way to get rid of this +class of issues. + +(cherry picked from commit abcfd980bfa03298792fd3aba051c78d52f10642) +(cherry picked from commit 2d2552e092b6ff32cd823692d512f126ee629842) +--- + sapi/cgi/cgi_main.c | 26 ++++++++------------------ + 1 file changed, 8 insertions(+), 18 deletions(-) + +diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c +index 8d1342727dc..a2761aafd7b 100644 +--- a/sapi/cgi/cgi_main.c ++++ b/sapi/cgi/cgi_main.c +@@ -1777,7 +1777,6 @@ int main(int argc, char *argv[]) + int status = 0; + #endif + char *query_string; +- char *decoded_query_string; + int skip_getopt = 0; + + #if defined(SIGPIPE) && defined(SIG_IGN) +@@ -1832,10 +1831,15 @@ int main(int argc, char *argv[]) + * the executable. Ideally we skip argument parsing when we're in cgi or fastcgi mode, + * but that breaks PHP scripts on Linux with a hashbang: `#!/php-cgi -d option=value`. + * Therefore, this code only prevents passing arguments if the query string starts with a '-'. +- * Similarly, scripts spawned in subprocesses on Windows may have the same issue. */ ++ * Similarly, scripts spawned in subprocesses on Windows may have the same issue. ++ * However, Windows has lots of conversion rules and command line parsing rules that ++ * are too difficult and dangerous to reliably emulate. */ + if((query_string = getenv("QUERY_STRING")) != NULL && strchr(query_string, '=') == NULL) { ++#ifdef PHP_WIN32 ++ skip_getopt = cgi || fastcgi; ++#else + unsigned char *p; +- decoded_query_string = strdup(query_string); ++ char *decoded_query_string = strdup(query_string); + php_url_decode(decoded_query_string, strlen(decoded_query_string)); + for (p = (unsigned char *)decoded_query_string; *p && *p <= ' '; p++) { + /* skip all leading spaces */ +@@ -1844,22 +1848,8 @@ int main(int argc, char *argv[]) + skip_getopt = 1; + } + +- /* On Windows we have to take into account the "best fit" mapping behaviour. */ +-#ifdef PHP_WIN32 +- if (*p >= 0x80) { +- wchar_t wide_buf[1]; +- wide_buf[0] = *p; +- char char_buf[4]; +- size_t wide_buf_len = sizeof(wide_buf) / sizeof(wide_buf[0]); +- size_t char_buf_len = sizeof(char_buf) / sizeof(char_buf[0]); +- if (WideCharToMultiByte(CP_ACP, 0, wide_buf, wide_buf_len, char_buf, char_buf_len, NULL, NULL) == 0 +- || char_buf[0] == '-') { +- skip_getopt = 1; +- } +- } +-#endif +- + free(decoded_query_string); ++#endif + } + + while (!skip_getopt && (c = php_getopt(argc, argv, OPTIONS, &php_optarg, &php_optind, 0, 2)) != -1) { +-- +2.46.1 + diff --git a/php-cve-2024-8927.patch b/php-cve-2024-8927.patch new file mode 100644 index 0000000..ed1e4cf --- /dev/null +++ b/php-cve-2024-8927.patch @@ -0,0 +1,57 @@ +From c7308ba7cd0533501b40eba255602bb5e085550f Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Tue, 18 Jun 2024 21:28:26 +0200 +Subject: [PATCH 06/11] Fix GHSA-94p6-54jq-9mwp + +Apache only generates REDIRECT_STATUS, so explicitly check for that +if the server name is Apache, don't allow other variable names. +Furthermore, redirect.so and Netscape no longer exist, so +remove those entries as we can't check their server name anymore. + +We now also check for the configuration override *first* such that it +always take precedence. This would allow for a mitigation path if +something like this happens in the future. + +(cherry picked from commit 48808d98f4fc2a05193cdcc1aedd6c66816450f1) +(cherry picked from commit 8aa748ee0657cdee8d883ba50d04b68bc450f686) +--- + sapi/cgi/cgi_main.c | 23 +++++++++++------------ + 1 file changed, 11 insertions(+), 12 deletions(-) + +diff --git a/sapi/cgi/cgi_main.c b/sapi/cgi/cgi_main.c +index a2761aafd7b..ebce6302b93 100644 +--- a/sapi/cgi/cgi_main.c ++++ b/sapi/cgi/cgi_main.c +@@ -1939,18 +1939,17 @@ int main(int argc, char *argv[]) + + /* check force_cgi after startup, so we have proper output */ + if (cgi && CGIG(force_redirect)) { +- /* Apache will generate REDIRECT_STATUS, +- * Netscape and redirect.so will generate HTTP_REDIRECT_STATUS. +- * redirect.so and installation instructions available from +- * http://www.koehntopp.de/php. +- * -- kk@netuse.de +- */ +- if (!getenv("REDIRECT_STATUS") && +- !getenv ("HTTP_REDIRECT_STATUS") && +- /* this is to allow a different env var to be configured +- * in case some server does something different than above */ +- (!CGIG(redirect_status_env) || !getenv(CGIG(redirect_status_env))) +- ) { ++ /* This is to allow a different environment variable to be configured ++ * in case the we cannot auto-detect which environment variable to use. ++ * Checking this first to allow user overrides in case the environment ++ * variable can be set by an untrusted party. */ ++ const char *redirect_status_env = CGIG(redirect_status_env); ++ if (!redirect_status_env) { ++ /* Apache will generate REDIRECT_STATUS. */ ++ redirect_status_env = "REDIRECT_STATUS"; ++ } ++ ++ if (!getenv(redirect_status_env)) { + zend_try { + SG(sapi_headers).http_response_code = 400; + PUTS("<b>Security Alert!</b> The PHP CGI cannot be accessed directly.\n\n\ +-- +2.46.1 + diff --git a/php-cve-2024-8929.patch b/php-cve-2024-8929.patch new file mode 100644 index 0000000..fdc844d --- /dev/null +++ b/php-cve-2024-8929.patch @@ -0,0 +1,2714 @@ +From e8bc357123ea19c4e2390374f088c9d4941f19e6 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Tue, 8 Oct 2024 16:17:53 +0100 +Subject: [PATCH 1/6] Fix GHSA-h35g-vwh6-m678: Mysqlnd - various heap buffer + over-reads + +This fixes issues causing buffer over-read that leak heap content: +- RESP packet field default left over for COM_LIST +- RESP packet upsert filename +- OK packet message +- RESP packet for stmt row data + - ps_fetch_from_1_to_8_bytes + - ps_fetch_float + - ps_fetch_double + - ps_fetch_time + - ps_fetch_date + - ps_fetch_datetime + - ps_fetch_string + - ps_fetch_bit +- RESP packet for query row data (just possible overflow on 32bit) + +It also adds various protocol tests using a new fake server. + +(cherry picked from commit 2f5aa9f9d150ca56e356f3ca9acf9d530108cb08) +(cherry picked from commit 0d3ccf4cc54d3844bc9d1c8f6bdcd36180752a2c) + +adapt for 7.x +--- + ext/mysqli/tests/fake_server.inc | 856 ++++++++++++++++++ + .../ghsa-h35g-vwh6-m678-auth-message.phpt | 38 + + ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt | 47 + + .../tests/ghsa-h35g-vwh6-m678-filename.phpt | 43 + + ...hsa-h35g-vwh6-m678-query-len-overflow.phpt | 48 + + .../ghsa-h35g-vwh6-m678-stmt-row-bit.phpt | 53 ++ + .../ghsa-h35g-vwh6-m678-stmt-row-date.phpt | 53 ++ + ...ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt | 53 ++ + .../ghsa-h35g-vwh6-m678-stmt-row-double.phpt | 53 ++ + .../ghsa-h35g-vwh6-m678-stmt-row-float.phpt | 53 ++ + .../ghsa-h35g-vwh6-m678-stmt-row-int.phpt | 53 ++ + ...ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt | 53 ++ + .../ghsa-h35g-vwh6-m678-stmt-row-string.phpt | 53 ++ + .../ghsa-h35g-vwh6-m678-stmt-row-time.phpt | 53 ++ + .../tests/protocol_query_row_fetch_data.phpt | 74 ++ + .../tests/protocol_stmt_row_fetch_data.phpt | 91 ++ + ext/mysqlnd/mysqlnd_ps_codec.c | 69 ++ + ext/mysqlnd/mysqlnd_result.c | 2 +- + ext/mysqlnd/mysqlnd_wireprotocol.c | 71 +- + 19 files changed, 1794 insertions(+), 22 deletions(-) + create mode 100644 ext/mysqli/tests/fake_server.inc + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt + create mode 100644 ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt + create mode 100644 ext/mysqli/tests/protocol_query_row_fetch_data.phpt + create mode 100644 ext/mysqli/tests/protocol_stmt_row_fetch_data.phpt + +diff --git a/ext/mysqli/tests/fake_server.inc b/ext/mysqli/tests/fake_server.inc +new file mode 100644 +index 00000000000..b02fabc584c +--- /dev/null ++++ b/ext/mysqli/tests/fake_server.inc +@@ -0,0 +1,856 @@ ++<?php ++ ++function my_mysqli_data_fields(): array ++{ ++ return [ ++ 'intval' => [ ++ 'type' => '03', ++ 'charset' => '3f00', ++ 'length' => '0b000000', ++ 'flags' => '0110', ++ 'decimal' => '00', ++ 'query_data_packet_length' => '080000', ++ 'query_data_value' => '023134', ++ 'stmt_data_packet_length' => '0b0000', ++ 'stmt_data_value' => '0e000000' ++ ], ++ 'fltval' => [ ++ 'type' => '04', ++ 'charset' => '3f00', ++ 'length' => '0c000000', ++ 'flags' => '0110', ++ 'decimal' => '1f', ++ 'query_data_packet_length' => '090000', ++ 'query_data_value' => '03322e33', ++ 'stmt_data_packet_length' => '0b0000', ++ 'stmt_data_value' => '33331340', ++ ], ++ 'dblval' => [ ++ 'type' => '05', ++ 'charset' => '3f00', ++ 'length' => '16000000', ++ 'flags' => '0110', ++ 'decimal' => '1f', ++ 'query_data_packet_length' => '090000', ++ 'query_data_value' => '03312e32', ++ 'stmt_data_packet_length' => '0f0000', ++ 'stmt_data_value' => '333333333333f33f' ++ ], ++ 'datval' => [ ++ 'type' => '0a', ++ 'charset' => '3f00', ++ 'length' => '0a000000', ++ 'flags' => '8110', ++ 'decimal' => '00', ++ 'query_data_packet_length' => '100000', ++ 'query_data_value' => '0a323031342d31322d3135', ++ 'stmt_data_packet_length' => '0c0000', ++ 'stmt_data_value' => '04de070c0f' ++ ], ++ 'timval' => [ ++ 'type' => '0b', ++ 'charset' => '3f00', ++ 'length' => '0a000000', ++ 'flags' => '8110', ++ 'decimal' => '00', ++ 'query_data_packet_length' => '0e0000', ++ 'query_data_value' => '0831333a30303a3032', ++ 'stmt_data_packet_length' => '100000', ++ 'stmt_data_value' => '080000000000150801' ++ ], ++ 'dtival' => [ ++ 'type' => '0c', ++ 'charset' => '3f00', ++ 'length' => '13000000', ++ 'flags' => '8110', ++ 'decimal' => '00', ++ 'query_data_packet_length' => '190000', ++ 'query_data_value' => '13323031342d31322d31362031333a30303a3031', ++ 'stmt_data_packet_length' => '0f0000', ++ 'stmt_data_value' => '07de070c100d0001' ++ ], ++ 'bitval' => [ ++ 'type' => '10', ++ 'charset' => '3f00', ++ 'length' => '40000000', ++ 'flags' => '2110', ++ 'decimal' => '00', ++ 'query_data_packet_length' => '0e0000', ++ 'query_data_value' => '080808080808080808', ++ 'stmt_data_packet_length' => '100000', ++ 'stmt_data_value' => '080808080808080808' ++ ], ++ 'strval' => [ ++ 'type' => 'fd', ++ 'charset' => 'e000', ++ 'length' => 'c8000000', ++ 'flags' => '0110', ++ 'decimal' => '00', ++ 'query_data_packet_length' => '0a0000', ++ 'query_data_value' => '0474657374', ++ 'stmt_data_packet_length' => '0c0000', ++ 'stmt_data_value' => '0474657374' ++ ], ++ ]; ++} ++ ++function my_mysqli_data_field(string $field): array ++{ ++ $fields = my_mysqli_data_fields(); ++ if (!isset($fields[$field])) { ++ throw new Exception("Unknown field $field"); ++ } ++ return $fields[$field]; ++} ++ ++ ++ ++class my_mysqli_fake_packet_item ++{ ++ public function __construct(public string|null $name, public string $value, public bool $is_hex = true) ++ { ++ } ++} ++ ++class my_mysqli_fake_packet ++{ ++ private array $data = array(); ++ ++ public function __get(string $name) ++ { ++ foreach ($this->data as $item) { ++ if ($item->name === $name) { ++ return $item->value; ++ } ++ } ++ return null; ++ } ++ ++ public function __set(string $name, string|my_mysqli_fake_packet_item $value) ++ { ++ if ($value instanceof my_mysqli_fake_packet_item) { ++ if ($value->name === null) { ++ $value->name = $name; ++ } ++ } else { ++ $value = new my_mysqli_fake_packet_item($name, $value, true); ++ } ++ ++ for ($i = 0; $i < count($this->data); $i++) { ++ if ($this->data[$i]->name === $name) { ++ $this->data[$i] = $value; ++ return; ++ } ++ } ++ ++ $this->data[] = $value; ++ } ++ ++ public function to_bytes(): string ++ { ++ $bytes = ''; ++ foreach ($this->data as $item) { ++ $bytes .= $item->is_hex ? hex2bin($item->value) : $item->value; ++ } ++ return $bytes; ++ } ++} ++ ++class my_mysqli_fake_packet_generator ++{ ++ public static function create_packet_item(int|string $value, bool $is_hex = false, string $format = 'v'): my_mysqli_fake_packet_item ++ { ++ if (is_string($value)) { ++ $packed_value = $value; ++ } else { ++ $packed_value = pack($format, $value); ++ } ++ return new my_mysqli_fake_packet_item(null, $packed_value, $is_hex); ++ } ++ ++ public function server_ok(): my_mysqli_fake_packet ++ { ++ $packet = new my_mysqli_fake_packet(); ++ $packet->packet_length = "070000"; ++ $packet->packet_number = "02"; ++ $packet->header = "00"; // OK ++ $packet->affected_rows = "00"; ++ $packet->last_insert_id = "00"; ++ $packet->server_status = "0200"; ++ $packet->warning_count = "0000"; ++ return $packet; ++ } ++ ++ public function server_greetings(): my_mysqli_fake_packet ++ { ++ $packet = new my_mysqli_fake_packet(); ++ $packet->packet_length = "580000"; ++ $packet->packet_number = "00"; ++ $packet->proto_version = "0a"; ++ $packet->version = self::create_packet_item('5.5.5-10.5.18-MariaDB' . chr(0)); ++ $packet->thread_id = "03000000"; ++ $packet->salt = "473e3f6047257c67"; ++ $packet->filler = "00"; ++ $packet->server_capabilities = self::create_packet_item(0b1111011111111110); ++ $packet->server_character_set = "08"; ++ $packet->server_status = self::create_packet_item(0b000000000000010); ++ $packet->extended_server_capabilities = self::create_packet_item(0b1000000111111111); ++ $packet->auth_plugin = "15"; ++ $packet->unused = "000000000000"; ++ $packet->mariadb_extended_server_capabilities = self::create_packet_item(0b1111, false, 'V'); ++ $packet->mariadb_extended_server_capabilities_salt = "6c6b55463f49335f686c643100"; ++ $packet->mariadb_extended_server_capabilities_auth_plugin = self::create_packet_item('mysql_native_password'); ++ ++ return $packet; ++ } ++ ++ public function server_tabular_query_response(): array ++ { ++ $qr1 = new my_mysqli_fake_packet(); ++ $qr1->packet_length = "010000"; ++ $qr1->packet_number = "01"; ++ $qr1->field_count = "01"; ++ ++ $qr2 = new my_mysqli_fake_packet(); ++ $qr2->packet_length = "190000"; ++ $qr2->packet_number = "02"; ++ $qr2->catalog_length_plus_name = "0164"; ++ $qr2->db_length_plus_name = "0164"; ++ $qr2->table_length_plus_name = "0164"; ++ $qr2->original_t = "0164"; ++ $qr2->name_length_plus_name = "0164"; ++ $qr2->original_n = "0164"; ++ $qr2->canary = "0c"; ++ $qr2->charset = "3f00"; ++ $qr2->length = "0b000000"; ++ $qr2->type = "03"; ++ $qr2->flags = "0350"; ++ $qr2->decimals = "000000"; ++ ++ $qr3 = new my_mysqli_fake_packet(); ++ $qr3->full = "05000003fe00002200"; ++ ++ $qr4 = new my_mysqli_fake_packet(); ++ $qr4->full = "0400000401350174"; ++ ++ $qr5 = new my_mysqli_fake_packet(); ++ $qr5->full = "05000005fe00002200"; ++ ++ return [$qr1, $qr2, $qr3, $qr4, $qr5]; ++ } ++ ++ public function server_upsert_query_response(): array ++ { ++ $qr1 = new my_mysqli_fake_packet(); ++ $qr1->packet_length = "010000"; ++ $qr1->packet_number = "01"; ++ $qr1->field_count = "00"; // UPSERT ++ $qr1->affected_rows = "00"; ++ $qr1->affected_rows = "00"; ++ $qr1->last_insert_id = "00"; ++ $qr1->server_status = "0000"; ++ $qr1->warning_count = "0000"; ++ $qr1->len = "01"; ++ $qr1->filename = "65"; ++ $qr1->packet_length = sprintf("%02x0000", strlen($qr1->to_bytes())-4); ++ ++ return [$qr1]; ++ } ++ ++ public function server_stmt_prepare_response_start($num_field): my_mysqli_fake_packet ++ { ++ $pr1 = new my_mysqli_fake_packet(); ++ $pr1->packet_length = "0c0000"; ++ $pr1->packet_number = "01"; ++ $pr1->response_code = '00'; // OK ++ $pr1->statement_id = '01000000'; ++ $pr1->num_fields = $num_field; ++ $pr1->num_params = '0000'; ++ $pr1->filler = '00'; ++ $pr1->warnings = '0000'; ++ ++ return $pr1; ++ } ++ ++ public function server_stmt_prepare_response_end($packer_number): my_mysqli_fake_packet ++ { ++ $pr3 = new my_mysqli_fake_packet(); ++ $pr3->packet_length = "050000"; ++ $pr3->packet_number = $packer_number; ++ $pr3->packet_type = 'fe'; // EOF ++ $pr3->warnings = '0000'; ++ $pr3->server_status = '0200'; ++ ++ return $pr3; ++ } ++ ++ public function server_stmt_prepare_items_response(): array ++ { ++ $pr1 = $this->server_stmt_prepare_response_start('0100'); ++ ++ $pr2 = new my_mysqli_fake_packet(); ++ $pr2->packet_length = "300000"; ++ $pr2->packet_number = "02"; ++ $pr2->catalogue_len = '03'; ++ $pr2->catalogue = '646566'; // def ++ $pr2->db_len = '08'; ++ $pr2->db = '7068705f74657374'; // php_test ++ $pr2->table_len = '05'; ++ $pr2->table = '6974656d73'; // items ++ $pr2->orig_table_len = '05'; ++ $pr2->orig_table = '6974656d73'; // items ++ $pr2->name_len = '04'; ++ $pr2->name = '6974656d'; ++ $pr2->orig_name_len = '04'; ++ $pr2->orig_name = '6974656d'; ++ $pr2->something = '0c'; ++ $pr2->charset = 'e000'; ++ $pr2->length = 'c8000000'; ++ $pr2->field_type = 'fd'; // FIELD_TYPE_VAR_STRING ++ $pr2->flags = '0110'; ++ $pr2->decimal = '00'; ++ $pr2->padding = '0000'; ++ ++ $pr3 = $this->server_stmt_prepare_response_end('03'); ++ ++ return [$pr1, $pr2, $pr3]; ++ } ++ ++ public function server_stmt_prepare_data_response_field($packet_number, $field_name): my_mysqli_fake_packet ++ { ++ if (strlen($field_name) != 6) { ++ throw new Exception("Invalid field length - only 6 is allowed"); ++ } ++ ++ $field = my_mysqli_data_field($field_name); ++ ++ $pr = new my_mysqli_fake_packet(); ++ $pr->packet_length = "320000"; ++ $pr->packet_number = $packet_number; ++ $pr->catalogue_len = '03'; ++ $pr->catalogue = bin2hex('def'); ++ $pr->db_len = '08'; ++ $pr->db = bin2hex('php_test'); ++ $pr->table_len = '04'; ++ $pr->table = bin2hex('data'); ++ $pr->orig_table_len = '04'; ++ $pr->orig_table = bin2hex('data'); ++ $pr->name_len = '06'; ++ $pr->name = bin2hex($field_name); ++ $pr->orig_name_len = '06'; ++ $pr->orig_name = bin2hex($field_name); ++ $pr->something = '0c'; ++ $pr->charset = $field['charset']; ++ $pr->length = $field['length']; ++ $pr->field_type = $field['type']; ++ $pr->flags = $field['flags']; ++ $pr->decimal = $field['decimal']; ++ $pr->padding = '0000'; ++ ++ return $pr; ++ } ++ ++ public function server_stmt_prepare_data_response(string $field_name): array ++ { ++ $pr1 = $this->server_stmt_prepare_response_start('0200'); ++ ++ $pr2 = $this->server_stmt_prepare_data_response_field('02', 'strval'); ++ $pr3 = $this->server_stmt_prepare_data_response_field('03', $field_name); ++ ++ $pr4 = $this->server_stmt_prepare_response_end('04'); ++ ++ return [$pr1, $pr2, $pr3, $pr4]; ++ } ++ ++ public function server_stmt_execute_items_response(): array ++ { ++ $pr1 = new my_mysqli_fake_packet(); ++ $pr1->packet_length = "010000"; ++ $pr1->packet_number = "01"; ++ $pr1->num_fields = '01'; ++ ++ $pr2 = new my_mysqli_fake_packet(); ++ $pr2->packet_length = "300000"; ++ $pr2->packet_number = "02"; ++ $pr2->catalogue_len = '03'; ++ $pr2->catalogue = '646566'; // def ++ $pr2->db_len = '08'; ++ $pr2->db = '7068705f74657374'; // php_test ++ $pr2->table_len = '05'; ++ $pr2->table = '6974656d73'; // items ++ $pr2->orig_table_len = '05'; ++ $pr2->orig_table = '6974656d73'; // items ++ $pr2->name_len = '04'; ++ $pr2->name = '6974656d'; ++ $pr2->orig_name_len = '04'; ++ $pr2->orig_name = '6974656d'; ++ $pr2->something = '0c'; ++ $pr2->charset = 'e000'; ++ $pr2->length = 'c8000000'; ++ $pr2->field_type = 'fd'; // FIELD_TYPE_VAR_STRING ++ $pr2->flags = '0110'; ++ $pr2->decimal = '00'; ++ $pr2->padding = '0000'; ++ ++ $pr3 = new my_mysqli_fake_packet(); ++ $pr3->packet_length = "050000"; ++ $pr3->packet_number = "03"; ++ $pr3->packet_type = 'fe'; // EOF ++ $pr3->warnings = '0000'; ++ $pr3->server_status = '2200'; ++ ++ $pr4 = new my_mysqli_fake_packet(); ++ $pr4->packet_length = "070000"; ++ $pr4->packet_number = "04"; ++ $pr4->packet_type = '00'; // OK ++ $pr4->affected_rows = '00'; ++ $pr4->row_data_len = '04'; ++ $pr4->row_data = '74657374'; // item ++ ++ $pr5 = new my_mysqli_fake_packet(); ++ $pr5->full = '05000005fe00002200'; ++ ++ return [$pr1, $pr2, $pr3, $pr4, $pr5]; ++ } ++ ++ private function server_execute_data_response_start(string $field_name): array ++ { ++ $pr1 = new my_mysqli_fake_packet(); ++ $pr1->packet_length = "010000"; ++ $pr1->packet_number = "01"; ++ $pr1->num_fields = '02'; ++ ++ $pr2 = new my_mysqli_fake_packet(); ++ $pr2->packet_length = "320000"; ++ $pr2->packet_number = "02"; ++ $pr2->catalogue_len = '03'; ++ $pr2->catalogue = '646566'; // def ++ $pr2->db_len = '08'; ++ $pr2->db = '7068705f74657374'; // php_test ++ $pr2->table_len = '04'; ++ $pr2->table = bin2hex('data'); ++ $pr2->orig_table_len = '04'; ++ $pr2->orig_table = bin2hex('data'); ++ $pr2->name_len = '06'; ++ $pr2->name = bin2hex('strval'); ++ $pr2->orig_name_len = '06'; ++ $pr2->orig_name = bin2hex('strval'); ++ $pr2->something = '0c'; ++ $pr2->charset = 'e000'; ++ $pr2->length = 'c8000000'; ++ $pr2->field_type = 'fd'; // FIELD_TYPE_VAR_STRING ++ $pr2->flags = '0110'; ++ $pr2->decimal = '00'; ++ $pr2->padding = '0000'; ++ ++ $field = my_mysqli_data_field($field_name); ++ ++ $pr3 = new my_mysqli_fake_packet(); ++ $pr3->packet_length = "320000"; ++ $pr3->packet_number = "03"; ++ $pr3->catalogue_len = '03'; ++ $pr3->catalogue = '646566'; // def ++ $pr3->db_len = '08'; ++ $pr3->db = '7068705f74657374'; // php_test ++ $pr3->table_len = '04'; ++ $pr3->table = bin2hex('data'); ++ $pr3->orig_table_len = '04'; ++ $pr3->orig_table = bin2hex('data'); ++ $pr3->name_len = '06'; ++ $pr3->name = bin2hex($field_name); ++ $pr3->orig_name_len = '06'; ++ $pr3->orig_name = bin2hex($field_name); ++ $pr3->something = '0c'; ++ $pr3->charset = $field['charset']; ++ $pr3->length = $field['length']; ++ $pr3->field_type = $field['type']; ++ $pr3->flags = $field['flags']; ++ $pr3->decimal = $field['decimal']; ++ $pr3->padding = '0000'; ++ ++ $pr4 = new my_mysqli_fake_packet(); ++ $pr4->packet_length = "050000"; ++ $pr4->packet_number = "04"; ++ $pr4->packet_type = 'fe'; // EOF ++ $pr4->warnings = '0000'; ++ $pr4->server_status = '2200'; ++ ++ return [$field, $pr1, $pr2, $pr3, $pr4]; ++ } ++ ++ private function server_execute_data_response_end(): my_mysqli_fake_packet ++ { ++ $pr6 = new my_mysqli_fake_packet(); ++ $pr6->packet_length = '050000'; ++ $pr6->packet_number = "06"; ++ $pr6->packet_type = 'fe'; // EOF ++ $pr6->warnings = '0000'; ++ $pr6->server_status = '2200'; ++ ++ return $pr6; ++ } ++ ++ public function server_stmt_execute_data_response(string $field_name): array ++ { ++ [$field, $pr1, $pr2, $pr3, $pr4] = $this->server_execute_data_response_start($field_name); ++ ++ $pr5 = new my_mysqli_fake_packet(); ++ $pr5->packet_length = $field['stmt_data_packet_length']; ++ $pr5->packet_number = "05"; ++ $pr5->packet_type = '00'; // OK ++ $pr5->affected_rows = '00'; ++ $pr5->row_field1_len = '04'; ++ $pr5->row_field1_data = '74657374'; // test ++ $pr5->row_field2 = $field['stmt_data_value']; ++ ++ return [$pr1, $pr2, $pr3, $pr4, $pr5, $this->server_execute_data_response_end()]; ++ } ++ ++ public function server_query_execute_data_response(string $field_name): array ++ { ++ [$field, $pr1, $pr2, $pr3, $pr4] = $this->server_execute_data_response_start($field_name); ++ ++ $pr5 = new my_mysqli_fake_packet(); ++ $pr5->packet_length = $field['query_data_packet_length']; ++ $pr5->packet_number = "05"; ++ $pr5->row_field1_len = '04'; ++ $pr5->row_field1_data = '74657374'; // test ++ $pr5->row_field2 = $field['query_data_value']; ++ ++ return [$pr1, $pr2, $pr3, $pr4, $pr5, $this->server_execute_data_response_end()]; ++ } ++} ++ ++class my_mysqli_fake_server_conn ++{ ++ private $conn; ++ public $packet_generator; ++ ++ public function __construct($socket) ++ { ++ $this->packet_generator = new my_mysqli_fake_packet_generator(); ++ $this->conn = stream_socket_accept($socket); ++ if ($this->conn) { ++ fprintf(STDERR, "[*] Connection established\n"); ++ } else { ++ fprintf(STDERR, "[*] Failed to establish connection\n"); ++ } ++ } ++ ++ public function packets_to_bytes(array $packets): string ++ { ++ return implode('', array_map(fn($s) => $s->to_bytes(), $packets)); ++ } ++ ++ public function send($payload, $message = null): void ++ { ++ if ($message) { ++ fprintf(STDERR, "[*] Sending - %s: %s\n", $message, bin2hex($payload)); ++ } ++ fwrite($this->conn, $payload); ++ } ++ ++ public function read($bytes_len = 1024) ++ { ++ // wait 10ms to fill the buffer ++ usleep(10000); ++ $data = fread($this->conn, $bytes_len); ++ if ($data) { ++ fprintf(STDERR, "[*] Received: %s\n", bin2hex($data)); ++ } ++ } ++ ++ public function close() ++ { ++ fclose($this->conn); ++ } ++ ++ public function send_server_greetings() ++ { ++ $this->send($this->packet_generator->server_greetings()->to_bytes(), "Server Greeting"); ++ } ++ ++ public function send_server_ok() ++ { ++ $this->send($this->packet_generator->server_ok()->to_bytes(), "Server OK"); ++ } ++ ++ public function send_server_tabular_query_response(): void ++ { ++ $packets = $this->packet_generator->server_tabular_query_response(); ++ $this->send($this->packets_to_bytes($packets), "Tabular response"); ++ } ++ ++ public function send_server_stmt_prepare_items_response(): void ++ { ++ $packets = $this->packet_generator->server_stmt_prepare_items_response(); ++ $this->send($this->packets_to_bytes($packets), "Stmt prepare items"); ++ } ++ ++ ++ public function send_server_stmt_prepare_data_response(string $field_name): void ++ { ++ $packets = $this->packet_generator->server_stmt_prepare_data_response($field_name); ++ $this->send($this->packets_to_bytes($packets), "Stmt prepare data $field_name"); ++ } ++ ++ public function send_server_stmt_execute_items_response(): void ++ { ++ $packets = $this->packet_generator->server_stmt_execute_items_response(); ++ $this->send($this->packets_to_bytes($packets), "Stmt execute items"); ++ } ++ ++ public function send_server_stmt_execute_data_response(string $field_name): void ++ { ++ $packets = $this->packet_generator->server_stmt_execute_data_response($field_name); ++ $this->send($this->packets_to_bytes($packets), "Stmt execute data $field_name"); ++ } ++ ++ public function send_server_query_execute_data_response(string $field_name): void ++ { ++ $packets = $this->packet_generator->server_query_execute_data_response($field_name); ++ $this->send($this->packets_to_bytes($packets), "Query execute data $field_name"); ++ } ++} ++ ++class my_mysqli_fake_server_process ++{ ++ public function __construct(private $process, private array $pipes) {} ++ ++ public function terminate(bool $wait = false) ++ { ++ if ($wait) { ++ $this->wait(); ++ } ++ proc_terminate($this->process); ++ } ++ ++ public function wait() ++ { ++ echo fgets($this->pipes[1]); ++ } ++} ++ ++function my_mysqli_test_tabular_response_def_over_read(my_mysqli_fake_server_conn $conn): void ++{ ++ $rh = $conn->packet_generator->server_tabular_query_response(); ++ ++ // Length of the packet is modified to include the next added data ++ $rh[1]->packet_length = "1e0000"; ++ ++ // We add a length field encoded on 4 bytes which evaluates to 65536. If the process crashes because ++ // the heap has been overread, lower this value. ++ $rh[1]->extra_def_size = "fd000001"; # 65536 ++ ++ // Filler ++ $rh[1]->extra_def_data = "aa"; ++ ++ $trrh = $conn->packets_to_bytes($rh); ++ ++ $conn->send_server_greetings(); ++ $conn->read(); ++ $conn->send_server_ok(); ++ $conn->read(); ++ $conn->send($trrh, "Malicious Tabular Response [Extract heap through buffer over-read]"); ++ $conn->read(65536); ++} ++ ++function my_mysqli_test_upsert_response_filename_over_read(my_mysqli_fake_server_conn $conn): void ++{ ++ $rh = $conn->packet_generator->server_upsert_query_response(); ++ ++ // Set extra length to overread ++ $rh[0]->len = "fa"; ++ ++ $trrh = $conn->packets_to_bytes($rh); ++ ++ $conn->send_server_greetings(); ++ $conn->read(); ++ $conn->send_server_ok(); ++ $conn->read(); ++ $conn->send($trrh, "Malicious Tabular Response [Extract heap through buffer over-read]"); ++ $conn->read(65536); ++} ++ ++function my_mysqli_test_auth_response_message_over_read(my_mysqli_fake_server_conn $conn): void ++{ ++ $p = $conn->packet_generator->server_ok(); ++ $p->packet_length = "090000"; ++ $p->message_len = "fcff"; ++ ++ $conn->send_server_greetings(); ++ $conn->read(); ++ $conn->send($p->to_bytes(), "Malicious OK Auth Response [Extract heap through buffer over-read]"); ++ $conn->read(); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_string(my_mysqli_fake_server_conn $conn): void ++{ ++ $rh = $conn->packet_generator->server_stmt_execute_items_response(); ++ ++ // Set extra length to overread ++ $rh[3]->row_data_len = "fa"; ++ ++ $conn->send_server_greetings(); ++ $conn->read(); ++ $conn->send_server_ok(); ++ $conn->read(); ++ $conn->send_server_stmt_prepare_items_response(); ++ $conn->read(); ++ $conn->send($conn->packets_to_bytes($rh), "Malicious Stmt Response for items [Extract heap through buffer over-read]"); ++ $conn->read(65536); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_two_fields( ++ my_mysqli_fake_server_conn $conn, ++ string $field_name, ++ string $row_field1_len = '06' ++): void { ++ $rh = $conn->packet_generator->server_stmt_execute_data_response($field_name); ++ ++ // Set extra length to overread by two bytes ++ $rh[4]->row_field1_len = $row_field1_len; ++ ++ $conn->send_server_greetings(); ++ $conn->read(); ++ $conn->send_server_ok(); ++ $conn->read(); ++ $conn->send_server_stmt_prepare_data_response($field_name); ++ $conn->read(); ++ $conn->send( ++ $conn->packets_to_bytes($rh), ++ "Malicious Stmt Response for data $field_name [Extract heap through buffer over-read]" ++ ); ++ $conn->read(65536); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_int(my_mysqli_fake_server_conn $conn): void ++{ ++ my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'intval'); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_float(my_mysqli_fake_server_conn $conn): void ++{ ++ my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'fltval'); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_double(my_mysqli_fake_server_conn $conn): void ++{ ++ my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'dblval'); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_date(my_mysqli_fake_server_conn $conn): void ++{ ++ my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'datval'); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_time(my_mysqli_fake_server_conn $conn): void ++{ ++ my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'timval', '0c'); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_datetime(my_mysqli_fake_server_conn $conn): void ++{ ++ my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'dtival'); ++} ++ ++function my_mysqli_test_stmt_response_row_no_space(my_mysqli_fake_server_conn $conn): void ++{ ++ my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'strval', '09'); ++} ++ ++function my_mysqli_test_stmt_response_row_over_read_bit(my_mysqli_fake_server_conn $conn): void ++{ ++ my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'bitval'); ++} ++ ++function my_mysqli_test_stmt_response_row_read_two_fields(my_mysqli_fake_server_conn $conn): void ++{ ++ $conn->send_server_greetings(); ++ $conn->read(); ++ $conn->send_server_ok(); ++ $conn->read(); ++ $field_names = array_keys(my_mysqli_data_fields()); ++ foreach ($field_names as $field_name) { ++ $conn->send_server_stmt_prepare_data_response($field_name); ++ $conn->read(65536); ++ $conn->send_server_stmt_execute_data_response($field_name); ++ $conn->read(65536); ++ } ++} ++ ++function my_mysqli_test_query_response_row_length_overflow(my_mysqli_fake_server_conn $conn): void ++{ ++ $rh = $conn->packet_generator->server_query_execute_data_response('strval'); ++ ++ // Set extra length to overread by two bytes ++ $rh[4]->row_field2 = 'fefefefefe'; ++ ++ $conn->send_server_greetings(); ++ $conn->read(); ++ $conn->send_server_ok(); ++ $conn->read(); ++ $conn->send($conn->packets_to_bytes($rh), "Malicious Query Response for data strval field [length overflow]"); ++ $conn->read(65536); ++} ++ ++function my_mysqli_test_query_response_row_read_two_fields(my_mysqli_fake_server_conn $conn): void ++{ ++ $conn->send_server_greetings(); ++ $conn->read(); ++ $conn->send_server_ok(); ++ $conn->read(); ++ $field_names = array_keys(my_mysqli_data_fields()); ++ foreach ($field_names as $field_name) { ++ $conn->send_server_query_execute_data_response($field_name); ++ $conn->read(); ++ } ++} ++ ++function run_fake_server(string $test_function, $port = 33305): void ++{ ++ $address = '127.0.0.1'; ++ ++ $socket = @stream_socket_server("tcp://$address:$port", $errno, $errstr); ++ if (!$socket) { ++ die("Failed to create socket: $errstr ($errno)\n"); ++ } ++ echo "[*] Server started\n"; ++ ++ try { ++ $conn = new my_mysqli_fake_server_conn($socket); ++ $test_function_name = 'my_mysqli_test_' . $test_function; ++ call_user_func($test_function_name, $conn); ++ $conn->close(); ++ } catch (Exception $e) { ++ fprintf(STDERR, "[!] Exception: " . $e->getMessage() . "\n"); ++ } ++ ++ fclose($socket); ++ ++ echo "[*] Server finished\n"; ++} ++ ++ ++function run_fake_server_in_background($test_function, $port = 33305): my_mysqli_fake_server_process ++{ ++ $command = [PHP_BINARY, '-n', __FILE__, 'mysqli_fake_server', $test_function, $port]; ++ ++ $descriptorspec = array( ++ 0 => array("pipe", "r"), ++ 1 => array("pipe", "w"), ++ 2 => STDERR, ++ ); ++ ++ $process = proc_open($command, $descriptorspec, $pipes); ++ ++ if (is_resource($process)) { ++ return new my_mysqli_fake_server_process($process, $pipes); ++ } else { ++ throw new Exception("Failed to start server process"); ++ } ++} ++ ++if (isset($argv) && $argc > 2 && $argv[1] == 'mysqli_fake_server') { ++ run_fake_server($argv[2], $argv[3] ?? '33305'); ++} +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt +new file mode 100644 +index 00000000000..db54a6c0177 +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt +@@ -0,0 +1,38 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - auth message buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 50001; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('auth_response_message_over_read', $port); ++$process->wait(); ++ ++try { ++ $conn = new mysqli( $servername, $username, $password, "", $port ); ++ $info = mysqli_info($conn); ++ var_dump($info); ++} catch (Exception $e) { ++ echo $e->getMessage() . PHP_EOL; ++} ++ ++$process->terminate(); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Malicious OK Auth Response [Extract heap through buffer over-read]: 0900000200000002000000fcff ++ ++Warning: mysqli::__construct(): OK packet message length is past the packet size in %s on line %d ++Unknown error while trying to connect via tcp://127.0.0.1:50001 ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt +new file mode 100644 +index 00000000000..77f2232eca6 +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-def.phpt +@@ -0,0 +1,47 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - tabular default) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('tabular_response_def_over_read', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Running query on the fake server...\n"; ++ ++$result = $conn->query("SELECT * from users"); ++ ++if ($result) { ++ $all_fields = $result->fetch_fields(); ++ var_dump($result->fetch_all(MYSQLI_ASSOC)); ++ var_dump(get_object_vars($all_fields[0])["def"]); ++} ++ ++$conn->close(); ++ ++$process->terminate(); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Running query on the fake server... ++[*] Received: 140000000353454c454354202a2066726f6d207573657273 ++[*] Sending - Malicious Tabular Response [Extract heap through buffer over-read]: 01000001011e0000020164016401640164016401640c3f000b000000030350000000fd000001aa05000003fe00002200040000040135017405000005fe00002200 ++ ++Warning: mysqli::query(): Protocol error. Server sent default for unsupported field list (mysqlnd_wireprotocol.c:%d) in %s on line %d ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt +new file mode 100644 +index 00000000000..0b4db8ccece +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-filename.phpt +@@ -0,0 +1,43 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - upsert filename buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('upsert_response_filename_over_read', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++echo "[*] Running query on the fake server...\n"; ++ ++$result = $conn->query("SELECT * from users"); ++$info = mysqli_info($conn); ++ ++var_dump($info); ++ ++$process->terminate(); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Running query on the fake server... ++[*] Received: 140000000353454c454354202a2066726f6d207573657273 ++[*] Sending - Malicious Tabular Response [Extract heap through buffer over-read]: 0900000100000000000000fa65 ++ ++Warning: mysqli::query(): RSET_HEADER packet additional data length is past 249 bytes the packet size in %s on line %d ++ ++Warning: mysqli::query(): Error reading result set's header in %s on line %d ++NULL ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt +new file mode 100644 +index 00000000000..f141a79bdaa +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt +@@ -0,0 +1,48 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row no space for the field) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('query_response_row_length_overflow', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Query the fake server...\n"; ++$sql = "SELECT strval, strval FROM data"; ++ ++$result = $conn->query($sql); ++ ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row['strval']); ++ } ++} ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Query the fake server... ++[*] Received: 200000000353454c4543542073747276616c2c2073747276616c2046524f4d2064617461 ++[*] Sending - Malicious Query Response for data strval field [length overflow]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe000022000a0000050474657374fefefefefe05000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after end of packet in %s on line %d ++[*] Received: 0100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt +new file mode 100644 +index 00000000000..e43518217eb +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row bit buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_over_read_bit', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT bitval, timval FROM data"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["bitval"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 200000001653454c4543542062697476616c2c2074696d76616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data bitval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for data bitval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe00002200100000050000067465737408080808080808080805000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt +new file mode 100644 +index 00000000000..76158e940d0 +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row date buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_over_read_date', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT strval, datval FROM data"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["datval"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 200000001653454c4543542073747276616c2c2064617476616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data datval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for data datval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe000022000c0000050000067465737404de070c0f05000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt +new file mode 100644 +index 00000000000..f53d5b83bd4 +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row datetime buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_over_read_datetime', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT strval, dtival FROM data"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["dtival"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 200000001653454c4543542073747276616c2c2064746976616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data dtival: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for data dtival [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe000022000f0000050000067465737407de070c100d000105000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt +new file mode 100644 +index 00000000000..03c9b045d73 +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row double buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_over_read_double', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT strval, dblval FROM data"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["dblval"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 200000001653454c4543542073747276616c2c2064626c76616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data dblval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for data dblval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe000022000f00000500000674657374333333333333f33f05000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt +new file mode 100644 +index 00000000000..b1ec9aa51ec +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row int buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_over_read_float', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT strval, fltval FROM data"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["fltval"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 200000001653454c4543542073747276616c2c20666c7476616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data fltval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for data fltval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe000022000b000005000006746573743333134005000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt +new file mode 100644 +index 00000000000..426d9ea7b3f +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row int buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_over_read_int', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT strval, intval FROM data"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["intval"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 200000001653454c4543542073747276616c2c20696e7476616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data intval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for data intval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe000022000b000005000006746573740e00000005000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt +new file mode 100644 +index 00000000000..6db6952d42a +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row no space for the field) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_no_space', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT strval, strval FROM data"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["strval"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 200000001653454c4543542073747276616c2c2073747276616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data strval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for data strval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe000022000c00000500000974657374047465737405000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. No packet space left for the field in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt +new file mode 100644 +index 00000000000..55bad4cc544 +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row string buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_over_read_string', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT item FROM items"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["item"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 170000001653454c454354206974656d2046524f4d206974656d73 ++[*] Sending - Stmt prepare items: 0c0000010001000000010000000000003000000203646566087068705f74657374056974656d73056974656d73046974656d046974656d0ce000c8000000fd011000000005000003fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for items [Extract heap through buffer over-read]: 01000001013000000203646566087068705f74657374056974656d73056974656d73046974656d046974656d0ce000c8000000fd011000000005000003fe00002200070000040000fa7465737405000005fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt +new file mode 100644 +index 00000000000..06918c375f3 +--- /dev/null ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt +@@ -0,0 +1,53 @@ ++--TEST-- ++GHSA-h35g-vwh6-m678 (mysqlnd leaks partial content of the heap - stmt row time buffer over-read) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_over_read_time', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++echo "[*] Preparing statement on the fake server...\n"; ++$stmt = $conn->prepare("SELECT strval, timval FROM data"); ++ ++$stmt->execute(); ++$result = $stmt->get_result(); ++ ++// Fetch and display the results ++if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row["timval"]); ++ } ++} ++$stmt->close(); ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECTF-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Preparing statement on the fake server... ++[*] Received: 200000001653454c4543542073747276616c2c2074696d76616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data timval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Malicious Stmt Response for data timval [Extract heap through buffer over-read]: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe000022001000000500000c7465737408000000000015080105000006fe00002200 ++ ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %d ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/protocol_query_row_fetch_data.phpt b/ext/mysqli/tests/protocol_query_row_fetch_data.phpt +new file mode 100644 +index 00000000000..524fe5e587c +--- /dev/null ++++ b/ext/mysqli/tests/protocol_query_row_fetch_data.phpt +@@ -0,0 +1,74 @@ ++--TEST-- ++MySQL protocol - statement row data fetch) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('query_response_row_read_two_fields', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++function my_query($conn, $field) ++{ ++ $sql = "SELECT strval, $field FROM data"; ++ ++ $result = $conn->query($sql); ++ ++ if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row[$field]); ++ } ++ } ++} ++ ++foreach (my_mysqli_data_fields() as $field_name => $field) { ++ my_query($conn, $field_name); ++} ++ ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECT-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Received: 200000000353454c4543542073747276616c2c20696e7476616c2046524f4d2064617461 ++[*] Sending - Query execute data intval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe0000220008000005047465737402313405000006fe00002200 ++string(2) "14" ++[*] Received: 200000000353454c4543542073747276616c2c20666c7476616c2046524f4d2064617461 ++[*] Sending - Query execute data fltval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe0000220009000005047465737403322e3305000006fe00002200 ++string(3) "2.3" ++[*] Received: 200000000353454c4543542073747276616c2c2064626c76616c2046524f4d2064617461 ++[*] Sending - Query execute data dblval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe0000220009000005047465737403312e3205000006fe00002200 ++string(3) "1.2" ++[*] Received: 200000000353454c4543542073747276616c2c2064617476616c2046524f4d2064617461 ++[*] Sending - Query execute data datval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe000022001000000504746573740a323031342d31322d313505000006fe00002200 ++string(10) "2014-12-15" ++[*] Received: 200000000353454c4543542073747276616c2c2074696d76616c2046524f4d2064617461 ++[*] Sending - Query execute data timval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe000022000e00000504746573740831333a30303a303205000006fe00002200 ++string(8) "13:00:02" ++[*] Received: 200000000353454c4543542073747276616c2c2064746976616c2046524f4d2064617461 ++[*] Sending - Query execute data dtival: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe0000220019000005047465737413323031342d31322d31362031333a30303a303105000006fe00002200 ++string(19) "2014-12-16 13:00:01" ++[*] Received: 200000000353454c4543542073747276616c2c2062697476616c2046524f4d2064617461 ++[*] Sending - Query execute data bitval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe000022000e000005047465737408080808080808080805000006fe00002200 ++string(18) "578721382704613384" ++[*] Received: 200000000353454c4543542073747276616c2c2073747276616c2046524f4d2064617461 ++[*] Sending - Query execute data strval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe000022000a0000050474657374047465737405000006fe00002200 ++string(4) "test" ++[*] Received: 0100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqli/tests/protocol_stmt_row_fetch_data.phpt b/ext/mysqli/tests/protocol_stmt_row_fetch_data.phpt +new file mode 100644 +index 00000000000..d461ec24b8c +--- /dev/null ++++ b/ext/mysqli/tests/protocol_stmt_row_fetch_data.phpt +@@ -0,0 +1,91 @@ ++--TEST-- ++MySQL protocol - statement row data fetch) ++--EXTENSIONS-- ++mysqli ++--FILE-- ++<?php ++require_once 'fake_server.inc'; ++ ++$port = 33305; ++$servername = "127.0.0.1"; ++$username = "root"; ++$password = ""; ++ ++$process = run_fake_server_in_background('stmt_response_row_read_two_fields', $port); ++$process->wait(); ++ ++$conn = new mysqli($servername, $username, $password, "", $port); ++ ++function my_query($conn, $field) ++{ ++ $stmt = $conn->prepare("SELECT strval, $field FROM data"); ++ ++ $stmt->execute(); ++ $result = $stmt->get_result(); ++ ++ if ($result->num_rows > 0) { ++ while ($row = $result->fetch_assoc()) { ++ var_dump($row[$field]); ++ } ++ } ++} ++ ++foreach (my_mysqli_data_fields() as $field_name => $field) { ++ my_query($conn, $field_name); ++} ++ ++$conn->close(); ++ ++$process->terminate(true); ++ ++print "done!"; ++?> ++--EXPECT-- ++[*] Server started ++[*] Connection established ++[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 ++[*] Received: 6900000185a21a00000000c0080000000000000000000000000000000000000000000000726f6f7400006d7973716c5f6e61746976655f70617373776f7264002c0c5f636c69656e745f6e616d65076d7973716c6e640c5f7365727665725f686f7374093132372e302e302e31 ++[*] Sending - Server OK: 0700000200000002000000 ++[*] Received: 200000001653454c4543542073747276616c2c20696e7476616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data intval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Stmt execute data intval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106696e7476616c06696e7476616c0c3f000b00000003011000000005000004fe000022000b000005000004746573740e00000005000006fe00002200 ++int(14) ++[*] Received: 050000001901000000200000001653454c4543542073747276616c2c20666c7476616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data fltval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Stmt execute data fltval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f746573740464617461046461746106666c7476616c06666c7476616c0c3f000c0000000401101f000005000004fe000022000b000005000004746573743333134005000006fe00002200 ++float(2.3) ++[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2064626c76616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data dblval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Stmt execute data dblval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664626c76616c0664626c76616c0c3f00160000000501101f000005000004fe000022000f00000500000474657374333333333333f33f05000006fe00002200 ++float(1.2) ++[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2064617476616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data datval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Stmt execute data datval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664617476616c0664617476616c0c3f000a0000000a811000000005000004fe000022000c0000050000047465737404de070c0f05000006fe00002200 ++string(10) "2014-12-15" ++[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2074696d76616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data timval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Stmt execute data timval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610674696d76616c0674696d76616c0c3f000a0000000b811000000005000004fe00002200100000050000047465737408000000000015080105000006fe00002200 ++string(8) "21:08:01" ++[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2064746976616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data dtival: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Stmt execute data dtival: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610664746976616c0664746976616c0c3f00130000000c811000000005000004fe000022000f0000050000047465737407de070c100d000105000006fe00002200 ++string(19) "2014-12-16 13:00:01" ++[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2062697476616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data bitval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Stmt execute data bitval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610662697476616c0662697476616c0c3f004000000010211000000005000004fe00002200100000050000047465737408080808080808080805000006fe00002200 ++int(578721382704613384) ++[*] Received: 050000001901000000200000001653454c4543542073747276616c2c2073747276616c2046524f4d2064617461 ++[*] Sending - Stmt prepare data strval: 0c0000010001000000020000000000003200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe00000200 ++[*] Received: 0a00000017010000000001000000 ++[*] Sending - Stmt execute data strval: 01000001023200000203646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd01100000003200000303646566087068705f74657374046461746104646174610673747276616c0673747276616c0ce000c8000000fd011000000005000004fe000022000c00000500000474657374047465737405000006fe00002200 ++string(4) "test" ++[*] Received: 0500000019010000000100000001 ++[*] Server finished ++done! +diff --git a/ext/mysqlnd/mysqlnd_ps_codec.c b/ext/mysqlnd/mysqlnd_ps_codec.c +index 12edd9a2849..976143cb471 100644 +--- a/ext/mysqlnd/mysqlnd_ps_codec.c ++++ b/ext/mysqlnd/mysqlnd_ps_codec.c +@@ -52,6 +52,37 @@ struct st_mysqlnd_perm_bind mysqlnd_ps_fetch_functions[MYSQL_TYPE_LAST + 1]; + #define MYSQLND_PS_SKIP_RESULT_W_LEN -1 + #define MYSQLND_PS_SKIP_RESULT_STR -2 + ++static inline void ps_fetch_over_read_error(const zend_uchar ** row) ++{ ++ php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing after the end of packet"); ++ *row = NULL; ++} ++ ++static inline zend_bool ps_fetch_is_packet_over_read_with_variable_length(const unsigned int pack_len, ++ const zend_uchar ** row, const zend_uchar *p, unsigned int length) ++{ ++ if (pack_len == 0) { ++ return 0; ++ } ++ size_t length_len = *row - p; ++ if (length_len > pack_len || length > pack_len - length_len) { ++ ps_fetch_over_read_error(row); ++ return 1; ++ } ++ return 0; ++} ++ ++static inline zend_bool ps_fetch_is_packet_over_read_with_static_length(const unsigned int pack_len, ++ const zend_uchar ** row, unsigned int length) ++{ ++ if (pack_len > 0 && length > pack_len) { ++ ps_fetch_over_read_error(row); ++ return 1; ++ } ++ return 0; ++} ++ ++ + /* {{{ ps_fetch_from_1_to_8_bytes */ + void + ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, +@@ -60,6 +91,11 @@ ps_fetch_from_1_to_8_bytes(zval * zv, const MYSQLND_FIELD * const field, const u + char tmp[22]; + size_t tmp_len = 0; + zend_bool is_bit = field->type == MYSQL_TYPE_BIT; ++ ++ if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, byte_count))) { ++ return; ++ } ++ + DBG_ENTER("ps_fetch_from_1_to_8_bytes"); + DBG_INF_FMT("zv=%p byte_count=%u", zv, byte_count); + if (field->flags & UNSIGNED_FLAG) { +@@ -178,6 +214,11 @@ ps_fetch_float(zval * zv, const MYSQLND_FIELD * const field, const unsigned int + float fval; + double dval; + DBG_ENTER("ps_fetch_float"); ++ ++ if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, 4))) { ++ return; ++ } ++ + float4get(fval, *row); + (*row)+= 4; + DBG_INF_FMT("value=%f", fval); +@@ -200,6 +241,11 @@ ps_fetch_double(zval * zv, const MYSQLND_FIELD * const field, const unsigned int + { + double value; + DBG_ENTER("ps_fetch_double"); ++ ++ if (UNEXPECTED(ps_fetch_is_packet_over_read_with_static_length(pack_len, row, 8))) { ++ return; ++ } ++ + float8get(value, *row); + ZVAL_DOUBLE(zv, value); + (*row)+= 8; +@@ -216,9 +262,14 @@ ps_fetch_time(zval * zv, const MYSQLND_FIELD * const field, const unsigned int p + struct st_mysqlnd_time t; + zend_ulong length; /* First byte encodes the length*/ + char * value; ++ const zend_uchar *p = *row; + DBG_ENTER("ps_fetch_time"); + + if ((length = php_mysqlnd_net_field_length(row))) { ++ if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) { ++ return; ++ } ++ + const zend_uchar * to = *row; + + t.time_type = MYSQLND_TIMESTAMP_TIME; +@@ -273,9 +324,14 @@ ps_fetch_date(zval * zv, const MYSQLND_FIELD * const field, const unsigned int p + struct st_mysqlnd_time t = {0}; + zend_ulong length; /* First byte encodes the length*/ + char * value; ++ const zend_uchar *p = *row; + DBG_ENTER("ps_fetch_date"); + + if ((length = php_mysqlnd_net_field_length(row))) { ++ if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) { ++ return; ++ } ++ + const zend_uchar * to = *row; + + t.time_type = MYSQLND_TIMESTAMP_DATE; +@@ -310,9 +366,14 @@ ps_fetch_datetime(zval * zv, const MYSQLND_FIELD * const field, const unsigned i + struct st_mysqlnd_time t; + zend_ulong length; /* First byte encodes the length*/ + char * value; ++ const zend_uchar *p = *row; + DBG_ENTER("ps_fetch_datetime"); + + if ((length = php_mysqlnd_net_field_length(row))) { ++ if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) { ++ return; ++ } ++ + const zend_uchar * to = *row; + + t.time_type = MYSQLND_TIMESTAMP_DATETIME; +@@ -371,7 +432,11 @@ ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int + For now just copy, before we make it possible + to write \0 to the row buffer + */ ++ const zend_uchar *p = *row; + const zend_ulong length = php_mysqlnd_net_field_length(row); ++ if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) { ++ return; ++ } + DBG_ENTER("ps_fetch_string"); + DBG_INF_FMT("len = %lu", length); + DBG_INF("copying from the row buffer"); +@@ -387,7 +452,11 @@ ps_fetch_string(zval * zv, const MYSQLND_FIELD * const field, const unsigned int + static void + ps_fetch_bit(zval * zv, const MYSQLND_FIELD * const field, const unsigned int pack_len, const zend_uchar ** row) + { ++ const zend_uchar *p = *row; + const zend_ulong length = php_mysqlnd_net_field_length(row); ++ if (UNEXPECTED(ps_fetch_is_packet_over_read_with_variable_length(pack_len, row, p, length))) { ++ return; ++ } + ps_fetch_from_1_to_8_bytes(zv, field, pack_len, row, length); + } + /* }}} */ +diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c +index 4dcaf121fb7..c0f65f854ac 100644 +--- a/ext/mysqlnd/mysqlnd_result.c ++++ b/ext/mysqlnd/mysqlnd_result.c +@@ -505,7 +505,7 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s) + if (FAIL == (ret = result->m.read_result_metadata(result, conn))) { + /* For PS, we leave them in Prepared state */ + if (!stmt && conn->current_result) { +- mnd_efree(conn->current_result); ++ conn->current_result->m.free_result(conn->current_result, TRUE); + conn->current_result = NULL; + } + DBG_ERR("Error occurred while reading metadata"); +diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c +index e4a298adaea..844b90c5704 100644 +--- a/ext/mysqlnd/mysqlnd_wireprotocol.c ++++ b/ext/mysqlnd/mysqlnd_wireprotocol.c +@@ -715,7 +715,14 @@ php_mysqlnd_auth_response_read(MYSQLND_CONN_DATA * conn, void * _packet) + + /* There is a message */ + if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) { +- packet->message_len = MIN(net_len, buf_len - (p - begin)); ++ /* p can get past packet size when getting field length so it needs to be checked first ++ * and after that it can be checked that the net_len is not greater than the packet size */ ++ if ((p - buf) > packet->header.size || packet->header.size - (p - buf) < net_len) { ++ DBG_ERR_FMT("OK packet message length is past the packet size"); ++ php_error_docref(NULL, E_WARNING, "OK packet message length is past the packet size"); ++ DBG_RETURN(FAIL); ++ } ++ packet->message_len = net_len; + packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE); + } else { + packet->message = NULL; +@@ -1113,6 +1120,17 @@ php_mysqlnd_rset_header_read(MYSQLND_CONN_DATA * conn, void * _packet) + BAIL_IF_NO_MORE_DATA; + /* Check for additional textual data */ + if (packet->header.size > (size_t) (p - buf) && (len = php_mysqlnd_net_field_length(&p))) { ++ /* p can get past packet size when getting field length so it needs to be checked first ++ * and after that it can be checked that the len is not greater than the packet size */ ++ if ((p - buf) > packet->header.size || packet->header.size - (p - buf) < len) { ++ size_t local_file_name_over_read = ((p - buf) - packet->header.size) + len; ++ DBG_ERR_FMT("RSET_HEADER packet additional data length is past %zu bytes the packet size", ++ local_file_name_over_read); ++ php_error_docref(NULL, E_WARNING, ++ "RSET_HEADER packet additional data length is past %zu bytes the packet size", ++ local_file_name_over_read); ++ DBG_RETURN(FAIL); ++ } + packet->info_or_local_file.s = mnd_emalloc(len + 1); + if (packet->info_or_local_file.s) { + memcpy(packet->info_or_local_file.s, p, len); +@@ -1271,23 +1289,16 @@ php_mysqlnd_rset_field_read(MYSQLND_CONN_DATA * conn, void * _packet) + meta->flags |= NUM_FLAG; + } + +- +- /* +- def could be empty, thus don't allocate on the root. +- NULL_LENGTH (0xFB) comes from COM_FIELD_LIST when the default value is NULL. +- Otherwise the string is length encoded. +- */ ++ /* COM_FIELD_LIST is no longer supported so def should not be present */ + if (packet->header.size > (size_t) (p - buf) && + (len = php_mysqlnd_net_field_length(&p)) && + len != MYSQLND_NULL_LENGTH) + { +- BAIL_IF_NO_MORE_DATA; +- DBG_INF_FMT("Def found, length %lu", len); +- meta->def = packet->memory_pool->get_chunk(packet->memory_pool, len + 1); +- memcpy(meta->def, p, len); +- meta->def[len] = '\0'; +- meta->def_length = len; +- p += len; ++ DBG_ERR_FMT("Protocol error. Server sent default for unsupported field list"); ++ php_error_docref(NULL, E_WARNING, ++ "Protocol error. Server sent default for unsupported field list (mysqlnd_wireprotocol.c:%u)", ++ __LINE__); ++ DBG_RETURN(FAIL); + } + + root_ptr = meta->root = packet->memory_pool->get_chunk(packet->memory_pool, total_len); +@@ -1462,8 +1473,10 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fi + const unsigned int field_count, const MYSQLND_FIELD * const fields_metadata, + const zend_bool as_int_or_float, MYSQLND_STATS * const stats) + { +- unsigned int i; +- const zend_uchar * p = row_buffer->ptr; ++ unsigned int i, j; ++ size_t rbs = row_buffer->size; ++ const zend_uchar * rbp = row_buffer->ptr; ++ const zend_uchar * p = rbp; + const zend_uchar * null_ptr; + zend_uchar bit; + zval *current_field, *end_field, *start_field; +@@ -1496,7 +1509,21 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fi + statistic = STAT_BINARY_TYPE_FETCHED_NULL; + } else { + enum_mysqlnd_field_types type = fields_metadata[i].type; +- mysqlnd_ps_fetch_functions[type].func(current_field, &fields_metadata[i], 0, &p); ++ size_t row_position = p - rbp; ++ if (rbs <= row_position) { ++ for (j = 0, current_field = start_field; j < i; current_field++, j++) { ++ zval_ptr_dtor(current_field); ++ } ++ php_error_docref(NULL, E_WARNING, "Malformed server packet. No packet space left for the field"); ++ DBG_RETURN(FAIL); ++ } ++ mysqlnd_ps_fetch_functions[type].func(current_field, &fields_metadata[i], rbs - row_position, &p); ++ if (p == NULL) { ++ for (j = 0, current_field = start_field; j < i; current_field++, j++) { ++ zval_ptr_dtor(current_field); ++ } ++ DBG_RETURN(FAIL); ++ } + + if (MYSQLND_G(collect_statistics)) { + switch (fields_metadata[i].type) { +@@ -1553,7 +1580,7 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval * + unsigned int field_count, const MYSQLND_FIELD * fields_metadata, + zend_bool as_int_or_float, MYSQLND_STATS * stats) + { +- unsigned int i; ++ unsigned int i, j; + zval *current_field, *end_field, *start_field; + zend_uchar * p = row_buffer->ptr; + const size_t data_size = row_buffer->size; +@@ -1574,9 +1601,11 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval * + /* NULL or NOT NULL, this is the question! */ + if (len == MYSQLND_NULL_LENGTH) { + ZVAL_NULL(current_field); +- } else if ((p + len) > packet_end) { +- php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing "MYSQLND_SZ_T_SPEC +- " bytes after end of packet", (p + len) - packet_end - 1); ++ } else if (p > packet_end || len > packet_end - p) { ++ php_error_docref(NULL, E_WARNING, "Malformed server packet. Field length pointing after end of packet"); ++ for (j = 0, current_field = start_field; j < i; current_field++, j++) { ++ zval_ptr_dtor(current_field); ++ } + DBG_RETURN(FAIL); + } else { + #if defined(MYSQLND_STRING_TO_INT_CONVERSION) +-- +2.47.0 + +From aaeb9549a1bdfa787fc3d3a2d499b418d09a5387 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Mon, 18 Nov 2024 15:54:30 +0100 +Subject: [PATCH 2/6] Fix MySQLnd possible buffer over read in auth_protocol + +(cherry picked from commit 32f905f1d689aaa8eacd6331a18c0dd45972c3c1) +(cherry picked from commit d5f9da0d6af72ae21b0a9f4c94c59dfdd409e3e2) +--- + ext/mysqlnd/mysqlnd_wireprotocol.c | 27 +++++++++++++++++++++++++-- + 1 file changed, 25 insertions(+), 2 deletions(-) + +diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c +index 844b90c5704..e75c859bab7 100644 +--- a/ext/mysqlnd/mysqlnd_wireprotocol.c ++++ b/ext/mysqlnd/mysqlnd_wireprotocol.c +@@ -441,8 +441,31 @@ php_mysqlnd_greet_read(MYSQLND_CONN_DATA * conn, void * _packet) + if (packet->server_capabilities & CLIENT_PLUGIN_AUTH) { + BAIL_IF_NO_MORE_DATA; + /* The server is 5.5.x and supports authentication plugins */ +- packet->auth_protocol = estrdup((char *)p); +- p+= strlen(packet->auth_protocol) + 1; /* eat the '\0' */ ++ size_t remaining_size = packet->header.size - (size_t)(p - buf); ++ if (remaining_size == 0) { ++ /* Might be better to fail but this will fail anyway */ ++ packet->auth_protocol = estrdup(""); ++ } else { ++ /* Check if NUL present */ ++ char *null_terminator = memchr(p, '\0', remaining_size); ++ size_t auth_protocol_len; ++ if (null_terminator) { ++ /* If present, do basically estrdup */ ++ auth_protocol_len = null_terminator - (char *)p; ++ } else { ++ /* If not present, copy the rest of the buffer */ ++ auth_protocol_len = remaining_size; ++ } ++ char *auth_protocol = emalloc(auth_protocol_len + 1); ++ memcpy(auth_protocol, p, auth_protocol_len); ++ auth_protocol[auth_protocol_len] = '\0'; ++ packet->auth_protocol = auth_protocol; ++ ++ p += auth_protocol_len; ++ if (null_terminator) { ++ p++; ++ } ++ } + } + + DBG_INF_FMT("proto=%u server=%s thread_id=%u", +-- +2.47.0 + +From 83a0d005d51a44bbe77a178c387e2c9f042a335d Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Wed, 27 Nov 2024 10:54:10 +0100 +Subject: [PATCH 3/6] Avoid using uninitialised struct + + (cherry picked from commit 7e7817bc2f82570bbc510a2bf5e4e0ec09dbc774) + +(cherry picked from commit 69853e12b73a989e2383452356cdc07172427ae3) +--- + ext/mysqlnd/mysqlnd_result.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c +index c0f65f854ac..9f19adb0ce9 100644 +--- a/ext/mysqlnd/mysqlnd_result.c ++++ b/ext/mysqlnd/mysqlnd_result.c +@@ -549,8 +549,8 @@ mysqlnd_query_read_result_set_header(MYSQLND_CONN_DATA * conn, MYSQLND_STMT * s) + } + MYSQLND_INC_CONN_STATISTIC(conn->stats, statistic); + } ++ PACKET_FREE(&fields_eof); + } while (0); +- PACKET_FREE(&fields_eof); + break; /* switch break */ + } + } while (0); +-- +2.47.0 + +From 606322b7f3475fb5980f7785789adfb9c381abbc Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Sun, 24 Nov 2024 20:13:47 +0100 +Subject: [PATCH 4/6] Change port for mysqli fake server auth message test + +(cherry picked from commit 51f5539914ae62ef8568ea1ed302dceda897c439) +(cherry picked from commit 7e6af9c78d84d15880cfbc7867501f25ab982f5f) +--- + ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt +index db54a6c0177..279aec6a2cb 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt +@@ -6,7 +6,7 @@ mysqli + <?php + require_once 'fake_server.inc'; + +-$port = 50001; ++$port = 33305; + $servername = "127.0.0.1"; + $username = "root"; + $password = ""; +@@ -34,5 +34,5 @@ print "done!"; + [*] Sending - Malicious OK Auth Response [Extract heap through buffer over-read]: 0900000200000002000000fcff + + Warning: mysqli::__construct(): OK packet message length is past the packet size in %s on line %d +-Unknown error while trying to connect via tcp://127.0.0.1:50001 ++Unknown error while trying to connect via tcp://127.0.0.1:33305 + done! +-- +2.47.0 + +From c308c94eefdbddb041ed3cf502ef5dd6969e14f1 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Sun, 24 Nov 2024 23:48:27 +0100 +Subject: [PATCH 5/6] Increase MySQLi fake server read timeout for ASAN job + +(cherry picked from commit eb951b3d11109aa16982a2132f8d1fd5129edc9e) +(cherry picked from commit cae38b1c749d27dc3a65f7d65fdf238439e2676c) +--- + ext/mysqli/tests/fake_server.inc | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ext/mysqli/tests/fake_server.inc b/ext/mysqli/tests/fake_server.inc +index b02fabc584c..1127f6c00e3 100644 +--- a/ext/mysqli/tests/fake_server.inc ++++ b/ext/mysqli/tests/fake_server.inc +@@ -552,8 +552,8 @@ class my_mysqli_fake_server_conn + + public function read($bytes_len = 1024) + { +- // wait 10ms to fill the buffer +- usleep(10000); ++ // wait 20ms to fill the buffer ++ usleep(20000); + $data = fread($this->conn, $bytes_len); + if ($data) { + fprintf(STDERR, "[*] Received: %s\n", bin2hex($data)); +-- +2.47.0 + +From 016ffd6131a6174fe5ca5f4af3c66ad9f59ed879 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Wed, 27 Nov 2024 11:17:48 +0100 +Subject: [PATCH 6/6] adapt test + NEWS + +--- + NEWS | 4 + + ext/mysqli/tests/fake_server.inc | 99 ++++++++++--------- + .../ghsa-h35g-vwh6-m678-auth-message.phpt | 3 +- + ext/mysqli/tests/mysqli_change_user_new.phpt | 3 + + 4 files changed, 61 insertions(+), 48 deletions(-) + +diff --git a/NEWS b/NEWS +index f600d6aea65..09cf2cfa0bb 100644 +--- a/NEWS ++++ b/NEWS +@@ -11,6 +11,10 @@ Backported from 8.1.31 + . Fixed bug GHSA-g665-fm4p-vhff (OOB access in ldap_escape). (CVE-2024-8932) + (nielsdos) + ++- MySQLnd: ++ . Fixed bug GHSA-h35g-vwh6-m678 (Leak partial content of the heap through ++ heap buffer over-read). (CVE-2024-8929) (Jakub Zelenka) ++ + - PDO DBLIB: + . Fixed bug GHSA-5hqh-c84r-qjcv (Integer overflow in the dblib quoter causing + OOB writes). (CVE-2024-11236) (nielsdos) +diff --git a/ext/mysqli/tests/fake_server.inc b/ext/mysqli/tests/fake_server.inc +index 1127f6c00e3..18b77cb1a76 100644 +--- a/ext/mysqli/tests/fake_server.inc ++++ b/ext/mysqli/tests/fake_server.inc +@@ -1,6 +1,6 @@ + <?php + +-function my_mysqli_data_fields(): array ++function my_mysqli_data_fields() + { + return [ + 'intval' => [ +@@ -107,8 +107,11 @@ function my_mysqli_data_field(string $field): array + + class my_mysqli_fake_packet_item + { +- public function __construct(public string|null $name, public string $value, public bool $is_hex = true) ++ public function __construct($name, string $value, bool $is_hex = true) + { ++ $this->name = $name; ++ $this->value = $value; ++ $this->is_hex = $is_hex; + } + } + +@@ -126,7 +129,7 @@ class my_mysqli_fake_packet + return null; + } + +- public function __set(string $name, string|my_mysqli_fake_packet_item $value) ++ public function __set(string $name, $value) + { + if ($value instanceof my_mysqli_fake_packet_item) { + if ($value->name === null) { +@@ -146,7 +149,7 @@ class my_mysqli_fake_packet + $this->data[] = $value; + } + +- public function to_bytes(): string ++ public function to_bytes() + { + $bytes = ''; + foreach ($this->data as $item) { +@@ -158,7 +161,7 @@ class my_mysqli_fake_packet + + class my_mysqli_fake_packet_generator + { +- public static function create_packet_item(int|string $value, bool $is_hex = false, string $format = 'v'): my_mysqli_fake_packet_item ++ public static function create_packet_item($value, bool $is_hex = false, string $format = 'v') + { + if (is_string($value)) { + $packed_value = $value; +@@ -168,7 +171,7 @@ class my_mysqli_fake_packet_generator + return new my_mysqli_fake_packet_item(null, $packed_value, $is_hex); + } + +- public function server_ok(): my_mysqli_fake_packet ++ public function server_ok() + { + $packet = new my_mysqli_fake_packet(); + $packet->packet_length = "070000"; +@@ -181,7 +184,7 @@ class my_mysqli_fake_packet_generator + return $packet; + } + +- public function server_greetings(): my_mysqli_fake_packet ++ public function server_greetings() + { + $packet = new my_mysqli_fake_packet(); + $packet->packet_length = "580000"; +@@ -204,7 +207,7 @@ class my_mysqli_fake_packet_generator + return $packet; + } + +- public function server_tabular_query_response(): array ++ public function server_tabular_query_response() + { + $qr1 = new my_mysqli_fake_packet(); + $qr1->packet_length = "010000"; +@@ -239,7 +242,7 @@ class my_mysqli_fake_packet_generator + return [$qr1, $qr2, $qr3, $qr4, $qr5]; + } + +- public function server_upsert_query_response(): array ++ public function server_upsert_query_response() + { + $qr1 = new my_mysqli_fake_packet(); + $qr1->packet_length = "010000"; +@@ -257,7 +260,7 @@ class my_mysqli_fake_packet_generator + return [$qr1]; + } + +- public function server_stmt_prepare_response_start($num_field): my_mysqli_fake_packet ++ public function server_stmt_prepare_response_start($num_field) + { + $pr1 = new my_mysqli_fake_packet(); + $pr1->packet_length = "0c0000"; +@@ -272,7 +275,7 @@ class my_mysqli_fake_packet_generator + return $pr1; + } + +- public function server_stmt_prepare_response_end($packer_number): my_mysqli_fake_packet ++ public function server_stmt_prepare_response_end($packer_number) + { + $pr3 = new my_mysqli_fake_packet(); + $pr3->packet_length = "050000"; +@@ -284,7 +287,7 @@ class my_mysqli_fake_packet_generator + return $pr3; + } + +- public function server_stmt_prepare_items_response(): array ++ public function server_stmt_prepare_items_response() + { + $pr1 = $this->server_stmt_prepare_response_start('0100'); + +@@ -316,7 +319,7 @@ class my_mysqli_fake_packet_generator + return [$pr1, $pr2, $pr3]; + } + +- public function server_stmt_prepare_data_response_field($packet_number, $field_name): my_mysqli_fake_packet ++ public function server_stmt_prepare_data_response_field($packet_number, $field_name) + { + if (strlen($field_name) != 6) { + throw new Exception("Invalid field length - only 6 is allowed"); +@@ -350,7 +353,7 @@ class my_mysqli_fake_packet_generator + return $pr; + } + +- public function server_stmt_prepare_data_response(string $field_name): array ++ public function server_stmt_prepare_data_response(string $field_name) + { + $pr1 = $this->server_stmt_prepare_response_start('0200'); + +@@ -362,7 +365,7 @@ class my_mysqli_fake_packet_generator + return [$pr1, $pr2, $pr3, $pr4]; + } + +- public function server_stmt_execute_items_response(): array ++ public function server_stmt_execute_items_response() + { + $pr1 = new my_mysqli_fake_packet(); + $pr1->packet_length = "010000"; +@@ -413,7 +416,7 @@ class my_mysqli_fake_packet_generator + return [$pr1, $pr2, $pr3, $pr4, $pr5]; + } + +- private function server_execute_data_response_start(string $field_name): array ++ private function server_execute_data_response_start(string $field_name) + { + $pr1 = new my_mysqli_fake_packet(); + $pr1->packet_length = "010000"; +@@ -478,7 +481,7 @@ class my_mysqli_fake_packet_generator + return [$field, $pr1, $pr2, $pr3, $pr4]; + } + +- private function server_execute_data_response_end(): my_mysqli_fake_packet ++ private function server_execute_data_response_end() + { + $pr6 = new my_mysqli_fake_packet(); + $pr6->packet_length = '050000'; +@@ -490,7 +493,7 @@ class my_mysqli_fake_packet_generator + return $pr6; + } + +- public function server_stmt_execute_data_response(string $field_name): array ++ public function server_stmt_execute_data_response(string $field_name) + { + [$field, $pr1, $pr2, $pr3, $pr4] = $this->server_execute_data_response_start($field_name); + +@@ -506,7 +509,7 @@ class my_mysqli_fake_packet_generator + return [$pr1, $pr2, $pr3, $pr4, $pr5, $this->server_execute_data_response_end()]; + } + +- public function server_query_execute_data_response(string $field_name): array ++ public function server_query_execute_data_response(string $field_name) + { + [$field, $pr1, $pr2, $pr3, $pr4] = $this->server_execute_data_response_start($field_name); + +@@ -537,12 +540,12 @@ class my_mysqli_fake_server_conn + } + } + +- public function packets_to_bytes(array $packets): string ++ public function packets_to_bytes(array $packets) + { + return implode('', array_map(fn($s) => $s->to_bytes(), $packets)); + } + +- public function send($payload, $message = null): void ++ public function send($payload, $message = null) + { + if ($message) { + fprintf(STDERR, "[*] Sending - %s: %s\n", $message, bin2hex($payload)); +@@ -575,38 +578,38 @@ class my_mysqli_fake_server_conn + $this->send($this->packet_generator->server_ok()->to_bytes(), "Server OK"); + } + +- public function send_server_tabular_query_response(): void ++ public function send_server_tabular_query_response() + { + $packets = $this->packet_generator->server_tabular_query_response(); + $this->send($this->packets_to_bytes($packets), "Tabular response"); + } + +- public function send_server_stmt_prepare_items_response(): void ++ public function send_server_stmt_prepare_items_response() + { + $packets = $this->packet_generator->server_stmt_prepare_items_response(); + $this->send($this->packets_to_bytes($packets), "Stmt prepare items"); + } + + +- public function send_server_stmt_prepare_data_response(string $field_name): void ++ public function send_server_stmt_prepare_data_response(string $field_name) + { + $packets = $this->packet_generator->server_stmt_prepare_data_response($field_name); + $this->send($this->packets_to_bytes($packets), "Stmt prepare data $field_name"); + } + +- public function send_server_stmt_execute_items_response(): void ++ public function send_server_stmt_execute_items_response() + { + $packets = $this->packet_generator->server_stmt_execute_items_response(); + $this->send($this->packets_to_bytes($packets), "Stmt execute items"); + } + +- public function send_server_stmt_execute_data_response(string $field_name): void ++ public function send_server_stmt_execute_data_response(string $field_name) + { + $packets = $this->packet_generator->server_stmt_execute_data_response($field_name); + $this->send($this->packets_to_bytes($packets), "Stmt execute data $field_name"); + } + +- public function send_server_query_execute_data_response(string $field_name): void ++ public function send_server_query_execute_data_response(string $field_name) + { + $packets = $this->packet_generator->server_query_execute_data_response($field_name); + $this->send($this->packets_to_bytes($packets), "Query execute data $field_name"); +@@ -615,7 +618,11 @@ class my_mysqli_fake_server_conn + + class my_mysqli_fake_server_process + { +- public function __construct(private $process, private array $pipes) {} ++ public function __construct($process, array $pipes) ++ { ++ $this->process = $process; ++ $this->pipes = $pipes; ++ } + + public function terminate(bool $wait = false) + { +@@ -631,7 +638,7 @@ class my_mysqli_fake_server_process + } + } + +-function my_mysqli_test_tabular_response_def_over_read(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_tabular_response_def_over_read(my_mysqli_fake_server_conn $conn) + { + $rh = $conn->packet_generator->server_tabular_query_response(); + +@@ -655,7 +662,7 @@ function my_mysqli_test_tabular_response_def_over_read(my_mysqli_fake_server_con + $conn->read(65536); + } + +-function my_mysqli_test_upsert_response_filename_over_read(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_upsert_response_filename_over_read(my_mysqli_fake_server_conn $conn) + { + $rh = $conn->packet_generator->server_upsert_query_response(); + +@@ -672,7 +679,7 @@ function my_mysqli_test_upsert_response_filename_over_read(my_mysqli_fake_server + $conn->read(65536); + } + +-function my_mysqli_test_auth_response_message_over_read(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_auth_response_message_over_read(my_mysqli_fake_server_conn $conn) + { + $p = $conn->packet_generator->server_ok(); + $p->packet_length = "090000"; +@@ -684,7 +691,7 @@ function my_mysqli_test_auth_response_message_over_read(my_mysqli_fake_server_co + $conn->read(); + } + +-function my_mysqli_test_stmt_response_row_over_read_string(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_over_read_string(my_mysqli_fake_server_conn $conn) + { + $rh = $conn->packet_generator->server_stmt_execute_items_response(); + +@@ -705,7 +712,7 @@ function my_mysqli_test_stmt_response_row_over_read_two_fields( + my_mysqli_fake_server_conn $conn, + string $field_name, + string $row_field1_len = '06' +-): void { ++) { + $rh = $conn->packet_generator->server_stmt_execute_data_response($field_name); + + // Set extra length to overread by two bytes +@@ -724,47 +731,47 @@ function my_mysqli_test_stmt_response_row_over_read_two_fields( + $conn->read(65536); + } + +-function my_mysqli_test_stmt_response_row_over_read_int(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_over_read_int(my_mysqli_fake_server_conn $conn) + { + my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'intval'); + } + +-function my_mysqli_test_stmt_response_row_over_read_float(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_over_read_float(my_mysqli_fake_server_conn $conn) + { + my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'fltval'); + } + +-function my_mysqli_test_stmt_response_row_over_read_double(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_over_read_double(my_mysqli_fake_server_conn $conn) + { + my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'dblval'); + } + +-function my_mysqli_test_stmt_response_row_over_read_date(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_over_read_date(my_mysqli_fake_server_conn $conn) + { + my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'datval'); + } + +-function my_mysqli_test_stmt_response_row_over_read_time(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_over_read_time(my_mysqli_fake_server_conn $conn) + { + my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'timval', '0c'); + } + +-function my_mysqli_test_stmt_response_row_over_read_datetime(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_over_read_datetime(my_mysqli_fake_server_conn $conn) + { + my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'dtival'); + } + +-function my_mysqli_test_stmt_response_row_no_space(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_no_space(my_mysqli_fake_server_conn $conn) + { + my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'strval', '09'); + } + +-function my_mysqli_test_stmt_response_row_over_read_bit(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_over_read_bit(my_mysqli_fake_server_conn $conn) + { + my_mysqli_test_stmt_response_row_over_read_two_fields($conn, 'bitval'); + } + +-function my_mysqli_test_stmt_response_row_read_two_fields(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_stmt_response_row_read_two_fields(my_mysqli_fake_server_conn $conn) + { + $conn->send_server_greetings(); + $conn->read(); +@@ -779,7 +786,7 @@ function my_mysqli_test_stmt_response_row_read_two_fields(my_mysqli_fake_server_ + } + } + +-function my_mysqli_test_query_response_row_length_overflow(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_query_response_row_length_overflow(my_mysqli_fake_server_conn $conn) + { + $rh = $conn->packet_generator->server_query_execute_data_response('strval'); + +@@ -794,7 +801,7 @@ function my_mysqli_test_query_response_row_length_overflow(my_mysqli_fake_server + $conn->read(65536); + } + +-function my_mysqli_test_query_response_row_read_two_fields(my_mysqli_fake_server_conn $conn): void ++function my_mysqli_test_query_response_row_read_two_fields(my_mysqli_fake_server_conn $conn) + { + $conn->send_server_greetings(); + $conn->read(); +@@ -807,7 +814,7 @@ function my_mysqli_test_query_response_row_read_two_fields(my_mysqli_fake_server + } + } + +-function run_fake_server(string $test_function, $port = 33305): void ++function run_fake_server(string $test_function, $port = 33305) + { + $address = '127.0.0.1'; + +@@ -832,7 +839,7 @@ function run_fake_server(string $test_function, $port = 33305): void + } + + +-function run_fake_server_in_background($test_function, $port = 33305): my_mysqli_fake_server_process ++function run_fake_server_in_background($test_function, $port = 33305) + { + $command = [PHP_BINARY, '-n', __FILE__, 'mysqli_fake_server', $test_function, $port]; + +diff --git a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt +index 279aec6a2cb..161c9a5b8e6 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-auth-message.phpt +@@ -34,5 +34,4 @@ print "done!"; + [*] Sending - Malicious OK Auth Response [Extract heap through buffer over-read]: 0900000200000002000000fcff + + Warning: mysqli::__construct(): OK packet message length is past the packet size in %s on line %d +-Unknown error while trying to connect via tcp://127.0.0.1:33305 +-done! ++%A +diff --git a/ext/mysqli/tests/mysqli_change_user_new.phpt b/ext/mysqli/tests/mysqli_change_user_new.phpt +index 2b8993fc20d..d8fed6dfaee 100644 +--- a/ext/mysqli/tests/mysqli_change_user_new.phpt ++++ b/ext/mysqli/tests/mysqli_change_user_new.phpt +@@ -12,6 +12,9 @@ if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + + if (mysqli_get_server_version($link) < 50600) + die("SKIP For MySQL >= 5.6.0"); ++ ++if (mysqli_get_server_version($link) >= 10_00_00) ++ die("SKIP Not applicable for MariaDB"); + ?> + --FILE-- + <?php +-- +2.47.0 + diff --git a/php-cve-2024-8932.patch b/php-cve-2024-8932.patch new file mode 100644 index 0000000..1efcff9 --- /dev/null +++ b/php-cve-2024-8932.patch @@ -0,0 +1,139 @@ +From 50e9e72530a4805980384b8ea6672877af816145 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Thu, 26 Sep 2024 22:22:27 +0200 +Subject: [PATCH 4/7] Fix GHSA-g665-fm4p-vhff: OOB access in ldap_escape + +(cherry picked from commit f9ecf90070a11dad09ca7671a712f81cc2a7d52f) +(cherry picked from commit 9f367d847989b339c33369737daf573e30bab5f1) +--- + ext/ldap/ldap.c | 21 ++++++++++++++-- + ext/ldap/tests/GHSA-g665-fm4p-vhff-1.phpt | 28 ++++++++++++++++++++++ + ext/ldap/tests/GHSA-g665-fm4p-vhff-2.phpt | 29 +++++++++++++++++++++++ + 3 files changed, 76 insertions(+), 2 deletions(-) + create mode 100644 ext/ldap/tests/GHSA-g665-fm4p-vhff-1.phpt + create mode 100644 ext/ldap/tests/GHSA-g665-fm4p-vhff-2.phpt + +diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c +index 72a39bd93df..75adf1b5df2 100644 +--- a/ext/ldap/ldap.c ++++ b/ext/ldap/ldap.c +@@ -49,6 +49,7 @@ + + #include "ext/standard/php_string.h" + #include "ext/standard/info.h" ++#include "Zend/zend_exceptions.h" + + #ifdef HAVE_LDAP_SASL + #include <sasl/sasl.h> +@@ -3836,13 +3837,23 @@ static zend_string* php_ldap_do_escape(const zend_bool *map, const char *value, + zend_string *ret; + + for (i = 0; i < valuelen; i++) { +- len += (map[(unsigned char) value[i]]) ? 3 : 1; ++ size_t addend = (map[(unsigned char) value[i]]) ? 3 : 1; ++ if (len > ZSTR_MAX_LEN - addend) { ++ return NULL; ++ } ++ len += addend; + } + /* Per RFC 4514, a leading and trailing space must be escaped */ + if ((flags & PHP_LDAP_ESCAPE_DN) && (value[0] == ' ')) { ++ if (len > ZSTR_MAX_LEN - 2) { ++ return NULL; ++ } + len += 2; + } + if ((flags & PHP_LDAP_ESCAPE_DN) && ((valuelen > 1) && (value[valuelen - 1] == ' '))) { ++ if (len > ZSTR_MAX_LEN - 2) { ++ return NULL; ++ } + len += 2; + } + +@@ -3909,7 +3920,13 @@ PHP_FUNCTION(ldap_escape) + php_ldap_escape_map_set_chars(map, ignores, ignoreslen, 0); + } + +- RETURN_NEW_STR(php_ldap_do_escape(map, value, valuelen, flags)); ++ zend_string *result = php_ldap_do_escape(map, value, valuelen, flags); ++ if (UNEXPECTED(!result)) { ++ zend_throw_exception(NULL, "Argument #1 ($value) is too long", 0); ++ return; ++ } ++ ++ RETURN_NEW_STR(result); + } + + #ifdef STR_TRANSLATION +diff --git a/ext/ldap/tests/GHSA-g665-fm4p-vhff-1.phpt b/ext/ldap/tests/GHSA-g665-fm4p-vhff-1.phpt +new file mode 100644 +index 00000000000..734bbe91d42 +--- /dev/null ++++ b/ext/ldap/tests/GHSA-g665-fm4p-vhff-1.phpt +@@ -0,0 +1,28 @@ ++--TEST-- ++GHSA-g665-fm4p-vhff (OOB access in ldap_escape) ++--EXTENSIONS-- ++ldap ++--INI-- ++memory_limit=-1 ++--SKIPIF-- ++<?php ++if (PHP_INT_SIZE !== 4) die("skip only for 32-bit"); ++if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); ++?> ++--FILE-- ++<?php ++try { ++ ldap_escape(' '.str_repeat("#", 1431655758), "", LDAP_ESCAPE_DN); ++} catch (Exception $e) { ++ echo $e->getMessage(), "\n"; ++} ++ ++try { ++ ldap_escape(str_repeat("#", 1431655758).' ', "", LDAP_ESCAPE_DN); ++} catch (Exception $e) { ++ echo $e->getMessage(), "\n"; ++} ++?> ++--EXPECT-- ++ldap_escape(): Argument #1 ($value) is too long ++ldap_escape(): Argument #1 ($value) is too long +diff --git a/ext/ldap/tests/GHSA-g665-fm4p-vhff-2.phpt b/ext/ldap/tests/GHSA-g665-fm4p-vhff-2.phpt +new file mode 100644 +index 00000000000..5c1b0fb6611 +--- /dev/null ++++ b/ext/ldap/tests/GHSA-g665-fm4p-vhff-2.phpt +@@ -0,0 +1,29 @@ ++--TEST-- ++GHSA-g665-fm4p-vhff (OOB access in ldap_escape) ++--EXTENSIONS-- ++ldap ++--INI-- ++memory_limit=-1 ++--SKIPIF-- ++<?php ++if (PHP_INT_SIZE !== 4) die("skip only for 32-bit"); ++if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); ++?> ++--FILE-- ++<?php ++try { ++ ldap_escape(str_repeat("*", 1431655759), "", LDAP_ESCAPE_FILTER); ++} catch (Exception $e) { ++ echo $e->getMessage(), "\n"; ++} ++ ++// would allocate a string of length 2 ++try { ++ ldap_escape(str_repeat("*", 1431655766), "", LDAP_ESCAPE_FILTER); ++} catch (Exception $e) { ++ echo $e->getMessage(), "\n"; ++} ++?> ++--EXPECT-- ++ldap_escape(): Argument #1 ($value) is too long ++ldap_escape(): Argument #1 ($value) is too long +-- +2.47.0 + diff --git a/php-cve-2024-9026.patch b/php-cve-2024-9026.patch new file mode 100644 index 0000000..997917b --- /dev/null +++ b/php-cve-2024-9026.patch @@ -0,0 +1,245 @@ +From 4a8b8fa2592bd8862adeacb5b2faacb30500b9f9 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Thu, 12 Sep 2024 13:11:11 +0100 +Subject: [PATCH 07/11] Fix GHSA-865w-9rf3-2wh5: FPM: Logs from childrens may + be altered + +(cherry picked from commit 1f8e16172c7961045c2b0f34ba7613e3f21cdee8) +(cherry picked from commit 22f4d3504d7613ce78bb96aa53cbfe7d672fa036) +--- + sapi/fpm/fpm/fpm_stdio.c | 2 +- + .../log-bwp-msg-flush-split-sep-pos-end.phpt | 47 +++++++++++++++++++ + ...log-bwp-msg-flush-split-sep-pos-start.phpt | 47 +++++++++++++++++++ + 3 files changed, 95 insertions(+), 1 deletion(-) + create mode 100644 sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt + create mode 100644 sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt + +diff --git a/sapi/fpm/fpm/fpm_stdio.c b/sapi/fpm/fpm/fpm_stdio.c +index ddedfb48c7c..9d87273314a 100644 +--- a/sapi/fpm/fpm/fpm_stdio.c ++++ b/sapi/fpm/fpm/fpm_stdio.c +@@ -177,7 +177,7 @@ stdio_read: + if ((sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos) <= in_buf && + !memcmp(buf, &FPM_STDIO_CMD_FLUSH[cmd_pos], sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos)) { + zlog_stream_finish(log_stream); +- start = cmd_pos; ++ start = sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos; + } else { + zlog_stream_str(log_stream, &FPM_STDIO_CMD_FLUSH[0], cmd_pos); + } +diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt +new file mode 100644 +index 00000000000..52826320080 +--- /dev/null ++++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt +@@ -0,0 +1,47 @@ ++--TEST-- ++FPM: Buffered worker output plain log with msg with flush split position towards separator end ++--SKIPIF-- ++<?php include "skipif.inc"; ?> ++--FILE-- ++<?php ++ ++require_once "tester.inc"; ++ ++$cfg = <<<EOT ++[global] ++error_log = {{FILE:LOG}} ++[unconfined] ++listen = {{ADDR}} ++pm = dynamic ++pm.max_children = 5 ++pm.start_servers = 1 ++pm.min_spare_servers = 1 ++pm.max_spare_servers = 3 ++catch_workers_output = yes ++decorate_workers_output = no ++EOT; ++ ++$code = <<<EOT ++<?php ++file_put_contents('php://stderr', str_repeat('a', 1013) . "Quarkslab\0fscf\0Quarkslab"); ++EOT; ++ ++$tester = new FPM\Tester($cfg, $code); ++$tester->start(); ++$tester->expectLogStartNotices(); ++$tester->request()->expectEmptyBody(); ++$tester->expectLogLine(str_repeat('a', 1013) . "Quarkslab", decorated: false); ++$tester->expectLogLine("Quarkslab", decorated: false); ++$tester->terminate(); ++$tester->expectLogTerminatingNotices(); ++$tester->close(); ++ ++?> ++Done ++--EXPECT-- ++Done ++--CLEAN-- ++<?php ++require_once "tester.inc"; ++FPM\Tester::clean(); ++?> +diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt +new file mode 100644 +index 00000000000..34905938553 +--- /dev/null ++++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt +@@ -0,0 +1,47 @@ ++--TEST-- ++FPM: Buffered worker output plain log with msg with flush split position towards separator start ++--SKIPIF-- ++<?php include "skipif.inc"; ?> ++--FILE-- ++<?php ++ ++require_once "tester.inc"; ++ ++$cfg = <<<EOT ++[global] ++error_log = {{FILE:LOG}} ++[unconfined] ++listen = {{ADDR}} ++pm = dynamic ++pm.max_children = 5 ++pm.start_servers = 1 ++pm.min_spare_servers = 1 ++pm.max_spare_servers = 3 ++catch_workers_output = yes ++decorate_workers_output = no ++EOT; ++ ++$code = <<<EOT ++<?php ++file_put_contents('php://stderr', str_repeat('a', 1009) . "Quarkslab\0fscf\0Quarkslab"); ++EOT; ++ ++$tester = new FPM\Tester($cfg, $code); ++$tester->start(); ++$tester->expectLogStartNotices(); ++$tester->request()->expectEmptyBody(); ++$tester->expectLogLine(str_repeat('a', 1009) . "Quarkslab", decorated: false); ++$tester->expectLogLine("Quarkslab", decorated: false); ++$tester->terminate(); ++$tester->expectLogTerminatingNotices(); ++$tester->close(); ++ ++?> ++Done ++--EXPECT-- ++Done ++--CLEAN-- ++<?php ++require_once "tester.inc"; ++FPM\Tester::clean(); ++?> +-- +2.46.1 + +From 1154fbd3ddfa418bf2492c5366adaefb47c47737 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 26 Sep 2024 11:50:54 +0200 +Subject: [PATCH 09/11] NEWS for 8.1.30 backports + +(cherry picked from commit af3fb385e7b328ab89db26ec712d89c7096f0743) +--- + NEWS | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/NEWS b/NEWS +index a96518695fb..62616d6312d 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,23 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.1.30 ++ ++- CGI: ++ . Fixed bug GHSA-p99j-rfp4-xqvq (Bypass of CVE-2024-4577, Parameter Injection ++ Vulnerability). (CVE-2024-8926) (nielsdos) ++ . Fixed bug GHSA-94p6-54jq-9mwp (cgi.force_redirect configuration is ++ bypassable due to the environment variable collision). (CVE-2024-8927) ++ (nielsdos) ++ ++- FPM: ++ . Fixed bug GHSA-865w-9rf3-2wh5 (Logs from childrens may be altered). ++ (CVE-2024-9026) (Jakub Zelenka) ++ ++- SAPI: ++ . Fixed bug GHSA-9pqp-7h25-4f32 (Erroneous parsing of multipart form data). ++ (CVE-2024-8925) (Arnaud) ++ + Backported from 8.1.29 + + - CGI: +-- +2.46.1 + +From bc574c256596abc4966e7f0e3e0913839092151e Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 26 Sep 2024 15:48:11 +0200 +Subject: [PATCH 10/11] adapt GHSA-865w-9rf3-2wh5 test for 7.x + +--- + sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt | 4 ++-- + sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt | 4 ++-- + sapi/fpm/tests/tester.inc | 4 ++-- + 3 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt +index 52826320080..bdd61782bfa 100644 +--- a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt ++++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-end.phpt +@@ -30,8 +30,8 @@ $tester = new FPM\Tester($cfg, $code); + $tester->start(); + $tester->expectLogStartNotices(); + $tester->request()->expectEmptyBody(); +-$tester->expectLogLine(str_repeat('a', 1013) . "Quarkslab", decorated: false); +-$tester->expectLogLine("Quarkslab", decorated: false); ++$tester->expectLogLine(str_repeat('a', 1013) . "Quarkslab", true, false); ++$tester->expectLogLine("Quarkslab", true, false); + $tester->terminate(); + $tester->expectLogTerminatingNotices(); + $tester->close(); +diff --git a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt +index 34905938553..f3461e4a0c8 100644 +--- a/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt ++++ b/sapi/fpm/tests/log-bwp-msg-flush-split-sep-pos-start.phpt +@@ -30,8 +30,8 @@ $tester = new FPM\Tester($cfg, $code); + $tester->start(); + $tester->expectLogStartNotices(); + $tester->request()->expectEmptyBody(); +-$tester->expectLogLine(str_repeat('a', 1009) . "Quarkslab", decorated: false); +-$tester->expectLogLine("Quarkslab", decorated: false); ++$tester->expectLogLine(str_repeat('a', 1009) . "Quarkslab", true, false); ++$tester->expectLogLine("Quarkslab", true, false); + $tester->terminate(); + $tester->expectLogTerminatingNotices(); + $tester->close(); +diff --git a/sapi/fpm/tests/tester.inc b/sapi/fpm/tests/tester.inc +index 7868afc4ac1..fe5f0c2fde7 100644 +--- a/sapi/fpm/tests/tester.inc ++++ b/sapi/fpm/tests/tester.inc +@@ -1315,7 +1315,7 @@ class Tester + * @param string $message + * @return bool + */ +- public function expectLogLine(string $message, bool $is_stderr = true) ++ public function expectLogLine(string $message, bool $is_stderr = true, bool $decorated = true) + { + $messageLen = strlen($message); + $limit = $messageLen > 1024 ? $messageLen + 16 : 1024; +@@ -1325,7 +1325,7 @@ class Tester + $this->message("LOG LINE: " . ($logLines[0] ?? '')); + } + +- return $this->logTool->checkWrappedMessage($logLines, false, true, $is_stderr); ++ return $this->logTool->checkWrappedMessage($logLines, false, $decorated, $is_stderr); + } + + /** +-- +2.46.1 + diff --git a/php-cve-2025-1217.patch b/php-cve-2025-1217.patch new file mode 100644 index 0000000..23d8b04 --- /dev/null +++ b/php-cve-2025-1217.patch @@ -0,0 +1,917 @@ +From bf4a8df2b3972118c87b05450e9062d3926f6be8 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Tue, 31 Dec 2024 18:57:02 +0100 +Subject: [PATCH 01/11] Fix GHSA-ghsa-v8xr-gpvj-cx9g: http header folding +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This adds HTTP header folding support for HTTP wrapper response +headers. + +Reviewed-by: Tim Düsterhus <tim@tideways-gmbh.com> +(cherry picked from commit d20b4c97a9f883b62b65b82d939c5af9a2028ef1) +(cherry picked from commit 4fec08542748c25573063ffc53ea89cd5de1edf0) +--- + ext/openssl/tests/ServerClientTestCase.inc | 65 +++- + ext/standard/http_fopen_wrapper.c | 347 ++++++++++++------ + .../tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt | 49 +++ + .../tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt | 51 +++ + .../tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt | 49 +++ + .../tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt | 48 +++ + .../tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt | 48 +++ + .../tests/http/http_response_header_05.phpt | 35 -- + 8 files changed, 537 insertions(+), 155 deletions(-) + create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt + create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt + create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt + create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt + create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt + delete mode 100644 ext/standard/tests/http/http_response_header_05.phpt + +diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc +index 753366df6f4..c74da444102 100644 +--- a/ext/openssl/tests/ServerClientTestCase.inc ++++ b/ext/openssl/tests/ServerClientTestCase.inc +@@ -4,14 +4,19 @@ const WORKER_ARGV_VALUE = 'RUN_WORKER'; + + const WORKER_DEFAULT_NAME = 'server'; + +-function phpt_notify($worker = WORKER_DEFAULT_NAME) ++function phpt_notify(string $worker = WORKER_DEFAULT_NAME, string $message = ""): void + { +- ServerClientTestCase::getInstance()->notify($worker); ++ ServerClientTestCase::getInstance()->notify($worker, $message); + } + +-function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null) ++function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null): ?string + { +- ServerClientTestCase::getInstance()->wait($worker, $timeout); ++ return ServerClientTestCase::getInstance()->wait($worker, $timeout); ++} ++ ++function phpt_notify_server_start($server): void ++{ ++ ServerClientTestCase::getInstance()->notify_server_start($server); + } + + function phpt_has_sslv3() { +@@ -119,43 +124,73 @@ class ServerClientTestCase + eval($code); + } + +- public function run($masterCode, $workerCode) ++ /** ++ * Run client and all workers ++ * ++ * @param string $clientCode The client PHP code ++ * @param string|array $workerCode ++ * @param bool $ephemeral Select whether automatic port selection and automatic awaiting is used ++ * @return void ++ * @throws Exception ++ */ ++ public function run(string $clientCode, $workerCode, bool $ephemeral = true): void + { + if (!is_array($workerCode)) { + $workerCode = [WORKER_DEFAULT_NAME => $workerCode]; + } +- foreach ($workerCode as $worker => $code) { ++ reset($workerCode); ++ $code = current($workerCode); ++ $worker = key($workerCode); ++ while ($worker != null) { + $this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code)); ++ $code = next($workerCode); ++ if ($ephemeral) { ++ $addr = trim($this->wait($worker)); ++ if (empty($addr)) { ++ throw new \Exception("Failed server start"); ++ } ++ if ($code === false) { ++ $clientCode = preg_replace('/{{\s*ADDR\s*}}/', $addr, $clientCode); ++ } else { ++ $code = preg_replace('/{{\s*ADDR\s*}}/', $addr, $code); ++ } ++ } ++ $worker = key($workerCode); + } +- eval($this->stripPhpTagsFromCode($masterCode)); ++ ++ eval($this->stripPhpTagsFromCode($clientCode)); + foreach ($workerCode as $worker => $code) { + $this->cleanupWorkerProcess($worker); + } + } + +- public function wait($worker, $timeout = null) ++ public function wait($worker, $timeout = null): ?string + { + $handle = $this->isWorker ? STDIN : $this->workerStdOut[$worker]; + if ($timeout === null) { +- fgets($handle); +- return true; ++ return fgets($handle); + } + + stream_set_blocking($handle, false); + $read = [$handle]; + $result = stream_select($read, $write, $except, $timeout); + if (!$result) { +- return false; ++ return null; + } + +- fgets($handle); ++ $result = fgets($handle); + stream_set_blocking($handle, true); +- return true; ++ return $result; ++ } ++ ++ public function notify(string $worker, string $message = ""): void ++ { ++ fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "$message\n"); + } + +- public function notify($worker) ++ public function notify_server_start($server): void + { +- fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n"); ++ echo stream_socket_get_name($server, false) . "\n"; + } + } + +diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c +index aeeb438f0f9..08386cfafcd 100644 +--- a/ext/standard/http_fopen_wrapper.c ++++ b/ext/standard/http_fopen_wrapper.c +@@ -116,6 +116,172 @@ static zend_bool check_has_header(const char *headers, const char *header) { + return 0; + } + ++typedef struct _php_stream_http_response_header_info { ++ php_stream_filter *transfer_encoding; ++ size_t file_size; ++ zend_bool follow_location; ++ char location[HTTP_HEADER_BLOCK_SIZE]; ++} php_stream_http_response_header_info; ++ ++static void php_stream_http_response_header_info_init( ++ php_stream_http_response_header_info *header_info) ++{ ++ header_info->transfer_encoding = NULL; ++ header_info->file_size = 0; ++ header_info->follow_location = 1; ++ header_info->location[0] = '\0'; ++} ++ ++/* Trim white spaces from response header line and update its length */ ++static zend_bool php_stream_http_response_header_trim(char *http_header_line, ++ size_t *http_header_line_length) ++{ ++ char *http_header_line_end = http_header_line + *http_header_line_length - 1; ++ while (http_header_line_end >= http_header_line && ++ (*http_header_line_end == '\n' || *http_header_line_end == '\r')) { ++ http_header_line_end--; ++ } ++ ++ /* The primary definition of an HTTP header in RFC 7230 states: ++ * > Each header field consists of a case-insensitive field name followed ++ * > by a colon (":"), optional leading whitespace, the field value, and ++ * > optional trailing whitespace. */ ++ ++ /* Strip trailing whitespace */ ++ zend_bool space_trim = (*http_header_line_end == ' ' || *http_header_line_end == '\t'); ++ if (space_trim) { ++ do { ++ http_header_line_end--; ++ } while (http_header_line_end >= http_header_line && ++ (*http_header_line_end == ' ' || *http_header_line_end == '\t')); ++ } ++ http_header_line_end++; ++ *http_header_line_end = '\0'; ++ *http_header_line_length = http_header_line_end - http_header_line; ++ ++ return space_trim; ++} ++ ++/* Process folding headers of the current line and if there are none, parse last full response ++ * header line. It returns NULL if the last header is finished, otherwise it returns updated ++ * last header line. */ ++static zend_string *php_stream_http_response_headers_parse(php_stream *stream, ++ php_stream_context *context, int options, zend_string *last_header_line_str, ++ char *header_line, size_t *header_line_length, int response_code, ++ zval *response_header, php_stream_http_response_header_info *header_info) ++{ ++ char *last_header_line = ZSTR_VAL(last_header_line_str); ++ size_t last_header_line_length = ZSTR_LEN(last_header_line_str); ++ char *last_header_line_end = ZSTR_VAL(last_header_line_str) + ZSTR_LEN(last_header_line_str) - 1; ++ ++ /* Process non empty header line. */ ++ if (header_line && (*header_line != '\n' && *header_line != '\r')) { ++ /* Removing trailing white spaces. */ ++ if (php_stream_http_response_header_trim(header_line, header_line_length) && ++ *header_line_length == 0) { ++ /* Only spaces so treat as an empty folding header. */ ++ return last_header_line_str; ++ } ++ ++ /* Process folding headers if starting with a space or a tab. */ ++ if (header_line && (*header_line == ' ' || *header_line == '\t')) { ++ char *http_folded_header_line = header_line; ++ size_t http_folded_header_line_length = *header_line_length; ++ /* Remove the leading white spaces. */ ++ while (*http_folded_header_line == ' ' || *http_folded_header_line == '\t') { ++ http_folded_header_line++; ++ http_folded_header_line_length--; ++ } ++ /* It has to have some characters because it would get returned after the call ++ * php_stream_http_response_header_trim above. */ ++ ZEND_ASSERT(http_folded_header_line_length > 0); ++ /* Concatenate last header line, space and current header line. */ ++ zend_string *extended_header_str = zend_string_alloc(last_header_line_length + 1 + http_folded_header_line_length, 0); ++ memcpy(ZSTR_VAL(extended_header_str), last_header_line, last_header_line_length); ++ ZSTR_VAL(extended_header_str)[last_header_line_length] = ' '; ++ memcpy(ZSTR_VAL(extended_header_str) + last_header_line_length + 1, http_folded_header_line, http_folded_header_line_length); ++ ZSTR_VAL(extended_header_str)[ZSTR_LEN(extended_header_str)] = 0; ++ zend_string_efree(last_header_line_str); ++ last_header_line_str = extended_header_str; ++ /* Return new header line. */ ++ return last_header_line_str; ++ } ++ } ++ ++ /* Find header separator position. */ ++ char *last_header_value = memchr(last_header_line, ':', last_header_line_length); ++ if (last_header_value) { ++ last_header_value++; /* Skip ':'. */ ++ ++ /* Strip leading whitespace. */ ++ while (last_header_value < last_header_line_end ++ && (*last_header_value == ' ' || *last_header_value == '\t')) { ++ last_header_value++; ++ } ++ } else { ++ /* There is no colon. Set the value to the end of the header line, which is effectively ++ * an empty string. */ ++ last_header_value = last_header_line_end; ++ } ++ ++ zend_bool store_header = 1; ++ zval *tmpzval = NULL; ++ ++ if (!strncasecmp(last_header_line, "Location:", sizeof("Location:")-1)) { ++ /* Check if the location should be followed. */ ++ if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) { ++ header_info->follow_location = zval_is_true(tmpzval); ++ } else if (!((response_code >= 300 && response_code < 304) ++ || 307 == response_code || 308 == response_code)) { ++ /* The redirection should not be automatic if follow_location is not set and ++ * response_code not in (300, 301, 302, 303 and 307) ++ * see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1 ++ * RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */ ++ header_info->follow_location = 0; ++ } ++ strlcpy(header_info->location, last_header_value, sizeof(header_info->location)); ++ } else if (!strncasecmp(last_header_line, "Content-Type:", sizeof("Content-Type:")-1)) { ++ php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, last_header_value, 0); ++ } else if (!strncasecmp(last_header_line, "Content-Length:", sizeof("Content-Length:")-1)) { ++ header_info->file_size = atoi(last_header_value); ++ php_stream_notify_file_size(context, header_info->file_size, last_header_line, 0); ++ } else if ( ++ !strncasecmp(last_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding:")-1) ++ && !strncasecmp(last_header_value, "Chunked", sizeof("Chunked")-1) ++ ) { ++ /* Create filter to decode response body. */ ++ if (!(options & STREAM_ONLY_GET_HEADERS)) { ++ zend_long decode = 1; ++ ++ if (context && (tmpzval = php_stream_context_get_option(context, "http", "auto_decode")) != NULL) { ++ decode = zend_is_true(tmpzval); ++ } ++ if (decode) { ++ if (header_info->transfer_encoding != NULL) { ++ /* Prevent a memory leak in case there are more transfer-encoding headers. */ ++ php_stream_filter_free(header_info->transfer_encoding); ++ } ++ header_info->transfer_encoding = php_stream_filter_create( ++ "dechunk", NULL, php_stream_is_persistent(stream)); ++ if (header_info->transfer_encoding != NULL) { ++ /* Do not store transfer-encoding header. */ ++ store_header = 0; ++ } ++ } ++ } ++ } ++ ++ if (store_header) { ++ zval http_header; ++ ZVAL_NEW_STR(&http_header, last_header_line_str); ++ zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header); ++ } else { ++ zend_string_efree(last_header_line_str); ++ } ++ ++ return NULL; ++} ++ + static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, + const char *path, const char *mode, int options, zend_string **opened_path, + php_stream_context *context, int redirect_max, int flags, +@@ -128,11 +294,12 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, + zend_string *tmp = NULL; + char *ua_str = NULL; + zval *ua_zval = NULL, *tmpzval = NULL, ssl_proxy_peer_name; +- char location[HTTP_HEADER_BLOCK_SIZE]; + int reqok = 0; + char *http_header_line = NULL; ++ zend_string *last_header_line_str = NULL; ++ php_stream_http_response_header_info header_info; + char tmp_line[128]; +- size_t chunk_size = 0, file_size = 0; ++ size_t chunk_size = 0; + int eol_detect = 0; + char *transport_string; + zend_string *errstr = NULL; +@@ -143,8 +310,6 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, + char *user_headers = NULL; + int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT) != 0); + int redirected = ((flags & HTTP_WRAPPER_REDIRECTED) != 0); +- zend_bool follow_location = 1; +- php_stream_filter *transfer_encoding = NULL; + int response_code; + smart_str req_buf = {0}; + zend_bool custom_request_method; +@@ -657,8 +822,6 @@ finish: + /* send it */ + php_stream_write(stream, ZSTR_VAL(req_buf.s), ZSTR_LEN(req_buf.s)); + +- location[0] = '\0'; +- + if (Z_ISUNDEF_P(response_header)) { + array_init(response_header); + } +@@ -736,125 +899,101 @@ finish: + } + } + +- /* read past HTTP headers */ ++ php_stream_http_response_header_info_init(&header_info); + ++ /* read past HTTP headers */ + while (!php_stream_eof(stream)) { + size_t http_header_line_length; + + if (http_header_line != NULL) { + efree(http_header_line); + } +- if ((http_header_line = php_stream_get_line(stream, NULL, 0, &http_header_line_length)) && *http_header_line != '\n' && *http_header_line != '\r') { +- char *e = http_header_line + http_header_line_length - 1; +- char *http_header_value; +- +- while (e >= http_header_line && (*e == '\n' || *e == '\r')) { +- e--; +- } +- +- /* The primary definition of an HTTP header in RFC 7230 states: +- * > Each header field consists of a case-insensitive field name followed +- * > by a colon (":"), optional leading whitespace, the field value, and +- * > optional trailing whitespace. */ +- +- /* Strip trailing whitespace */ +- while (e >= http_header_line && (*e == ' ' || *e == '\t')) { +- e--; +- } +- +- /* Terminate header line */ +- e++; +- *e = '\0'; +- http_header_line_length = e - http_header_line; +- +- http_header_value = memchr(http_header_line, ':', http_header_line_length); +- if (http_header_value) { +- http_header_value++; /* Skip ':' */ +- +- /* Strip leading whitespace */ +- while (http_header_value < e +- && (*http_header_value == ' ' || *http_header_value == '\t')) { +- http_header_value++; ++ if ((http_header_line = php_stream_get_line(stream, NULL, 0, &http_header_line_length))) { ++ zend_bool last_line; ++ if (*http_header_line == '\r') { ++ if (http_header_line[1] != '\n') { ++ php_stream_close(stream); ++ stream = NULL; ++ php_stream_wrapper_log_error(wrapper, options, ++ "HTTP invalid header name (cannot start with CR character)!"); ++ goto out; + } ++ last_line = 1; ++ } else if (*http_header_line == '\n') { ++ last_line = 1; + } else { +- /* There is no colon. Set the value to the end of the header line, which is +- * effectively an empty string. */ +- http_header_value = e; ++ last_line = 0; + } +- +- if (!strncasecmp(http_header_line, "Location:", sizeof("Location:")-1)) { +- if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) { +- follow_location = zval_is_true(tmpzval); +- } else if (!((response_code >= 300 && response_code < 304) +- || 307 == response_code || 308 == response_code)) { +- /* we shouldn't redirect automatically +- if follow_location isn't set and response_code not in (300, 301, 302, 303 and 307) +- see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1 +- RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */ +- follow_location = 0; ++ ++ if (last_header_line_str != NULL) { ++ /* Parse last header line. */ ++ last_header_line_str = php_stream_http_response_headers_parse(stream, context, ++ options, last_header_line_str, http_header_line, &http_header_line_length, ++ response_code, response_header, &header_info); ++ if (last_header_line_str != NULL) { ++ /* Folding header present so continue. */ ++ continue; + } +- strlcpy(location, http_header_value, sizeof(location)); +- } else if (!strncasecmp(http_header_line, "Content-Type:", sizeof("Content-Type:")-1)) { +- php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, http_header_value, 0); +- } else if (!strncasecmp(http_header_line, "Content-Length:", sizeof("Content-Length:")-1)) { +- file_size = atoi(http_header_value); +- php_stream_notify_file_size(context, file_size, http_header_line, 0); +- } else if ( +- !strncasecmp(http_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding:")-1) +- && !strncasecmp(http_header_value, "Chunked", sizeof("Chunked")-1) +- ) { +- +- /* create filter to decode response body */ +- if (!(options & STREAM_ONLY_GET_HEADERS)) { +- zend_long decode = 1; +- +- if (context && (tmpzval = php_stream_context_get_option(context, "http", "auto_decode")) != NULL) { +- decode = zend_is_true(tmpzval); +- } +- if (decode) { +- transfer_encoding = php_stream_filter_create("dechunk", NULL, php_stream_is_persistent(stream)); +- if (transfer_encoding) { +- /* don't store transfer-encodeing header */ +- continue; +- } +- } ++ } else if (!last_line) { ++ /* The first line cannot start with spaces. */ ++ if (*http_header_line == ' ' || *http_header_line == '\t') { ++ php_stream_close(stream); ++ stream = NULL; ++ php_stream_wrapper_log_error(wrapper, options, ++ "HTTP invalid response format (folding header at the start)!"); ++ goto out; + } ++ /* Trim the first line if it is not the last line. */ ++ php_stream_http_response_header_trim(http_header_line, &http_header_line_length); + } +- +- { +- zval http_header; +- ZVAL_STRINGL(&http_header, http_header_line, http_header_line_length); +- zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header); ++ if (last_line) { ++ /* For the last line the last header line must be NULL. */ ++ ZEND_ASSERT(last_header_line_str == NULL); ++ break; + } ++ /* Save current line as the last line so it gets parsed in the next round. */ ++ last_header_line_str = zend_string_init(http_header_line, http_header_line_length, 0); + } else { + break; + } + } + +- if (!reqok || (location[0] != '\0' && follow_location)) { +- if (!follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) { ++ /* If the stream was closed early, we still want to process the last line to keep BC. */ ++ if (last_header_line_str != NULL) { ++ php_stream_http_response_headers_parse(stream, context, options, last_header_line_str, ++ NULL, NULL, response_code, response_header, &header_info); ++ } ++ ++ if (!reqok || (header_info.location[0] != '\0' && header_info.follow_location)) { ++ if (!header_info.follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) { + goto out; + } + +- if (location[0] != '\0') +- php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, location, 0); ++ if (header_info.location[0] != '\0') ++ php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, header_info.location, 0); + + php_stream_close(stream); + stream = NULL; + +- if (location[0] != '\0') { ++ if (header_info.transfer_encoding) { ++ php_stream_filter_free(header_info.transfer_encoding); ++ header_info.transfer_encoding = NULL; ++ } ++ ++ if (header_info.location[0] != '\0') { + + char new_path[HTTP_HEADER_BLOCK_SIZE]; + char loc_path[HTTP_HEADER_BLOCK_SIZE]; + + *new_path='\0'; +- if (strlen(location)<8 || (strncasecmp(location, "http://", sizeof("http://")-1) && +- strncasecmp(location, "https://", sizeof("https://")-1) && +- strncasecmp(location, "ftp://", sizeof("ftp://")-1) && +- strncasecmp(location, "ftps://", sizeof("ftps://")-1))) ++ if (strlen(header_info.location) < 8 || ++ (strncasecmp(header_info.location, "http://", sizeof("http://")-1) && ++ strncasecmp(header_info.location, "https://", sizeof("https://")-1) && ++ strncasecmp(header_info.location, "ftp://", sizeof("ftp://")-1) && ++ strncasecmp(header_info.location, "ftps://", sizeof("ftps://")-1))) + { +- if (*location != '/') { +- if (*(location+1) != '\0' && resource->path) { ++ if (*header_info.location != '/') { ++ if (*(header_info.location+1) != '\0' && resource->path) { + char *s = strrchr(ZSTR_VAL(resource->path), '/'); + if (!s) { + s = ZSTR_VAL(resource->path); +@@ -870,15 +1009,17 @@ finish: + if (resource->path && + ZSTR_VAL(resource->path)[0] == '/' && + ZSTR_VAL(resource->path)[1] == '\0') { +- snprintf(loc_path, sizeof(loc_path) - 1, "%s%s", ZSTR_VAL(resource->path), location); ++ snprintf(loc_path, sizeof(loc_path) - 1, "%s%s", ++ ZSTR_VAL(resource->path), header_info.location); + } else { +- snprintf(loc_path, sizeof(loc_path) - 1, "%s/%s", ZSTR_VAL(resource->path), location); ++ snprintf(loc_path, sizeof(loc_path) - 1, "%s/%s", ++ ZSTR_VAL(resource->path), header_info.location); + } + } else { +- snprintf(loc_path, sizeof(loc_path) - 1, "/%s", location); ++ snprintf(loc_path, sizeof(loc_path) - 1, "/%s", header_info.location); + } + } else { +- strlcpy(loc_path, location, sizeof(loc_path)); ++ strlcpy(loc_path, header_info.location, sizeof(loc_path)); + } + if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) { + snprintf(new_path, sizeof(new_path) - 1, "%s://%s:%d%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), resource->port, loc_path); +@@ -886,7 +1027,7 @@ finish: + snprintf(new_path, sizeof(new_path) - 1, "%s://%s%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), loc_path); + } + } else { +- strlcpy(new_path, location, sizeof(new_path)); ++ strlcpy(new_path, header_info.location, sizeof(new_path)); + } + + php_url_free(resource); +@@ -939,7 +1080,7 @@ out: + if (header_init) { + ZVAL_COPY(&stream->wrapperdata, response_header); + } +- php_stream_notify_progress_init(context, 0, file_size); ++ php_stream_notify_progress_init(context, 0, header_info.file_size); + + /* Restore original chunk size now that we're done with headers */ + if (options & STREAM_WILL_CAST) +@@ -955,12 +1096,8 @@ out: + /* restore mode */ + strlcpy(stream->mode, mode, sizeof(stream->mode)); + +- if (transfer_encoding) { +- php_stream_filter_append(&stream->readfilters, transfer_encoding); +- } +- } else { +- if (transfer_encoding) { +- php_stream_filter_free(transfer_encoding); ++ if (header_info.transfer_encoding) { ++ php_stream_filter_append(&stream->readfilters, header_info.transfer_encoding); + } + } + +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt +new file mode 100644 +index 00000000000..64904bfcd1d +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt +@@ -0,0 +1,49 @@ ++--TEST-- ++GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (single) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\n charset=utf-8\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++Found the mime-type: text/html; charset=utf-8 ++string(4) "body" ++array(2) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++ [1]=> ++ string(38) "Content-Type: text/html; charset=utf-8" ++} +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt +new file mode 100644 +index 00000000000..a6d9d00fd58 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt +@@ -0,0 +1,51 @@ ++--TEST-- ++GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (multiple) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\nCustom-Header: somevalue;\r\n param1=value1; \r\n param2=value2\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++Found the mime-type: text/html; ++string(4) "body" ++array(3) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++ [1]=> ++ string(24) "Content-Type: text/html;" ++ [2]=> ++ string(54) "Custom-Header: somevalue; param1=value1; param2=value2" ++} +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt +new file mode 100644 +index 00000000000..4eff7fc63f3 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt +@@ -0,0 +1,49 @@ ++--TEST-- ++GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (empty) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\n \r\n charset=utf-8\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++Found the mime-type: text/html; charset=utf-8 ++string(4) "body" ++array(2) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++ [1]=> ++ string(38) "Content-Type: text/html; charset=utf-8" ++} +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt +new file mode 100644 +index 00000000000..71aed2fa2e8 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt +@@ -0,0 +1,48 @@ ++--TEST-- ++GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (first line) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\n Content-Type: text/html;\r\n \r\n charset=utf-8\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx)); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++ ++Warning: file_get_contents(http://127.0.0.1:%d): failed to open stream: HTTP invalid response format (folding header at the start)! in %s ++bool(false) ++array(1) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++} +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt +new file mode 100644 +index 00000000000..49d845d84b4 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt +@@ -0,0 +1,48 @@ ++--TEST-- ++GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (CR before header name) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\n\rIgnored: ignored\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx)); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++ ++Warning: file_get_contents(http://127.0.0.1:%d): failed to open stream: HTTP invalid header name (cannot start with CR character)! in %s ++bool(false) ++array(1) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++} +diff --git a/ext/standard/tests/http/http_response_header_05.phpt b/ext/standard/tests/http/http_response_header_05.phpt +deleted file mode 100644 +index dbdd7b8b1a0..00000000000 +--- a/ext/standard/tests/http/http_response_header_05.phpt ++++ /dev/null +@@ -1,35 +0,0 @@ +---TEST-- +-$http_reponse_header (whitespace-only "header") +---SKIPIF-- +-<?php require 'server.inc'; http_server_skipif('tcp://127.0.0.1:22350'); ?> +---INI-- +-allow_url_fopen=1 +---FILE-- +-<?php +-require 'server.inc'; +- +-$responses = array( +- "data://text/plain,HTTP/1.0 200 Ok\r\n \r\n\r\nBody", +-); +- +-$pid = http_server("tcp://127.0.0.1:22350", $responses, $output); +- +-function test() { +- $f = file_get_contents('http://127.0.0.1:22350/'); +- var_dump($f); +- var_dump($http_response_header); +-} +-test(); +- +-http_server_kill($pid); +-?> +-==DONE== +---EXPECT-- +-string(4) "Body" +-array(2) { +- [0]=> +- string(15) "HTTP/1.0 200 Ok" +- [1]=> +- string(0) "" +-} +-==DONE== +-- +2.48.1 + diff --git a/php-cve-2025-1219.patch b/php-cve-2025-1219.patch new file mode 100644 index 0000000..5d164b6 --- /dev/null +++ b/php-cve-2025-1219.patch @@ -0,0 +1,1906 @@ +From 8a2195d2c64e0323d05656238c5c72414e8ad340 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Sat, 29 Apr 2023 21:07:50 +0200 +Subject: [PATCH 05/11] Fix GH-11160: Few tests failed building with new libxml + 2.11.0 + +It's possible to categorise the failures into 2 categories: + - Changed error message. In this case we either duplicate the test and + modify the error message. Or if the change in error message is + small, we use the EXPECTF matchers to make the test compatible with both + old and new versions of libxml2. + - Missing warnings. This is caused by a change in libxml2 where the + parser started using SAX APIs internally [1]. In this case the + error_type passed to php_libxml_internal_error_handler() changed from + PHP_LIBXML_ERROR to PHP_LIBXML_CTX_WARNING because it internally + started to use the SAX handlers instead of the generic handlers. + However, for the SAX handlers the current input stack is empty, so + nothing is actually printed. I fixed this by falling back to a + regular warning without a filename & line number reference, which + mimicks the old behaviour. Furthermore, this change now also shows + an additional warning in a test which was previously hidden. + +[1] https://gitlab.gnome.org/GNOME/libxml2/-/commit/9a82b94a94bd310db426edd453b0f38c6c8f69f5 + +Closes GH-11162. + +(cherry picked from commit 7c0dfc5cf58d3c445b935fa14ea8f5f13568c419) +(cherry picked from commit 78ae0886bd1a3e42c53c9ba65764b6e6357640b5) +--- + .../DOMDocument_loadXML_error2_gte2_11.phpt | 34 +++ + ...> DOMDocument_loadXML_error2_pre2_11.phpt} | 7 +- + .../DOMDocument_load_error2_gte2_11.phpt | 34 +++ + ...t => DOMDocument_load_error2_pre2_11.phpt} | 7 +- + ext/libxml/libxml.c | 2 + + ext/libxml/tests/bug61367-read_2.phpt | 2 +- + .../tests/libxml_disable_entity_loader_2.phpt | 2 +- + ...xml_set_external_entity_loader_error1.phpt | 2 + + ...set_external_entity_loader_variation2.phpt | 2 + + ext/openssl/tests/ServerClientTestCase.inc | 65 ++---- + .../tests/http/ServerClientTestCase.inc | 199 ++++++++++++++++++ + .../tests/http/ghsa-52jp-hrpf-2jff-001.phpt | 2 +- + .../tests/http/ghsa-52jp-hrpf-2jff-002.phpt | 2 +- + .../tests/http/ghsa-hgf5-96fm-v528-001.phpt | 2 +- + .../tests/http/ghsa-hgf5-96fm-v528-002.phpt | 2 +- + .../tests/http/ghsa-hgf5-96fm-v528-003.phpt | 2 +- + .../tests/http/ghsa-pcmh-g36c-qc44-001.phpt | 2 +- + .../tests/http/ghsa-pcmh-g36c-qc44-002.phpt | 2 +- + .../tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt | 2 +- + .../tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt | 2 +- + .../tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt | 2 +- + .../tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt | 2 +- + .../tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt | 2 +- + ext/xml/tests/bug26614_libxml_gte2_11.phpt | 95 +++++++++ + ...bxml.phpt => bug26614_libxml_pre2_11.phpt} | 1 + + 25 files changed, 408 insertions(+), 68 deletions(-) + create mode 100644 ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt + rename ext/dom/tests/{DOMDocument_loadXML_error2.phpt => DOMDocument_loadXML_error2_pre2_11.phpt} (89%) + create mode 100644 ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt + rename ext/dom/tests/{DOMDocument_load_error2.phpt => DOMDocument_load_error2_pre2_11.phpt} (89%) + create mode 100644 ext/standard/tests/http/ServerClientTestCase.inc + create mode 100644 ext/xml/tests/bug26614_libxml_gte2_11.phpt + rename ext/xml/tests/{bug26614_libxml.phpt => bug26614_libxml_pre2_11.phpt} (96%) + +diff --git a/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt +new file mode 100644 +index 00000000000..ff5ceb3fbed +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_loadXML_error2_gte2_11.phpt +@@ -0,0 +1,34 @@ ++--TEST-- ++Test DOMDocument::loadXML() detects not-well formed XML ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21100) die('skip libxml2 test variant for version >= 2.11'); ++?> ++--DESCRIPTION-- ++This test verifies the method detects attributes values not closed between " or ' ++Environment variables used in the test: ++- XML_FILE: the xml file to load ++- LOAD_OPTIONS: the second parameter to pass to the method ++- EXPECTED_RESULT: the expected result ++--CREDITS-- ++Antonio Diaz Ruiz <dejalatele@gmail.com> ++--INI-- ++assert.bail=true ++--EXTENSIONS-- ++dom ++--ENV-- ++XML_FILE=/not_well_formed2.xml ++LOAD_OPTIONS=0 ++EXPECTED_RESULT=0 ++--FILE_EXTERNAL-- ++domdocumentloadxml_test_method.inc ++--EXPECTF-- ++Warning: DOMDocument::loadXML(): AttValue: " or ' expected in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): internal error: xmlParseStartTag: problem parsing attributes in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): Couldn't find end of Start Tag book line 4 in Entity, line: 4 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): Opening and ending tag mismatch: books line 3 and book in Entity, line: 7 in %s on line %d ++ ++Warning: DOMDocument::loadXML(): Extra content at the end of the document in Entity, line: 8 in %s on line %d +diff --git a/ext/dom/tests/DOMDocument_loadXML_error2.phpt b/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt +similarity index 89% +rename from ext/dom/tests/DOMDocument_loadXML_error2.phpt +rename to ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt +index 6d56a317ed7..7e10771fdb7 100644 +--- a/ext/dom/tests/DOMDocument_loadXML_error2.phpt ++++ b/ext/dom/tests/DOMDocument_loadXML_error2_pre2_11.phpt +@@ -1,5 +1,10 @@ + --TEST-- + Test DOMDocument::loadXML() detects not-well formed XML ++--SKIPIF-- ++<?php ++include('skipif.inc'); ++if (LIBXML_VERSION >= 21100) die('skip libxml2 test variant for version < 2.11'); ++?> + --DESCRIPTION-- + This test verifies the method detects attributes values not closed between " or ' + Environment variables used in the test: +@@ -10,8 +15,6 @@ Environment variables used in the test: + Antonio Diaz Ruiz <dejalatele@gmail.com> + --INI-- + assert.bail=true +---SKIPIF-- +-<?php include('skipif.inc'); ?> + --ENV-- + XML_FILE=/not_well_formed2.xml + LOAD_OPTIONS=0 +diff --git a/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt b/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt +new file mode 100644 +index 00000000000..32b6bf16114 +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_load_error2_gte2_11.phpt +@@ -0,0 +1,34 @@ ++--TEST-- ++Test DOMDocument::load() detects not-well formed ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21100) die('skip libxml2 test variant for version >= 2.11'); ++?> ++--DESCRIPTION-- ++This test verifies the method detects attributes values not closed between " or ' ++Environment variables used in the test: ++- XML_FILE: the xml file to load ++- LOAD_OPTIONS: the second parameter to pass to the method ++- EXPECTED_RESULT: the expected result ++--CREDITS-- ++Antonio Diaz Ruiz <dejalatele@gmail.com> ++--INI-- ++assert.bail=true ++--EXTENSIONS-- ++dom ++--ENV-- ++XML_FILE=/not_well_formed2.xml ++LOAD_OPTIONS=0 ++EXPECTED_RESULT=0 ++--FILE_EXTERNAL-- ++domdocumentload_test_method.inc ++--EXPECTF-- ++Warning: DOMDocument::load(): AttValue: " or ' expected in %s on line %d ++ ++Warning: DOMDocument::load(): internal error: xmlParseStartTag: problem parsing attributes in %s on line %d ++ ++Warning: DOMDocument::load(): Couldn't find end of Start Tag book line 4 in %s on line %d ++ ++Warning: DOMDocument::load(): Opening and ending tag mismatch: books line 3 and book in %s on line %d ++ ++Warning: DOMDocument::load(): Extra content at the end of the document in %s on line %d +diff --git a/ext/dom/tests/DOMDocument_load_error2.phpt b/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt +similarity index 89% +rename from ext/dom/tests/DOMDocument_load_error2.phpt +rename to ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt +index f450cf16545..74b20c171e0 100644 +--- a/ext/dom/tests/DOMDocument_load_error2.phpt ++++ b/ext/dom/tests/DOMDocument_load_error2_pre2_11.phpt +@@ -1,5 +1,10 @@ + --TEST-- + Test DOMDocument::load() detects not-well formed XML ++--SKIPIF-- ++<?php ++include('skipif.inc'); ++if (LIBXML_VERSION >= 21100) die('skip libxml2 test variant for version < 2.11'); ++?> + --DESCRIPTION-- + This test verifies the method detects attributes values not closed between " or ' + Environment variables used in the test: +@@ -10,8 +15,6 @@ Environment variables used in the test: + Antonio Diaz Ruiz <dejalatele@gmail.com> + --INI-- + assert.bail=true +---SKIPIF-- +-<?php include('skipif.inc'); ?> + --ENV-- + XML_FILE=/not_well_formed2.xml + LOAD_OPTIONS=0 +diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c +index d343135b98d..5d9c23e0d7e 100644 +--- a/ext/libxml/libxml.c ++++ b/ext/libxml/libxml.c +@@ -574,6 +574,8 @@ static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg) + } else { + php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line); + } ++ } else { ++ php_error_docref(NULL, E_WARNING, "%s", msg); + } + } + +diff --git a/ext/libxml/tests/bug61367-read_2.phpt b/ext/libxml/tests/bug61367-read_2.phpt +index 8cc0b50144c..12743adab1c 100644 +--- a/ext/libxml/tests/bug61367-read_2.phpt ++++ b/ext/libxml/tests/bug61367-read_2.phpt +@@ -55,6 +55,6 @@ bool(true) + int(4) + bool(true) + +-Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "file:///%s/test_bug_61367-read/bad" in %s on line %d ++Warning: DOMDocument::loadXML(): %Sfailed to load external entity "file:///%s/test_bug_61367-read/bad" in %s on line %d + + Notice: Trying to get property 'nodeValue' of non-object in %s on line %d +diff --git a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt +index 845bd4bbe3c..55d8e61ee09 100644 +--- a/ext/libxml/tests/libxml_disable_entity_loader_2.phpt ++++ b/ext/libxml/tests/libxml_disable_entity_loader_2.phpt +@@ -36,6 +36,6 @@ echo "Done\n"; + bool(true) + bool(false) + +-Warning: DOMDocument::loadXML(): I/O warning : failed to load external entity "%s" in %s on line %d ++Warning: DOMDocument::loadXML(): %Sfailed to load external entity "%s" in %s on line %d + bool(true) + Done +diff --git a/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt b/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt +index 40b31ea85d3..00e06eb8a25 100644 +--- a/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt ++++ b/ext/libxml/tests/libxml_set_external_entity_loader_error1.phpt +@@ -35,6 +35,8 @@ Warning: libxml_set_external_entity_loader() expects exactly 1 parameter, 2 give + NULL + bool(true) + ++Warning: DOMDocument::validate(): Call to user entity loader callback %s ++ + Warning: DOMDocument::validate(): Could not load the external subset "http://example.com/foobar" in %s on line %d + Exception: Too few arguments to function {closure}(), 3 passed and exactly 4 expected + Done. +diff --git a/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt b/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt +index e51869cf47f..0664de1ea6b 100644 +--- a/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt ++++ b/ext/libxml/tests/libxml_set_external_entity_loader_variation2.phpt +@@ -38,6 +38,8 @@ echo "Done.\n"; + string(10) "-//FOO/BAR" + string(%d) "%sfoobar.dtd" + ++Warning: DOMDocument::validate(): Failed to load external entity "-//FOO/BAR" in %s on line %d ++ + Warning: DOMDocument::validate(): Could not load the external subset "foobar.dtd" in %s on line %d + bool(false) + bool(true) +diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc +index c74da444102..753366df6f4 100644 +--- a/ext/openssl/tests/ServerClientTestCase.inc ++++ b/ext/openssl/tests/ServerClientTestCase.inc +@@ -4,19 +4,14 @@ const WORKER_ARGV_VALUE = 'RUN_WORKER'; + + const WORKER_DEFAULT_NAME = 'server'; + +-function phpt_notify(string $worker = WORKER_DEFAULT_NAME, string $message = ""): void ++function phpt_notify($worker = WORKER_DEFAULT_NAME) + { +- ServerClientTestCase::getInstance()->notify($worker, $message); ++ ServerClientTestCase::getInstance()->notify($worker); + } + +-function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null): ?string ++function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null) + { +- return ServerClientTestCase::getInstance()->wait($worker, $timeout); +-} +- +-function phpt_notify_server_start($server): void +-{ +- ServerClientTestCase::getInstance()->notify_server_start($server); ++ ServerClientTestCase::getInstance()->wait($worker, $timeout); + } + + function phpt_has_sslv3() { +@@ -124,73 +119,43 @@ class ServerClientTestCase + eval($code); + } + +- /** +- * Run client and all workers +- * +- * @param string $clientCode The client PHP code +- * @param string|array $workerCode +- * @param bool $ephemeral Select whether automatic port selection and automatic awaiting is used +- * @return void +- * @throws Exception +- */ +- public function run(string $clientCode, $workerCode, bool $ephemeral = true): void ++ public function run($masterCode, $workerCode) + { + if (!is_array($workerCode)) { + $workerCode = [WORKER_DEFAULT_NAME => $workerCode]; + } +- reset($workerCode); +- $code = current($workerCode); +- $worker = key($workerCode); +- while ($worker != null) { ++ foreach ($workerCode as $worker => $code) { + $this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code)); +- $code = next($workerCode); +- if ($ephemeral) { +- $addr = trim($this->wait($worker)); +- if (empty($addr)) { +- throw new \Exception("Failed server start"); +- } +- if ($code === false) { +- $clientCode = preg_replace('/{{\s*ADDR\s*}}/', $addr, $clientCode); +- } else { +- $code = preg_replace('/{{\s*ADDR\s*}}/', $addr, $code); +- } +- } +- $worker = key($workerCode); + } +- +- eval($this->stripPhpTagsFromCode($clientCode)); ++ eval($this->stripPhpTagsFromCode($masterCode)); + foreach ($workerCode as $worker => $code) { + $this->cleanupWorkerProcess($worker); + } + } + +- public function wait($worker, $timeout = null): ?string ++ public function wait($worker, $timeout = null) + { + $handle = $this->isWorker ? STDIN : $this->workerStdOut[$worker]; + if ($timeout === null) { +- return fgets($handle); ++ fgets($handle); ++ return true; + } + + stream_set_blocking($handle, false); + $read = [$handle]; + $result = stream_select($read, $write, $except, $timeout); + if (!$result) { +- return null; ++ return false; + } + +- $result = fgets($handle); ++ fgets($handle); + stream_set_blocking($handle, true); +- return $result; +- } +- +- public function notify(string $worker, string $message = ""): void +- { +- fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "$message\n"); ++ return true; + } + +- public function notify_server_start($server): void ++ public function notify($worker) + { +- echo stream_socket_get_name($server, false) . "\n"; ++ fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n"); + } + } + +diff --git a/ext/standard/tests/http/ServerClientTestCase.inc b/ext/standard/tests/http/ServerClientTestCase.inc +new file mode 100644 +index 00000000000..c74da444102 +--- /dev/null ++++ b/ext/standard/tests/http/ServerClientTestCase.inc +@@ -0,0 +1,199 @@ ++<?php ++ ++const WORKER_ARGV_VALUE = 'RUN_WORKER'; ++ ++const WORKER_DEFAULT_NAME = 'server'; ++ ++function phpt_notify(string $worker = WORKER_DEFAULT_NAME, string $message = ""): void ++{ ++ ServerClientTestCase::getInstance()->notify($worker, $message); ++} ++ ++function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null): ?string ++{ ++ return ServerClientTestCase::getInstance()->wait($worker, $timeout); ++} ++ ++function phpt_notify_server_start($server): void ++{ ++ ServerClientTestCase::getInstance()->notify_server_start($server); ++} ++ ++function phpt_has_sslv3() { ++ static $result = null; ++ if (!is_null($result)) { ++ return $result; ++ } ++ $server = @stream_socket_server('sslv3://127.0.0.1:10013'); ++ if ($result = !!$server) { ++ fclose($server); ++ } ++ return $result; ++} ++ ++/** ++ * This is a singleton to let the wait/notify functions work ++ * I know it's horrible, but it's a means to an end ++ */ ++class ServerClientTestCase ++{ ++ private $isWorker = false; ++ ++ private $workerHandle = []; ++ ++ private $workerStdIn = []; ++ ++ private $workerStdOut = []; ++ ++ private static $instance; ++ ++ public static function getInstance($isWorker = false) ++ { ++ if (!isset(self::$instance)) { ++ self::$instance = new self($isWorker); ++ } ++ ++ return self::$instance; ++ } ++ ++ public function __construct($isWorker = false) ++ { ++ if (!isset(self::$instance)) { ++ self::$instance = $this; ++ } ++ ++ $this->isWorker = $isWorker; ++ } ++ ++ private function spawnWorkerProcess($worker, $code) ++ { ++ if (defined("PHP_WINDOWS_VERSION_MAJOR")) { ++ $ini = php_ini_loaded_file(); ++ $cmd = sprintf( ++ '%s %s "%s" %s', ++ PHP_BINARY, $ini ? "-n -c $ini" : "", ++ __FILE__, ++ WORKER_ARGV_VALUE ++ ); ++ } else { ++ $cmd = sprintf( ++ '%s "%s" %s %s', ++ PHP_BINARY, ++ __FILE__, ++ WORKER_ARGV_VALUE, ++ $worker ++ ); ++ } ++ $this->workerHandle[$worker] = proc_open( ++ $cmd, ++ [['pipe', 'r'], ['pipe', 'w'], STDERR], ++ $pipes ++ ); ++ $this->workerStdIn[$worker] = $pipes[0]; ++ $this->workerStdOut[$worker] = $pipes[1]; ++ ++ fwrite($this->workerStdIn[$worker], $code . "\n---\n"); ++ } ++ ++ private function cleanupWorkerProcess($worker) ++ { ++ fclose($this->workerStdIn[$worker]); ++ fclose($this->workerStdOut[$worker]); ++ proc_close($this->workerHandle[$worker]); ++ } ++ ++ private function stripPhpTagsFromCode($code) ++ { ++ return preg_replace('/^\s*<\?(?:php)?|\?>\s*$/i', '', $code); ++ } ++ ++ public function runWorker() ++ { ++ $code = ''; ++ ++ while (1) { ++ $line = fgets(STDIN); ++ ++ if (trim($line) === "---") { ++ break; ++ } ++ ++ $code .= $line; ++ } ++ ++ eval($code); ++ } ++ ++ /** ++ * Run client and all workers ++ * ++ * @param string $clientCode The client PHP code ++ * @param string|array $workerCode ++ * @param bool $ephemeral Select whether automatic port selection and automatic awaiting is used ++ * @return void ++ * @throws Exception ++ */ ++ public function run(string $clientCode, $workerCode, bool $ephemeral = true): void ++ { ++ if (!is_array($workerCode)) { ++ $workerCode = [WORKER_DEFAULT_NAME => $workerCode]; ++ } ++ reset($workerCode); ++ $code = current($workerCode); ++ $worker = key($workerCode); ++ while ($worker != null) { ++ $this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code)); ++ $code = next($workerCode); ++ if ($ephemeral) { ++ $addr = trim($this->wait($worker)); ++ if (empty($addr)) { ++ throw new \Exception("Failed server start"); ++ } ++ if ($code === false) { ++ $clientCode = preg_replace('/{{\s*ADDR\s*}}/', $addr, $clientCode); ++ } else { ++ $code = preg_replace('/{{\s*ADDR\s*}}/', $addr, $code); ++ } ++ } ++ $worker = key($workerCode); ++ } ++ ++ eval($this->stripPhpTagsFromCode($clientCode)); ++ foreach ($workerCode as $worker => $code) { ++ $this->cleanupWorkerProcess($worker); ++ } ++ } ++ ++ public function wait($worker, $timeout = null): ?string ++ { ++ $handle = $this->isWorker ? STDIN : $this->workerStdOut[$worker]; ++ if ($timeout === null) { ++ return fgets($handle); ++ } ++ ++ stream_set_blocking($handle, false); ++ $read = [$handle]; ++ $result = stream_select($read, $write, $except, $timeout); ++ if (!$result) { ++ return null; ++ } ++ ++ $result = fgets($handle); ++ stream_set_blocking($handle, true); ++ return $result; ++ } ++ ++ public function notify(string $worker, string $message = ""): void ++ { ++ fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "$message\n"); ++ } ++ ++ public function notify_server_start($server): void ++ { ++ echo stream_socket_get_name($server, false) . "\n"; ++ } ++} ++ ++if (isset($argv[1]) && $argv[1] === WORKER_ARGV_VALUE) { ++ ServerClientTestCase::getInstance(true)->runWorker(); ++} +diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt +index 46d77ec4aff..3475a03beed 100644 +--- a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt ++++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt +@@ -39,7 +39,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt +index d25c89d06e5..706a85f410b 100644 +--- a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt ++++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt +@@ -39,7 +39,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt +index c8dcd47a4a4..121f077c9f5 100644 +--- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt ++++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt +@@ -36,7 +36,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt +index ca8f75f0327..0d141f93af3 100644 +--- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt ++++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt +@@ -36,7 +36,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt +index 4cfbc7ee804..8041487d044 100644 +--- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt ++++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt +@@ -36,7 +36,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt +index 53baa1c92d6..f491acfae27 100644 +--- a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt ++++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt +@@ -35,7 +35,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt +index 5aa0ee00618..4320b17b97d 100644 +--- a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt ++++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt +@@ -35,7 +35,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt +index 64904bfcd1d..3f1cc79bd9c 100644 +--- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt +@@ -35,7 +35,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt +index a6d9d00fd58..c7c13877fef 100644 +--- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt +@@ -35,7 +35,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt +index 4eff7fc63f3..c67663b65f7 100644 +--- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt +@@ -35,7 +35,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt +index 71aed2fa2e8..7a59e2688fd 100644 +--- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt +@@ -35,7 +35,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt +index 49d845d84b4..f097762ef9e 100644 +--- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt ++++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt +@@ -35,7 +35,7 @@ $clientCode = <<<'CODE' + var_dump($http_response_header); + CODE; + +-include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++include sprintf("%s/ServerClientTestCase.inc", __DIR__); + ServerClientTestCase::getInstance()->run($clientCode, $serverCode); + ?> + --EXPECTF-- +diff --git a/ext/xml/tests/bug26614_libxml_gte2_11.phpt b/ext/xml/tests/bug26614_libxml_gte2_11.phpt +new file mode 100644 +index 00000000000..9a81b67686d +--- /dev/null ++++ b/ext/xml/tests/bug26614_libxml_gte2_11.phpt +@@ -0,0 +1,95 @@ ++--TEST-- ++Bug #26614 (CDATA sections skipped on line count) ++--EXTENSIONS-- ++xml ++--SKIPIF-- ++<?php ++if (!defined("LIBXML_VERSION")) die('skip libxml2 test'); ++if (LIBXML_VERSION < 21100) die('skip libxml2 test variant for version >= 2.11'); ++?> ++--FILE-- ++<?php ++/* ++this test works fine with Expat but fails with libxml ++which we now use as default ++ ++further investigation has shown that not only line count ++is skipped on CDATA sections but that libxml does also ++show different column numbers and byte positions depending ++on context and in opposition to what one would expect to ++see and what good old Expat reported just fine ... ++*/ ++ ++$xmls = array(); ++ ++// Case 1: CDATA Sections ++$xmls["CDATA"] ='<?xml version="1.0" encoding="iso-8859-1" ?> ++<data> ++<![CDATA[ ++multi ++line ++CDATA ++block ++]]> ++</data>'; ++ ++// Case 2: replace some characters so that we get comments instead ++$xmls["Comment"] ='<?xml version="1.0" encoding="iso-8859-1" ?> ++<data> ++<!-- ATA[ ++multi ++line ++CDATA ++block ++--> ++</data>'; ++ ++// Case 3: replace even more characters so that only textual data is left ++$xmls["Text"] ='<?xml version="1.0" encoding="iso-8859-1" ?> ++<data> ++-!-- ATA[ ++multi ++line ++CDATA ++block ++--- ++</data>'; ++ ++function startElement($parser, $name, $attrs) { ++ printf("<$name> at line %d, col %d (byte %d)\n", ++ xml_get_current_line_number($parser), ++ xml_get_current_column_number($parser), ++ xml_get_current_byte_index($parser)); ++} ++ ++function endElement($parser, $name) { ++ printf("</$name> at line %d, col %d (byte %d)\n", ++ xml_get_current_line_number($parser), ++ xml_get_current_column_number($parser), ++ xml_get_current_byte_index($parser)); ++} ++ ++function characterData($parser, $data) { ++ // dummy ++} ++ ++foreach ($xmls as $desc => $xml) { ++ echo "$desc\n"; ++ $xml_parser = xml_parser_create(); ++ xml_set_element_handler($xml_parser, "startElement", "endElement"); ++ xml_set_character_data_handler($xml_parser, "characterData"); ++ if (!xml_parse($xml_parser, $xml, true)) ++ echo "Error: ".xml_error_string(xml_get_error_code($xml_parser))."\n"; ++ xml_parser_free($xml_parser); ++} ++?> ++--EXPECTF-- ++CDATA ++<DATA> at line 2, col %d (byte 50) ++</DATA> at line 9, col %d (byte 96) ++Comment ++<DATA> at line 2, col %d (byte 50) ++</DATA> at line 9, col %d (byte 96) ++Text ++<DATA> at line 2, col %d (byte 50) ++</DATA> at line 9, col %d (byte 96) +diff --git a/ext/xml/tests/bug26614_libxml.phpt b/ext/xml/tests/bug26614_libxml_pre2_11.phpt +similarity index 96% +rename from ext/xml/tests/bug26614_libxml.phpt +rename to ext/xml/tests/bug26614_libxml_pre2_11.phpt +index 3ddd35ed0ea..afacaa1c59a 100644 +--- a/ext/xml/tests/bug26614_libxml.phpt ++++ b/ext/xml/tests/bug26614_libxml_pre2_11.phpt +@@ -4,6 +4,7 @@ Bug #26614 (CDATA sections skipped on line count) + <?php + require_once("skipif.inc"); + if (!defined("LIBXML_VERSION")) die('skip libxml2 test'); ++if (LIBXML_VERSION >= 21100) die('skip libxml2 test variant for version < 2.11'); + ?> + --FILE-- + <?php +-- +2.48.1 + +From 087e974d74efc977dfbd18fd3cd568b60fb7675d Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Fri, 1 Dec 2023 18:03:35 +0100 +Subject: [PATCH 06/11] Backport 0a39890c: Fix libxml2 2.12 build due to API + breaks + +See https://github.com/php/php-src/actions/runs/7062192818/job/19225478601 + +(cherry picked from commit fa6a0f80f644932506666beb7c85e4041c4a4646) +(cherry picked from commit 6e8e9f558aa0903e9650dd166a0a53c359d9e9e0) +--- + ext/libxml/libxml.c | 14 ++++++++++---- + ext/soap/php_sdl.c | 2 +- + 2 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c +index 5d9c23e0d7e..7917f636a9e 100644 +--- a/ext/libxml/libxml.c ++++ b/ext/libxml/libxml.c +@@ -530,7 +530,11 @@ static int _php_libxml_free_error(xmlErrorPtr error) + return 1; + } + +-static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg) ++#if LIBXML_VERSION >= 21200 ++static void _php_list_set_error_structure(const xmlError *error, const char *msg) ++#else ++static void _php_list_set_error_structure(xmlError *error, const char *msg) ++#endif + { + xmlError error_copy; + int ret; +@@ -784,7 +788,11 @@ PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...) + va_end(args); + } + ++#if LIBXML_VERSION >= 21200 ++PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, const xmlError *error) ++#else + PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error) ++#endif + { + _php_list_set_error_structure(error, NULL); + +@@ -1063,9 +1071,7 @@ static PHP_FUNCTION(libxml_use_internal_errors) + Retrieve last error from libxml */ + static PHP_FUNCTION(libxml_get_last_error) + { +- xmlErrorPtr error; +- +- error = xmlGetLastError(); ++ const xmlError *error = xmlGetLastError(); + + if (error) { + object_init_ex(return_value, libxmlerror_class_entry); +diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c +index 26a23f57db2..3df532a2d65 100644 +--- a/ext/soap/php_sdl.c ++++ b/ext/soap/php_sdl.c +@@ -333,7 +333,7 @@ static void load_wsdl_ex(zval *this_ptr, char *struri, sdlCtx *ctx, int include) + sdl_restore_uri_credentials(ctx); + + if (!wsdl) { +- xmlErrorPtr xmlErrorPtr = xmlGetLastError(); ++ const xmlError *xmlErrorPtr = xmlGetLastError(); + + if (xmlErrorPtr) { + soap_error2(E_ERROR, "Parsing WSDL: Couldn't load from '%s' : %s", struri, xmlErrorPtr->message); +-- +2.48.1 + +From aa8817ab42f758c988dfd3158f705da770238a88 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Thu, 4 Jul 2024 06:29:50 -0700 +Subject: [PATCH 07/11] Backport 4fe82131: Backport libxml2 2.13.2 fixes + (#14816) + +Backproted from https://github.com/php/php-src/pull/14789 + +(cherry picked from commit bb46b4b799b583528025a775af45308133bfd4c1) +(cherry picked from commit 6cb68826aaf68ffe8c70c8782450c38970236040) +--- + ext/dom/document.c | 6 ++-- + .../DOMDocument_loadHTMLfile_error1.phpt | 2 +- + .../DOMDocument_relaxNGValidate_error2.phpt | 2 +- + .../tests/DOMDocument_saveHTMLFile_basic.phpt | 1 + + ...DOMDocument_saveHTMLFile_formatOutput.phpt | 1 + + ...nt_saveHTMLFile_formatOutput_gte_2_13.phpt | 32 +++++++++++++++++++ + .../DOMDocument_saveHTML_basic_gte_2_13.phpt | 31 ++++++++++++++++++ + .../DOMDocument_schemaValidate_error5.phpt | 2 +- + ext/dom/tests/dom_create_element.phpt | 14 +++----- + ext/libxml/libxml.c | 4 ++- + ext/simplexml/tests/bug79971_1.phpt | 2 +- + ext/soap/php_encoding.c | 9 ++++-- + ext/soap/php_xml.c | 4 ++- + ext/soap/tests/bugs/bug42151.phpt | 4 +-- + ext/xml/compat.c | 3 +- + ext/xmlwriter/php_xmlwriter.c | 3 +- + 16 files changed, 95 insertions(+), 25 deletions(-) + create mode 100644 ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput_gte_2_13.phpt + create mode 100644 ext/dom/tests/DOMDocument_saveHTML_basic_gte_2_13.phpt + +diff --git a/ext/dom/document.c b/ext/dom/document.c +index 989b5b3dd24..af06fb41240 100644 +--- a/ext/dom/document.c ++++ b/ext/dom/document.c +@@ -1457,11 +1457,13 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so + if (keep_blanks == 0 && ! (options & XML_PARSE_NOBLANKS)) { + options |= XML_PARSE_NOBLANKS; + } ++ if (recover) { ++ options |= XML_PARSE_RECOVER; ++ } + + php_libxml_sanitize_parse_ctxt_options(ctxt); + xmlCtxtUseOptions(ctxt, options); + +- ctxt->recovery = recover; + if (recover) { + old_error_reporting = EG(error_reporting); + EG(error_reporting) = old_error_reporting | E_WARNING; +@@ -1471,7 +1473,7 @@ static xmlDocPtr dom_document_parser(zval *id, int mode, char *source, size_t so + + if (ctxt->wellFormed || recover) { + ret = ctxt->myDoc; +- if (ctxt->recovery) { ++ if (recover) { + EG(error_reporting) = old_error_reporting; + } + /* If loading from memory, set the base reference uri for the document */ +diff --git a/ext/dom/tests/DOMDocument_loadHTMLfile_error1.phpt b/ext/dom/tests/DOMDocument_loadHTMLfile_error1.phpt +index cfb41686e87..fc78273c85f 100644 +--- a/ext/dom/tests/DOMDocument_loadHTMLfile_error1.phpt ++++ b/ext/dom/tests/DOMDocument_loadHTMLfile_error1.phpt +@@ -15,4 +15,4 @@ $result = $doc->loadHTMLFile(__DIR__ . "/ffff/test.html"); + assert($result === false); + ?> + --EXPECTF-- +-%r(PHP ){0,1}%rWarning: DOMDocument::loadHTMLFile(): I/O warning : failed to load external entity %s ++%r(PHP ){0,1}%rWarning: DOMDocument::loadHTMLFile(): I/O %s +diff --git a/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt b/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt +index cdd6e64194c..19bb4dce2d6 100644 +--- a/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt ++++ b/ext/dom/tests/DOMDocument_relaxNGValidate_error2.phpt +@@ -22,7 +22,7 @@ $result = $doc->relaxNGValidate($rng); + var_dump($result); + ?> + --EXPECTF-- +-Warning: DOMDocument::relaxNGValidate(): I/O warning : failed to load external entity "%s/foo.rng" in %s on line %d ++Warning: DOMDocument::relaxNGValidate(): I/O %s : failed to load %s + + Warning: DOMDocument::relaxNGValidate(): xmlRelaxNGParse: could not load %s/foo.rng in %s on line %d + +diff --git a/ext/dom/tests/DOMDocument_saveHTMLFile_basic.phpt b/ext/dom/tests/DOMDocument_saveHTMLFile_basic.phpt +index f71db0c32a3..c51852e120c 100644 +--- a/ext/dom/tests/DOMDocument_saveHTMLFile_basic.phpt ++++ b/ext/dom/tests/DOMDocument_saveHTMLFile_basic.phpt +@@ -6,6 +6,7 @@ Knut Urdalen <knut@php.net> + --SKIPIF-- + <?php + require_once __DIR__ .'/skipif.inc'; ++if (LIBXML_VERSION >= 21300) die("skip see https://gitlab.gnome.org/GNOME/libxml2/-/issues/756"); + ?> + --FILE-- + <?php +diff --git a/ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput.phpt b/ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput.phpt +index 376c9a8e323..8d7baa7b7e8 100644 +--- a/ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput.phpt ++++ b/ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput.phpt +@@ -6,6 +6,7 @@ Knut Urdalen <knut@php.net> + --SKIPIF-- + <?php + require_once __DIR__ .'/skipif.inc'; ++if (LIBXML_VERSION >= 21300) die("skip see https://gitlab.gnome.org/GNOME/libxml2/-/issues/756"); + ?> + --FILE-- + <?php +diff --git a/ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput_gte_2_13.phpt b/ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput_gte_2_13.phpt +new file mode 100644 +index 00000000000..3477edfcf5f +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_saveHTMLFile_formatOutput_gte_2_13.phpt +@@ -0,0 +1,32 @@ ++--TEST-- ++DOMDocument::saveHTMLFile() should format output on demand ++--CREDITS-- ++Knut Urdalen <knut@php.net> ++#PHPTestFest2009 Norway 2009-06-09 \o/ ++--EXTENSIONS-- ++dom ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21300) die("skip see https://gitlab.gnome.org/GNOME/libxml2/-/issues/756"); ++?> ++--FILE-- ++<?php ++$filename = __DIR__."/DOMDocument_saveHTMLFile_formatOutput_gte_2_13.html"; ++$doc = new DOMDocument('1.0'); ++$doc->formatOutput = true; ++$root = $doc->createElement('html'); ++$root = $doc->appendChild($root); ++$head = $doc->createElement('head'); ++$head = $root->appendChild($head); ++$title = $doc->createElement('title'); ++$title = $head->appendChild($title); ++$text = $doc->createTextNode('This is the title'); ++$text = $title->appendChild($text); ++$bytes = $doc->saveHTMLFile($filename); ++var_dump($bytes); ++echo file_get_contents($filename); ++unlink($filename); ++?> ++--EXPECT-- ++int(59) ++<html><head><title>This is the title</title></head></html> +diff --git a/ext/dom/tests/DOMDocument_saveHTML_basic_gte_2_13.phpt b/ext/dom/tests/DOMDocument_saveHTML_basic_gte_2_13.phpt +new file mode 100644 +index 00000000000..c0be105253d +--- /dev/null ++++ b/ext/dom/tests/DOMDocument_saveHTML_basic_gte_2_13.phpt +@@ -0,0 +1,31 @@ ++--TEST-- ++DOMDocument::saveHTMLFile() should dump the internal document into a file using HTML formatting ++--CREDITS-- ++Knut Urdalen <knut@php.net> ++#PHPTestFest2009 Norway 2009-06-09 \o/ ++--EXTENSIONS-- ++dom ++--SKIPIF-- ++<?php ++if (LIBXML_VERSION < 21300) die("skip see https://gitlab.gnome.org/GNOME/libxml2/-/issues/756"); ++?> ++--FILE-- ++<?php ++$filename = __DIR__."/DOMDocument_saveHTMLFile_basic_gte_2_13.html"; ++$doc = new DOMDocument('1.0'); ++$root = $doc->createElement('html'); ++$root = $doc->appendChild($root); ++$head = $doc->createElement('head'); ++$head = $root->appendChild($head); ++$title = $doc->createElement('title'); ++$title = $head->appendChild($title); ++$text = $doc->createTextNode('This is the title'); ++$text = $title->appendChild($text); ++$bytes = $doc->saveHTMLFile($filename); ++var_dump($bytes); ++echo file_get_contents($filename); ++unlink($filename); ++?> ++--EXPECT-- ++int(59) ++<html><head><title>This is the title</title></head></html> +diff --git a/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt b/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt +index cb57b55b41a..44ea52c2d06 100644 +--- a/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt ++++ b/ext/dom/tests/DOMDocument_schemaValidate_error5.phpt +@@ -17,7 +17,7 @@ var_dump($result); + + ?> + --EXPECTF-- +-Warning: DOMDocument::schemaValidate(): I/O warning : failed to load external entity "%snon-existent-file" in %s.php on line %d ++Warning: DOMDocument::schemaValidate(): I/O %s : failed to load %s + + Warning: DOMDocument::schemaValidate(): Failed to locate the main schema resource at '%s/non-existent-file'. in %s.php on line %d + +diff --git a/ext/dom/tests/dom_create_element.phpt b/ext/dom/tests/dom_create_element.phpt +index bd2c8f11dae..70ae54a11bb 100644 +--- a/ext/dom/tests/dom_create_element.phpt ++++ b/ext/dom/tests/dom_create_element.phpt +@@ -251,14 +251,10 @@ try { + print $e->getMessage() . "\n"; + } + +-/* This isn't because the xml namespace isn't there and we can't create it */ +-print "29 DOMElement::__construct('xml:valid', '', 'http://www.w3.org/XML/1998/namespace')\n"; +-try { +- $element = new DomElement('xml:valid', '', 'http://www.w3.org/XML/1998/namespace'); +- print "valid\n"; +-} catch (Exception $e) { +- print $e->getMessage() . "\n"; +-} ++/* There used to be a 29 here that tested DOMElement::__construct('xml:valid', '', 'http://www.w3.org/XML/1998/namespace'). ++ * In libxml2 version 2.12 or prior this didn't work because the xml namespace isn't there and you can't create it without ++ * a document. Starting from libxml2 version 2.13 it does actually work because the XML namespace is statically defined. ++ * The behaviour from version 2.13 is actually the desired behaviour anyway. */ + + + /* the qualifiedName or its prefix is "xmlns" and the namespaceURI is +@@ -378,8 +374,6 @@ Namespace Error + Namespace Error + 28 DOMDocument::createElementNS('http://www.w3.org/XML/1998/namespace', 'xml:valid') + valid +-29 DOMElement::__construct('xml:valid', '', 'http://www.w3.org/XML/1998/namespace') +-Namespace Error + 30 DOMDocument::createElementNS('http://wrong.namespaceURI.com', 'xmlns:valid') + Namespace Error + 31 DOMElement::__construct('xmlns:valid', '', 'http://wrong.namespaceURI.com') +diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c +index 7917f636a9e..4b9e6a918d4 100644 +--- a/ext/libxml/libxml.c ++++ b/ext/libxml/libxml.c +@@ -476,8 +476,10 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) + static xmlOutputBufferPtr + php_libxml_output_buffer_create_filename(const char *URI, + xmlCharEncodingHandlerPtr encoder, +- int compression ATTRIBUTE_UNUSED) ++ int compression) + { ++ ZEND_IGNORE_VALUE(compression); ++ + xmlOutputBufferPtr ret; + xmlURIPtr puri; + void *context = NULL; +diff --git a/ext/simplexml/tests/bug79971_1.phpt b/ext/simplexml/tests/bug79971_1.phpt +index 197776d82d3..2ee24e89f12 100644 +--- a/ext/simplexml/tests/bug79971_1.phpt ++++ b/ext/simplexml/tests/bug79971_1.phpt +@@ -20,7 +20,7 @@ var_dump($sxe->asXML("$uri.out%00foo")); + --EXPECTF-- + Warning: simplexml_load_file(): URI must not contain percent-encoded NUL bytes in %s on line %d + +-Warning: simplexml_load_file(): I/O warning : failed to load external entity "%s/bug79971_1.xml%00foo" in %s on line %d ++Warning: simplexml_load_file(): I/O warning : failed to load %s + bool(false) + + Warning: SimpleXMLElement::asXML(): URI must not contain percent-encoded NUL bytes in %s on line %d +diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c +index e0cf63dd1da..0a6edbf5a41 100644 +--- a/ext/soap/php_encoding.c ++++ b/ext/soap/php_encoding.c +@@ -3381,7 +3381,6 @@ xmlNsPtr encode_add_ns(xmlNodePtr node, const char* ns) + } else { + smart_str prefix = {0}; + int num = ++SOAP_GLOBAL(cur_uniq_ns); +- xmlChar *enc_ns; + + while (1) { + smart_str_appendl(&prefix, "ns", 2); +@@ -3395,9 +3394,15 @@ xmlNsPtr encode_add_ns(xmlNodePtr node, const char* ns) + num = ++SOAP_GLOBAL(cur_uniq_ns); + } + +- enc_ns = xmlEncodeSpecialChars(node->doc, BAD_CAST(ns)); ++ /* Starting with libxml 2.13, we don't have to do this workaround anymore, otherwise we get double-encoded ++ * entities. See libxml2 commit f506ec66547ef9bac97a2bf306d368ecea8c0c9e. */ ++#if LIBXML_VERSION < 21300 ++ xmlChar *enc_ns = xmlEncodeSpecialChars(node->doc, BAD_CAST(ns)); + xmlns = xmlNewNs(node->doc->children, enc_ns, BAD_CAST(prefix.s ? ZSTR_VAL(prefix.s) : "")); + xmlFree(enc_ns); ++#else ++ xmlns = xmlNewNs(node->doc->children, BAD_CAST(ns), BAD_CAST(prefix.s ? ZSTR_VAL(prefix.s) : "")); ++#endif + smart_str_free(&prefix); + } + } +diff --git a/ext/soap/php_xml.c b/ext/soap/php_xml.c +index 1bb7fa00a37..446017eb5c8 100644 +--- a/ext/soap/php_xml.c ++++ b/ext/soap/php_xml.c +@@ -94,13 +94,14 @@ xmlDocPtr soap_xmlParseFile(const char *filename) + zend_bool old; + + php_libxml_sanitize_parse_ctxt_options(ctxt); ++ /* TODO: In libxml2 2.14.0 change this to the new options API so we don't rely on deprecated APIs. */ + ctxt->keepBlanks = 0; ++ ctxt->options |= XML_PARSE_HUGE; + ctxt->sax->ignorableWhitespace = soap_ignorableWhitespace; + ctxt->sax->comment = soap_Comment; + ctxt->sax->warning = NULL; + ctxt->sax->error = NULL; + /*ctxt->sax->fatalError = NULL;*/ +- ctxt->options |= XML_PARSE_HUGE; + old = php_libxml_disable_entity_loader(1); + xmlParseDocument(ctxt); + php_libxml_disable_entity_loader(old); +@@ -148,6 +149,7 @@ xmlDocPtr soap_xmlParseMemory(const void *buf, size_t buf_size) + ctxt->sax->warning = NULL; + ctxt->sax->error = NULL; + /*ctxt->sax->fatalError = NULL;*/ ++ /* TODO: In libxml2 2.14.0 change this to the new options API so we don't rely on deprecated APIs. */ + ctxt->options |= XML_PARSE_HUGE; + old = php_libxml_disable_entity_loader(1); + xmlParseDocument(ctxt); +diff --git a/ext/soap/tests/bugs/bug42151.phpt b/ext/soap/tests/bugs/bug42151.phpt +index ee53e6d525d..d1bcae83364 100644 +--- a/ext/soap/tests/bugs/bug42151.phpt ++++ b/ext/soap/tests/bugs/bug42151.phpt +@@ -25,8 +25,8 @@ try { + } + echo "ok\n"; + ?> +---EXPECT-- +-SOAP-ERROR: Parsing WSDL: Couldn't load from 'httpx://' : failed to load external entity "httpx://" ++--EXPECTF-- ++SOAP-ERROR: Parsing WSDL: Couldn't load from 'httpx://' : failed to load %s + + ok + I don't get executed either. +diff --git a/ext/xml/compat.c b/ext/xml/compat.c +index 57eb00dd429..ea1fd835059 100644 +--- a/ext/xml/compat.c ++++ b/ext/xml/compat.c +@@ -716,8 +716,7 @@ XML_GetCurrentByteCount(XML_Parser parser) + { + /* WARNING: this is identical to ByteIndex; it should probably + * be different */ +- return parser->parser->input->consumed + +- (parser->parser->input->cur - parser->parser->input->base); ++ return XML_GetCurrentByteIndex(parser); + } + + PHP_XML_API const XML_Char *XML_ExpatVersion(void) +diff --git a/ext/xmlwriter/php_xmlwriter.c b/ext/xmlwriter/php_xmlwriter.c +index 5cb141dad39..55874420f3b 100644 +--- a/ext/xmlwriter/php_xmlwriter.c ++++ b/ext/xmlwriter/php_xmlwriter.c +@@ -1785,7 +1785,8 @@ static void php_xmlwriter_flush(INTERNAL_FUNCTION_PARAMETERS, int force_string) + } + output_bytes = xmlTextWriterFlush(ptr); + if (buffer) { +- RETVAL_STRING((char *) buffer->content); ++ const xmlChar *content = xmlBufferContent(buffer); ++ RETVAL_STRING((const char *) content); + if (empty) { + xmlBufferEmpty(buffer); + } +-- +2.48.1 + +From 238d5f0aeaedc9c355f1bc1159b01e357bdaf344 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= <tim@tideways-gmbh.com> +Date: Wed, 20 Nov 2024 10:47:27 +0100 +Subject: [PATCH 08/11] Fix GHSA-p3x9-6h7p-cgfc: libxml streams wrong + `content-type` on redirect + +libxml streams use wrong content-type header when requesting a +redirected resource. + +(cherry picked from commit b6004a043c16b211d462218fbb3f72db68ec2b18) +(cherry picked from commit 1196e566681a34564c02173ba234b5a42587ff07) +--- + ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt | 60 ++++++++++ + ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt | 60 ++++++++++ + ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt | 60 ++++++++++ + ext/libxml/libxml.c | 77 +++++++------ + ext/standard/tests/http/newserver.inc | 124 +++++++++++++++++++++ + 5 files changed, 348 insertions(+), 33 deletions(-) + create mode 100644 ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt + create mode 100644 ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt + create mode 100644 ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt + create mode 100644 ext/standard/tests/http/newserver.inc + +diff --git a/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt +new file mode 100644 +index 00000000000..87cb2aa0b1f +--- /dev/null ++++ b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_001.phpt +@@ -0,0 +1,60 @@ ++--TEST-- ++GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Basic) ++--EXTENSIONS-- ++dom ++--SKIPIF-- ++<?php ++if (@!include "./ext/standard/tests/http/newserver.inc") die('skip server.inc not available'); ++http_server_skipif(); ++?> ++--FILE-- ++<?php ++require "./ext/standard/tests/http/newserver.inc"; ++ ++function genResponses($server) { ++ $uri = 'http://' . stream_socket_get_name($server, false); ++ yield "data://text/plain,HTTP/1.1 302 Moved Temporarily\r\nLocation: $uri/document.xml\r\nContent-Type: text/html;charset=utf-16\r\n\r\n"; ++ $xml = <<<'EOT' ++ <!doctype html> ++ <html> ++ <head> ++ <title>GHSA-p3x9-6h7p-cgfc</title> ++ ++ <meta charset="utf-8" /> ++ <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> ++ </head> ++ ++ <body> ++ <h1>GHSA-p3x9-6h7p-cgfc</h1> ++ </body> ++ </html> ++ EOT; ++ // Intentionally using non-standard casing for content-type to verify it is matched not case sensitively. ++ yield "data://text/plain,HTTP/1.1 200 OK\r\nconteNt-tyPe: text/html; charset=utf-8\r\n\r\n{$xml}"; ++} ++ ++['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output); ++$document = new \DOMDocument(); ++$document->loadHTMLFile($uri); ++ ++$h1 = $document->getElementsByTagName('h1'); ++var_dump($h1->length); ++var_dump($document->saveHTML()); ++http_server_kill($pid); ++?> ++--EXPECT-- ++int(1) ++string(266) "<!DOCTYPE html> ++<html> ++ <head> ++ <title>GHSA-p3x9-6h7p-cgfc</title> ++ ++ <meta charset="utf-8"> ++ <meta http-equiv="Content-type" content="text/html; charset=utf-8"> ++ </head> ++ ++ <body> ++ <h1>GHSA-p3x9-6h7p-cgfc</h1> ++ </body> ++</html> ++" +diff --git a/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt +new file mode 100644 +index 00000000000..1ce468c3b19 +--- /dev/null ++++ b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_002.phpt +@@ -0,0 +1,60 @@ ++--TEST-- ++GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Missing content-type) ++--EXTENSIONS-- ++dom ++--SKIPIF-- ++<?php ++if (@!include "./ext/standard/tests/http/newserver.inc") die('skip server.inc not available'); ++http_server_skipif(); ++?> ++--FILE-- ++<?php ++require "./ext/standard/tests/http/newserver.inc"; ++ ++function genResponses($server) { ++ $uri = 'http://' . stream_socket_get_name($server, false); ++ yield "data://text/plain,HTTP/1.1 302 Moved Temporarily\r\nLocation: $uri/document.xml\r\nContent-Type: text/html;charset=utf-16\r\n\r\n"; ++ $xml = <<<'EOT' ++ <!doctype html> ++ <html> ++ <head> ++ <title>GHSA-p3x9-6h7p-cgfc</title> ++ ++ <meta charset="utf-8" /> ++ <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> ++ </head> ++ ++ <body> ++ <h1>GHSA-p3x9-6h7p-cgfc</h1> ++ </body> ++ </html> ++ EOT; ++ // Missing content-type in actual response. ++ yield "data://text/plain,HTTP/1.1 200 OK\r\n\r\n{$xml}"; ++} ++ ++['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output); ++$document = new \DOMDocument(); ++$document->loadHTMLFile($uri); ++ ++$h1 = $document->getElementsByTagName('h1'); ++var_dump($h1->length); ++var_dump($document->saveHTML()); ++http_server_kill($pid); ++?> ++--EXPECT-- ++int(1) ++string(266) "<!DOCTYPE html> ++<html> ++ <head> ++ <title>GHSA-p3x9-6h7p-cgfc</title> ++ ++ <meta charset="utf-8"> ++ <meta http-equiv="Content-type" content="text/html; charset=utf-8"> ++ </head> ++ ++ <body> ++ <h1>GHSA-p3x9-6h7p-cgfc</h1> ++ </body> ++</html> ++" +diff --git a/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt +new file mode 100644 +index 00000000000..b8cac7e3247 +--- /dev/null ++++ b/ext/dom/tests/ghsa-p3x9-6h7p-cgfc_003.phpt +@@ -0,0 +1,60 @@ ++--TEST-- ++GHSA-p3x9-6h7p-cgfc: libxml streams use wrong `content-type` header when requesting a redirected resource (Reason with colon) ++--EXTENSIONS-- ++dom ++--SKIPIF-- ++<?php ++if (@!include "./ext/standard/tests/http/newserver.inc") die('skip server.inc not available'); ++http_server_skipif(); ++?> ++--FILE-- ++<?php ++require "./ext/standard/tests/http/newserver.inc"; ++ ++function genResponses($server) { ++ $uri = 'http://' . stream_socket_get_name($server, false); ++ yield "data://text/plain,HTTP/1.1 302 Moved Temporarily\r\nLocation: $uri/document.xml\r\nContent-Type: text/html;charset=utf-16\r\n\r\n"; ++ $xml = <<<'EOT' ++ <!doctype html> ++ <html> ++ <head> ++ <title>GHSA-p3x9-6h7p-cgfc</title> ++ ++ <meta charset="utf-8" /> ++ <meta http-equiv="Content-type" content="text/html; charset=utf-8" /> ++ </head> ++ ++ <body> ++ <h1>GHSA-p3x9-6h7p-cgfc</h1> ++ </body> ++ </html> ++ EOT; ++ // Missing content-type in actual response. ++ yield "data://text/plain,HTTP/1.1 200 OK: This is fine\r\n\r\n{$xml}"; ++} ++ ++['pid' => $pid, 'uri' => $uri] = http_server('genResponses', $output); ++$document = new \DOMDocument(); ++$document->loadHTMLFile($uri); ++ ++$h1 = $document->getElementsByTagName('h1'); ++var_dump($h1->length); ++var_dump($document->saveHTML()); ++http_server_kill($pid); ++?> ++--EXPECT-- ++int(1) ++string(266) "<!DOCTYPE html> ++<html> ++ <head> ++ <title>GHSA-p3x9-6h7p-cgfc</title> ++ ++ <meta charset="utf-8"> ++ <meta http-equiv="Content-type" content="text/html; charset=utf-8"> ++ </head> ++ ++ <body> ++ <h1>GHSA-p3x9-6h7p-cgfc</h1> ++ </body> ++</html> ++" +diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c +index 4b9e6a918d4..1866b7b21f4 100644 +--- a/ext/libxml/libxml.c ++++ b/ext/libxml/libxml.c +@@ -420,42 +420,53 @@ php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc) + if (Z_TYPE(s->wrapperdata) == IS_ARRAY) { + zval *header; + +- ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) { ++ /* Scan backwards: The header array might contain the headers for multiple responses, if ++ * a redirect was followed. ++ */ ++ ZEND_HASH_REVERSE_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) { + const char buf[] = "Content-Type:"; +- if (Z_TYPE_P(header) == IS_STRING && +- !zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) { +- char *needle = estrdup("charset="); +- char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header)); +- char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), sizeof("charset=")-1); +- +- if (encoding) { +- char *end; +- +- encoding += sizeof("charset=")-1; +- if (*encoding == '"') { +- encoding++; +- } +- end = strchr(encoding, ';'); +- if (end == NULL) { +- end = encoding + strlen(encoding); +- } +- end--; /* end == encoding-1 isn't a buffer underrun */ +- while (*end == ' ' || *end == '\t') { +- end--; +- } +- if (*end == '"') { +- end--; +- } +- if (encoding >= end) continue; +- *(end+1) = '\0'; +- enc = xmlParseCharEncoding(encoding); +- if (enc <= XML_CHAR_ENCODING_NONE) { +- enc = XML_CHAR_ENCODING_NONE; ++ if (Z_TYPE_P(header) == IS_STRING) { ++ /* If no colon is found in the header, we assume it's the HTTP status line and bail out. */ ++ char *colon = memchr(Z_STRVAL_P(header), ':', Z_STRLEN_P(header)); ++ char *space = memchr(Z_STRVAL_P(header), ' ', Z_STRLEN_P(header)); ++ if (colon == NULL || space < colon) { ++ break; ++ } ++ ++ if (!zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) { ++ char *needle = estrdup("charset="); ++ char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header)); ++ char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), sizeof("charset=")-1); ++ ++ if (encoding) { ++ char *end; ++ ++ encoding += sizeof("charset=")-1; ++ if (*encoding == '"') { ++ encoding++; ++ } ++ end = strchr(encoding, ';'); ++ if (end == NULL) { ++ end = encoding + strlen(encoding); ++ } ++ end--; /* end == encoding-1 isn't a buffer underrun */ ++ while (*end == ' ' || *end == '\t') { ++ end--; ++ } ++ if (*end == '"') { ++ end--; ++ } ++ if (encoding >= end) continue; ++ *(end+1) = '\0'; ++ enc = xmlParseCharEncoding(encoding); ++ if (enc <= XML_CHAR_ENCODING_NONE) { ++ enc = XML_CHAR_ENCODING_NONE; ++ } + } ++ efree(haystack); ++ efree(needle); ++ break; /* found content-type */ + } +- efree(haystack); +- efree(needle); +- break; /* found content-type */ + } + } ZEND_HASH_FOREACH_END(); + } +diff --git a/ext/standard/tests/http/newserver.inc b/ext/standard/tests/http/newserver.inc +new file mode 100644 +index 00000000000..5c636705e8c +--- /dev/null ++++ b/ext/standard/tests/http/newserver.inc +@@ -0,0 +1,124 @@ ++<?php declare(strict_types=1); ++ ++function http_server_skipif() { ++ ++ if (!function_exists('pcntl_fork')) die('skip pcntl_fork() not available'); ++ if (!function_exists('posix_kill')) die('skip posix_kill() not available'); ++ if (!stream_socket_server('tcp://localhost:0')) die('skip stream_socket_server() failed'); ++} ++ ++function http_server_init(&$output = null) { ++ pcntl_alarm(60); ++ ++ $server = stream_socket_server('tcp://localhost:0', $errno, $errstr); ++ if (!$server) { ++ return false; ++ } ++ ++ if ($output === null) { ++ $output = tmpfile(); ++ if ($output === false) { ++ return false; ++ } ++ } ++ ++ $pid = pcntl_fork(); ++ if ($pid == -1) { ++ die('could not fork'); ++ } else if ($pid) { ++ return [ ++ 'pid' => $pid, ++ 'uri' => 'http://' . stream_socket_get_name($server, false), ++ ]; ++ } ++ ++ return $server; ++} ++ ++/* Minimal HTTP server with predefined responses. ++ * ++ * $socket_string is the socket to create and listen on (e.g. tcp://127.0.0.1:1234) ++ * $files is an iterable of files or callable generator yielding files. ++ * containing N responses for N expected requests. Server dies after N requests. ++ * $output is a stream on which everything sent by clients is written to ++ */ ++function http_server($files, &$output = null) { ++ ++ if (!is_resource($server = http_server_init($output))) { ++ return $server; ++ } ++ ++ if (is_callable($files)) { ++ $files = $files($server); ++ } ++ ++ foreach($files as $file) { ++ ++ $sock = stream_socket_accept($server); ++ if (!$sock) { ++ exit(1); ++ } ++ ++ // read headers ++ ++ $content_length = 0; ++ ++ stream_set_blocking($sock, false); ++ while (!feof($sock)) { ++ ++ list($r, $w, $e) = array(array($sock), null, null); ++ if (!stream_select($r, $w, $e, 1)) continue; ++ ++ $line = stream_get_line($sock, 8192, "\r\n"); ++ if ($line === '') { ++ fwrite($output, "\r\n"); ++ break; ++ } else if ($line !== false) { ++ fwrite($output, "$line\r\n"); ++ ++ if (preg_match('#^Content-Length\s*:\s*([[:digit:]]+)\s*$#i', $line, $matches)) { ++ $content_length = (int) $matches[1]; ++ } ++ } ++ } ++ stream_set_blocking($sock, true); ++ ++ // read content ++ ++ if ($content_length > 0) { ++ stream_copy_to_stream($sock, $output, $content_length); ++ } ++ ++ // send response ++ ++ $fd = fopen($file, 'rb'); ++ stream_copy_to_stream($fd, $sock); ++ ++ fclose($sock); ++ } ++ ++ exit(0); ++} ++ ++function http_server_sleep($micro_seconds = 500000) ++{ ++ if (!is_resource($server = http_server_init($output))) { ++ return $server; ++ } ++ ++ $sock = stream_socket_accept($server); ++ if (!$sock) { ++ exit(1); ++ } ++ ++ usleep($micro_seconds); ++ ++ fclose($sock); ++ ++ exit(0); ++} ++ ++function http_server_kill(int $pid) { ++ posix_kill($pid, SIGTERM); ++ pcntl_waitpid($pid, $status); ++} +-- +2.48.1 + +From c5e836c5f98c6a01778595d448bb6a5b84eccec1 Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Wed, 18 Dec 2024 18:44:05 +0100 +Subject: [PATCH 09/11] Fix GHSA-wg4p-4hqh-c3g9 + +(cherry picked from commit 0e715e71d945b68f8ccedd62c5960df747af6625) +(cherry picked from commit 294140ee981fda6a38244215e4b16e53b7f5b2a6) +--- + ext/xml/tests/toffset_bounds.phpt | 42 +++++++++++++++++++++++++++++++ + ext/xml/xml.c | 12 ++++++--- + 2 files changed, 50 insertions(+), 4 deletions(-) + create mode 100644 ext/xml/tests/toffset_bounds.phpt + +diff --git a/ext/xml/tests/toffset_bounds.phpt b/ext/xml/tests/toffset_bounds.phpt +new file mode 100644 +index 00000000000..5a3fd22f86c +--- /dev/null ++++ b/ext/xml/tests/toffset_bounds.phpt +@@ -0,0 +1,42 @@ ++--TEST-- ++XML_OPTION_SKIP_TAGSTART bounds ++--EXTENSIONS-- ++xml ++--FILE-- ++<?php ++$sample = "<?xml version=\"1.0\"?><test><child/></test>"; ++$parser = xml_parser_create(); ++xml_parser_set_option($parser, XML_OPTION_SKIP_TAGSTART, 100); ++$res = xml_parse_into_struct($parser,$sample,$vals,$index); ++var_dump($vals); ++?> ++--EXPECT-- ++array(3) { ++ [0]=> ++ array(3) { ++ ["tag"]=> ++ string(0) "" ++ ["type"]=> ++ string(4) "open" ++ ["level"]=> ++ int(1) ++ } ++ [1]=> ++ array(3) { ++ ["tag"]=> ++ string(0) "" ++ ["type"]=> ++ string(8) "complete" ++ ["level"]=> ++ int(2) ++ } ++ [2]=> ++ array(3) { ++ ["tag"]=> ++ string(0) "" ++ ["type"]=> ++ string(5) "close" ++ ["level"]=> ++ int(1) ++ } ++} +diff --git a/ext/xml/xml.c b/ext/xml/xml.c +index 6fe6151c7a1..b56bf79f55d 100644 +--- a/ext/xml/xml.c ++++ b/ext/xml/xml.c +@@ -773,9 +773,11 @@ void _xml_startElementHandler(void *userData, const XML_Char *name, const XML_Ch + array_init(&tag); + array_init(&atr); + +- _xml_add_to_info(parser, ZSTR_VAL(tag_name) + parser->toffset); ++ char *skipped_tag_name = SKIP_TAGSTART(ZSTR_VAL(tag_name)); + +- add_assoc_string(&tag, "tag", SKIP_TAGSTART(ZSTR_VAL(tag_name))); /* cast to avoid gcc-warning */ ++ _xml_add_to_info(parser, skipped_tag_name); ++ ++ add_assoc_string(&tag, "tag", skipped_tag_name); + add_assoc_string(&tag, "type", "open"); + add_assoc_long(&tag, "level", parser->level); + +@@ -842,9 +844,11 @@ void _xml_endElementHandler(void *userData, const XML_Char *name) + } else { + array_init(&tag); + +- _xml_add_to_info(parser, ZSTR_VAL(tag_name) + parser->toffset); ++ char *skipped_tag_name = SKIP_TAGSTART(ZSTR_VAL(tag_name)); ++ ++ _xml_add_to_info(parser, skipped_tag_name); + +- add_assoc_string(&tag, "tag", SKIP_TAGSTART(ZSTR_VAL(tag_name))); /* cast to avoid gcc-warning */ ++ add_assoc_string(&tag, "tag", skipped_tag_name); + add_assoc_string(&tag, "type", "close"); + add_assoc_long(&tag, "level", parser->level); + +-- +2.48.1 + +From 3faf7b2017ccd1e7347c30cf64cddcb684300cba Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Fri, 17 Nov 2023 19:45:40 +0100 +Subject: [PATCH 10/11] Fix GH-12702: libxml2 2.12.0 issue building from src + +Fixes GH-12702. + +Co-authored-by: nono303 <github@nono303.net> +(cherry picked from commit 6a76e5d0a2dcf46b4ab74cc3ffcbfeb860c4fdb3) +(cherry picked from commit d7ab2bb9856d938fca7989575695c14c25892589) +--- + ext/dom/document.c | 1 + + ext/libxml/php_libxml.h | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/ext/dom/document.c b/ext/dom/document.c +index af06fb41240..f8071774b92 100644 +--- a/ext/dom/document.c ++++ b/ext/dom/document.c +@@ -25,6 +25,7 @@ + #if HAVE_LIBXML && HAVE_DOM + #include "php_dom.h" + #include <libxml/SAX.h> ++#include <libxml/xmlsave.h> + #ifdef LIBXML_SCHEMAS_ENABLED + #include <libxml/relaxng.h> + #include <libxml/xmlschemas.h> +diff --git a/ext/libxml/php_libxml.h b/ext/libxml/php_libxml.h +index 92028d5703e..6f3295b5241 100644 +--- a/ext/libxml/php_libxml.h ++++ b/ext/libxml/php_libxml.h +@@ -37,6 +37,7 @@ extern zend_module_entry libxml_module_entry; + + #include "zend_smart_str.h" + #include <libxml/tree.h> ++#include <libxml/parser.h> + + #define LIBXML_SAVE_NOEMPTYTAG 1<<2 + +-- +2.48.1 + +From 8ab957ca87b42b808aec7fd472fbc4063073a119 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 13 Mar 2025 09:39:19 +0100 +Subject: [PATCH 11/11] NEWS + +(cherry picked from commit adae2b8de8963ac6f92103803bf91a5174172f88) +--- + NEWS | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/NEWS b/NEWS +index 09cf2cfa0bb..fda646c7010 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,23 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.1.32 ++ ++- LibXML: ++ . Fixed GHSA-wg4p-4hqh-c3g9 (Reocurrence of #72714). (nielsdos) ++ . Fixed GHSA-p3x9-6h7p-cgfc (libxml streams use wrong `content-type` header ++ when requesting a redirected resource). (CVE-2025-1219) (timwolla) ++ ++- Streams: ++ . Fixed GHSA-hgf54-96fm-v528 (Stream HTTP wrapper header check might omit ++ basic auth header). (CVE-2025-1736) (Jakub Zelenka) ++ . Fixed GHSA-52jp-hrpf-2jff (Stream HTTP wrapper truncate redirect location ++ to 1024 bytes). (CVE-2025-1861) (Jakub Zelenka) ++ . Fixed GHSA-pcmh-g36c-qc44 (Streams HTTP wrapper does not fail for headers ++ without colon). (CVE-2025-1734) (Jakub Zelenka) ++ . Fixed GHSA-v8xr-gpvj-cx9g (Header parser of `http` stream wrapper does not ++ handle folded headers). (CVE-2025-1217) (Jakub Zelenka) ++ + Backported from 8.1.31 + + - CLI: +-- +2.48.1 + diff --git a/php-cve-2025-1220.patch b/php-cve-2025-1220.patch new file mode 100644 index 0000000..25f3b61 --- /dev/null +++ b/php-cve-2025-1220.patch @@ -0,0 +1,154 @@ +From d407d8a8735ebf43bee3e6b49fb013b8aa4b6bfc Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Thu, 10 Apr 2025 15:15:36 +0200 +Subject: [PATCH 2/4] Fix GHSA-3cr5-j632-f35r: Null byte in hostnames + +This fixes stream_socket_client() and fsockopen(). + +Specifically it adds a check to parse_ip_address_ex and it also makes +sure that the \0 is not ignored in fsockopen() hostname formatting. + +(cherry picked from commit cac8f7f1cf4939f55f06b68120040f057682d89c) +(cherry picked from commit 36150278addd8686a9899559241296094bd57282) +--- + ext/standard/fsock.c | 27 +++++++++++++++++-- + .../tests/network/ghsa-3cr5-j632-f35r.phpt | 21 +++++++++++++++ + .../tests/streams/ghsa-3cr5-j632-f35r.phpt | 26 ++++++++++++++++++ + main/streams/xp_socket.c | 9 ++++--- + 4 files changed, 78 insertions(+), 5 deletions(-) + create mode 100644 ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt + create mode 100644 ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt + +diff --git a/ext/standard/fsock.c b/ext/standard/fsock.c +index fe8fbea85ca..df6a74b078f 100644 +--- a/ext/standard/fsock.c ++++ b/ext/standard/fsock.c +@@ -25,6 +25,28 @@ + #include "php_network.h" + #include "file.h" + ++static size_t php_fsockopen_format_host_port(char **message, const char *prefix, size_t prefix_len, ++ const char *host, size_t host_len, zend_long port) ++{ ++ char portbuf[32]; ++ int portlen = snprintf(portbuf, sizeof(portbuf), ":" ZEND_LONG_FMT, port); ++ size_t total_len = prefix_len + host_len + portlen; ++ ++ char *result = emalloc(total_len + 1); ++ ++ if (prefix_len > 0) { ++ memcpy(result, prefix, prefix_len); ++ } ++ memcpy(result + prefix_len, host, host_len); ++ memcpy(result + prefix_len + host_len, portbuf, portlen); ++ ++ result[total_len] = '\0'; ++ ++ *message = result; ++ ++ return total_len; ++} ++ + /* {{{ php_fsockopen() */ + + static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) +@@ -59,11 +81,12 @@ static void php_fsockopen_stream(INTERNAL_FUNCTION_PARAMETERS, int persistent) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (persistent) { +- spprintf(&hashkey, 0, "pfsockopen__%s:" ZEND_LONG_FMT, host, port); ++ php_fsockopen_format_host_port(&hashkey, "pfsockopen__", strlen("pfsockopen__"), host, ++ host_len, port); + } + + if (port > 0) { +- hostname_len = spprintf(&hostname, 0, "%s:" ZEND_LONG_FMT, host, port); ++ hostname_len = php_fsockopen_format_host_port(&hostname, "", 0, host, host_len, port); + } else { + hostname_len = host_len; + hostname = host; +diff --git a/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt +new file mode 100644 +index 00000000000..e16d3fa9060 +--- /dev/null ++++ b/ext/standard/tests/network/ghsa-3cr5-j632-f35r.phpt +@@ -0,0 +1,21 @@ ++--TEST-- ++GHSA-3cr5-j632-f35r: Null byte termination in fsockopen() ++--FILE-- ++<?php ++ ++$server = stream_socket_server("tcp://localhost:0"); ++ ++if (preg_match('/:(\d+)$/', stream_socket_get_name($server, false), $m)) { ++ $client = fsockopen("localhost\0.example.com", intval($m[1])); ++ var_dump($client); ++ if ($client) { ++ fclose($client); ++ } ++} ++fclose($server); ++ ++?> ++--EXPECTF-- ++ ++Warning: fsockopen(): unable to connect to localhost:%d (The hostname must not contain null bytes) in %s ++bool(false) +diff --git a/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt +new file mode 100644 +index 00000000000..bc1f34eaf58 +--- /dev/null ++++ b/ext/standard/tests/streams/ghsa-3cr5-j632-f35r.phpt +@@ -0,0 +1,26 @@ ++--TEST-- ++GHSA-3cr5-j632-f35r: Null byte termination in stream_socket_client() ++--FILE-- ++<?php ++ ++$server = stream_socket_server("tcp://localhost:0"); ++$socket_name = stream_socket_get_name($server, false); ++ ++if (preg_match('/:(\d+)$/', $socket_name, $m)) { ++ $port = $m[1]; ++ $client = stream_socket_client("tcp://localhost\0.example.com:$port"); ++ var_dump($client); ++ if ($client) { ++ fclose($client); ++ } ++} else { ++ echo "Could not extract port from socket name: $socket_name\n"; ++} ++ ++fclose($server); ++ ++?> ++--EXPECTF-- ++ ++Warning: stream_socket_client(): unable to connect to tcp://localhost\0.example.com:%d (The hostname must not contain null bytes) in %s ++bool(false) +diff --git a/main/streams/xp_socket.c b/main/streams/xp_socket.c +index 46b23b63ada..7a192ea6c0b 100644 +--- a/main/streams/xp_socket.c ++++ b/main/streams/xp_socket.c +@@ -580,12 +580,15 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po + char *colon; + char *host = NULL; + +-#ifdef HAVE_IPV6 +- char *p; ++ if (memchr(str, '\0', str_len)) { ++ *err = strpprintf(0, "The hostname must not contain null bytes"); ++ return NULL; ++ } + ++#ifdef HAVE_IPV6 + if (*(str) == '[' && str_len > 1) { + /* IPV6 notation to specify raw address with port (i.e. [fe80::1]:80) */ +- p = memchr(str + 1, ']', str_len - 2); ++ char *p = memchr(str + 1, ']', str_len - 2); + if (!p || *(p + 1) != ':') { + if (get_err) { + *err = strpprintf(0, "Failed to parse IPv6 address \"%s\"", str); +-- +2.50.0 + diff --git a/php-cve-2025-1734.patch b/php-cve-2025-1734.patch new file mode 100644 index 0000000..6c9aa52 --- /dev/null +++ b/php-cve-2025-1734.patch @@ -0,0 +1,314 @@ +From 0b965cf85f512b1a7b87f100ac77e4aa13f7f421 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Sun, 19 Jan 2025 17:49:53 +0100 +Subject: [PATCH 02/11] Fix GHSA-pcmh-g36c-qc44: http headers without colon +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The header line must contain colon otherwise it is invalid and it needs +to fail. + +Reviewed-by: Tim Düsterhus <tim@tideways-gmbh.com> +(cherry picked from commit 0548c4c1756724a89ef8310709419b08aadb2b3b) +(cherry picked from commit e81d0cd14bfeb17e899c73e3aece4991bbda76af) +--- + ext/standard/http_fopen_wrapper.c | 51 ++++++++++++++----- + ext/standard/tests/http/bug47021.phpt | 26 ++++++---- + ext/standard/tests/http/bug75535.phpt | 4 +- + .../tests/http/ghsa-pcmh-g36c-qc44-001.phpt | 51 +++++++++++++++++++ + .../tests/http/ghsa-pcmh-g36c-qc44-002.phpt | 51 +++++++++++++++++++ + 5 files changed, 156 insertions(+), 27 deletions(-) + create mode 100644 ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt + create mode 100644 ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt + +diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c +index 08386cfafcd..071d6a4d119 100644 +--- a/ext/standard/http_fopen_wrapper.c ++++ b/ext/standard/http_fopen_wrapper.c +@@ -119,6 +119,7 @@ static zend_bool check_has_header(const char *headers, const char *header) { + typedef struct _php_stream_http_response_header_info { + php_stream_filter *transfer_encoding; + size_t file_size; ++ zend_bool error; + zend_bool follow_location; + char location[HTTP_HEADER_BLOCK_SIZE]; + } php_stream_http_response_header_info; +@@ -128,6 +129,7 @@ static void php_stream_http_response_header_info_init( + { + header_info->transfer_encoding = NULL; + header_info->file_size = 0; ++ header_info->error = 0; + header_info->follow_location = 1; + header_info->location[0] = '\0'; + } +@@ -165,10 +167,11 @@ static zend_bool php_stream_http_response_header_trim(char *http_header_line, + /* Process folding headers of the current line and if there are none, parse last full response + * header line. It returns NULL if the last header is finished, otherwise it returns updated + * last header line. */ +-static zend_string *php_stream_http_response_headers_parse(php_stream *stream, +- php_stream_context *context, int options, zend_string *last_header_line_str, +- char *header_line, size_t *header_line_length, int response_code, +- zval *response_header, php_stream_http_response_header_info *header_info) ++static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *wrapper, ++ php_stream *stream, php_stream_context *context, int options, ++ zend_string *last_header_line_str, char *header_line, size_t *header_line_length, ++ int response_code, zval *response_header, ++ php_stream_http_response_header_info *header_info) + { + char *last_header_line = ZSTR_VAL(last_header_line_str); + size_t last_header_line_length = ZSTR_LEN(last_header_line_str); +@@ -211,6 +214,19 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream, + /* Find header separator position. */ + char *last_header_value = memchr(last_header_line, ':', last_header_line_length); + if (last_header_value) { ++ /* Verify there is no space in header name */ ++ char *last_header_name = last_header_line + 1; ++ while (last_header_name < last_header_value) { ++ if (*last_header_name == ' ' || *last_header_name == '\t') { ++ header_info->error = 1; ++ php_stream_wrapper_log_error(wrapper, options, ++ "HTTP invalid response format (space in header name)!"); ++ zend_string_efree(last_header_line_str); ++ return NULL; ++ } ++ ++last_header_name; ++ } ++ + last_header_value++; /* Skip ':'. */ + + /* Strip leading whitespace. */ +@@ -219,9 +235,12 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream, + last_header_value++; + } + } else { +- /* There is no colon. Set the value to the end of the header line, which is effectively +- * an empty string. */ +- last_header_value = last_header_line_end; ++ /* There is no colon which means invalid response so error. */ ++ header_info->error = 1; ++ php_stream_wrapper_log_error(wrapper, options, ++ "HTTP invalid response format (no colon in header line)!"); ++ zend_string_efree(last_header_line_str); ++ return NULL; + } + + zend_bool store_header = 1; +@@ -927,10 +946,16 @@ finish: + + if (last_header_line_str != NULL) { + /* Parse last header line. */ +- last_header_line_str = php_stream_http_response_headers_parse(stream, context, +- options, last_header_line_str, http_header_line, &http_header_line_length, +- response_code, response_header, &header_info); +- if (last_header_line_str != NULL) { ++ last_header_line_str = php_stream_http_response_headers_parse(wrapper, stream, ++ context, options, last_header_line_str, http_header_line, ++ &http_header_line_length, response_code, response_header, &header_info); ++ if (EXPECTED(last_header_line_str == NULL)) { ++ if (UNEXPECTED(header_info.error)) { ++ php_stream_close(stream); ++ stream = NULL; ++ goto out; ++ } ++ } else { + /* Folding header present so continue. */ + continue; + } +@@ -960,8 +985,8 @@ finish: + + /* If the stream was closed early, we still want to process the last line to keep BC. */ + if (last_header_line_str != NULL) { +- php_stream_http_response_headers_parse(stream, context, options, last_header_line_str, +- NULL, NULL, response_code, response_header, &header_info); ++ php_stream_http_response_headers_parse(wrapper, stream, context, options, ++ last_header_line_str, NULL, NULL, response_code, response_header, &header_info); + } + + if (!reqok || (header_info.location[0] != '\0' && header_info.follow_location)) { +diff --git a/ext/standard/tests/http/bug47021.phpt b/ext/standard/tests/http/bug47021.phpt +index f3db3e1be23..a287b714c62 100644 +--- a/ext/standard/tests/http/bug47021.phpt ++++ b/ext/standard/tests/http/bug47021.phpt +@@ -47,9 +47,9 @@ function do_test($num_spaces, $leave_trailing_space=false) { + ]; + $pid = http_server('tcp://127.0.0.1:12342', $responses); + +- echo file_get_contents('http://127.0.0.1:12342/', false, $ctx); ++ echo file_get_contents('http://127.0.0.1:12342', false, $ctx); + echo "\n"; +- echo file_get_contents('http://127.0.0.1:12342/', false, $ctx); ++ echo file_get_contents('http://127.0.0.1:12342', false, $ctx); + echo "\n"; + + http_server_kill($pid); +@@ -70,23 +70,27 @@ do_test(1, true); + echo "\n"; + + ?> +---EXPECT-- ++--EXPECTF-- ++ + Type='text/plain' + Hello +-Size=5 +-World ++ ++Warning: file_get_contents(http://%s:%d): failed to open stream: HTTP invalid response format (no colon in header line)! in %s ++ + + Type='text/plain' + Hello +-Size=5 +-World ++ ++Warning: file_get_contents(http://%s:%d): failed to open stream: HTTP invalid response format (no colon in header line)! in %s ++ + + Type='text/plain' + Hello +-Size=5 +-World ++ ++Warning: file_get_contents(http://%s:%d): failed to open stream: HTTP invalid response format (no colon in header line)! in %s ++ + + Type='text/plain' + Hello +-Size=5 +-World ++ ++Warning: file_get_contents(http://%s:%d): failed to open stream: HTTP invalid response format (no colon in header line)! in %s +diff --git a/ext/standard/tests/http/bug75535.phpt b/ext/standard/tests/http/bug75535.phpt +index 9bf298cc065..e3757ba4f1d 100644 +--- a/ext/standard/tests/http/bug75535.phpt ++++ b/ext/standard/tests/http/bug75535.phpt +@@ -22,10 +22,8 @@ http_server_kill($pid); + ==DONE== + --EXPECT-- + string(0) "" +-array(2) { ++array(1) { + [0]=> + string(15) "HTTP/1.0 200 Ok" +- [1]=> +- string(14) "Content-Length" + } + ==DONE== +diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt +new file mode 100644 +index 00000000000..53baa1c92d6 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt +@@ -0,0 +1,51 @@ ++--TEST-- ++GHSA-pcmh-g36c-qc44: Header parser of http stream wrapper does not verify header name and colon (colon) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\nWrong-Header\r\nGood-Header: test\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx)); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++Found the mime-type: text/html ++ ++Warning: file_get_contents(http://127.0.0.1:%d): failed to open stream: HTTP invalid response format (no colon in header line)! in %s ++bool(false) ++array(2) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++ [1]=> ++ string(23) "Content-Type: text/html" ++} +diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt +new file mode 100644 +index 00000000000..5aa0ee00618 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt +@@ -0,0 +1,51 @@ ++--TEST-- ++GHSA-pcmh-g36c-qc44: Header parser of http stream wrapper does not verify header name and colon (name) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\nWrong-Header : test\r\nGood-Header: test\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx)); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++Found the mime-type: text/html ++ ++Warning: file_get_contents(http://127.0.0.1:%d): failed to open stream: HTTP invalid response format (space in header name)! in %s ++bool(false) ++array(2) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++ [1]=> ++ string(23) "Content-Type: text/html" ++} +-- +2.48.1 + diff --git a/php-cve-2025-1735.patch b/php-cve-2025-1735.patch new file mode 100644 index 0000000..e144cc4 --- /dev/null +++ b/php-cve-2025-1735.patch @@ -0,0 +1,492 @@ +From df2ecf34256c4a301e8959fe2eed0323f8b1b57a Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Tue, 4 Mar 2025 17:23:01 +0100 +Subject: [PATCH 3/4] Fix GHSA-hrwm-9436-5mv3: pgsql escaping no error checks + +This adds error checks for escape function is pgsql and pdo_pgsql +extensions. It prevents possibility of storing not properly escaped +data which could potentially lead to some security issues. + +(cherry picked from commit 9376aeef9f8ff81f2705b8016237ec3e30bdee44) +(cherry picked from commit 7633d987cc11ee2601223e73cfdb8b31fed5980f) +--- + ext/pdo_pgsql/pgsql_driver.c | 10 +- + ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 22 ++++ + ext/pgsql/pgsql.c | 129 +++++++++++++++---- + ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt | 64 +++++++++ + 4 files changed, 202 insertions(+), 23 deletions(-) + create mode 100644 ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt + create mode 100644 ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt + +diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c +index e578bbc2720..021471cefc0 100644 +--- a/ext/pdo_pgsql/pgsql_driver.c ++++ b/ext/pdo_pgsql/pgsql_driver.c +@@ -323,11 +323,15 @@ static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unqu + unsigned char *escaped; + pdo_pgsql_db_handle *H = (pdo_pgsql_db_handle *)dbh->driver_data; + size_t tmp_len; ++ int err; + + switch (paramtype) { + case PDO_PARAM_LOB: + /* escapedlen returned by PQescapeBytea() accounts for trailing 0 */ + escaped = PQescapeByteaConn(H->server, (unsigned char *)unquoted, unquotedlen, &tmp_len); ++ if (escaped == NULL) { ++ return 0; ++ } + *quotedlen = tmp_len + 1; + *quoted = emalloc(*quotedlen + 1); + memcpy((*quoted)+1, escaped, *quotedlen-2); +@@ -339,7 +343,11 @@ static int pgsql_handle_quoter(pdo_dbh_t *dbh, const char *unquoted, size_t unqu + default: + *quoted = safe_emalloc(2, unquotedlen, 3); + (*quoted)[0] = '\''; +- *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, NULL); ++ *quotedlen = PQescapeStringConn(H->server, *quoted + 1, unquoted, unquotedlen, &err); ++ if (err) { ++ efree(*quoted); ++ return 0; ++ } + (*quoted)[*quotedlen + 1] = '\''; + (*quoted)[*quotedlen + 2] = '\0'; + *quotedlen += 2; +diff --git a/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt +new file mode 100644 +index 00000000000..60e13613d04 +--- /dev/null ++++ b/ext/pdo_pgsql/tests/ghsa-hrwm-9436-5mv3.phpt +@@ -0,0 +1,22 @@ ++--TEST-- ++#GHSA-hrwm-9436-5mv3: pdo_pgsql extension does not check for errors during escaping ++--SKIPIF-- ++<?php ++if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded'); ++require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; ++require_once dirname(__FILE__) . '/config.inc'; ++PDOTest::skip(); ++?> ++--FILE-- ++<?php ++require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc'; ++require_once dirname(__FILE__) . '/config.inc'; ++$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); ++$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); ++ ++$invalid = "ABC\xff\x30';"; ++var_dump($db->quote($invalid)); ++ ++?> ++--EXPECT-- ++bool(false) +diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c +index 7dcd56cf144..9e06497125c 100644 +--- a/ext/pgsql/pgsql.c ++++ b/ext/pgsql/pgsql.c +@@ -4393,10 +4393,16 @@ PHP_FUNCTION(pg_escape_string) + to = zend_string_safe_alloc(ZSTR_LEN(from), 2, 0, 0); + #ifdef HAVE_PQESCAPE_CONN + if (link) { ++ int err; + if ((pgsql = (PGconn *)zend_fetch_resource2(link, "PostgreSQL link", le_link, le_plink)) == NULL) { + RETURN_FALSE; + } +- ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), NULL); ++ ZSTR_LEN(to) = PQescapeStringConn(pgsql, ZSTR_VAL(to), ZSTR_VAL(from), ZSTR_LEN(from), &err); ++ if (err) { ++ zend_throw_exception(zend_ce_exception, "Escaping string failed", 0); ++ zend_string_efree(to); ++ return; ++ } + } else + #endif + { +@@ -4445,6 +4451,10 @@ PHP_FUNCTION(pg_escape_bytea) + } else + #endif + to = (char *)PQescapeBytea((unsigned char*)from, from_len, &to_len); ++ if (to == NULL) { ++ zend_throw_exception(zend_ce_exception, "Escape failure", 0); ++ return; ++ } + + RETVAL_STRINGL(to, to_len-1); /* to_len includes additional '\0' */ + PQfreemem(to); +@@ -5529,7 +5539,7 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z + char *escaped; + smart_str querystr = {0}; + size_t new_len; +- int i, num_rows; ++ int i, num_rows, err; + zval elem; + + if (!*table_name) { +@@ -5570,7 +5580,14 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z + "WHERE a.attnum > 0 AND c.relname = '"); + } + escaped = (char *)safe_emalloc(strlen(tmp_name2), 2, 1); +- new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), NULL); ++ new_len = PQescapeStringConn(pg_link, escaped, tmp_name2, strlen(tmp_name2), &err); ++ if (err) { ++ php_error_docref(NULL, E_WARNING, "Escaping table name '%s' failed", table_name); ++ efree(src); ++ efree(escaped); ++ smart_str_free(&querystr); ++ return FAILURE; ++ } + if (new_len) { + smart_str_appendl(&querystr, escaped, new_len); + } +@@ -5578,7 +5595,14 @@ PHP_PGSQL_API int php_pgsql_meta_data(PGconn *pg_link, const char *table_name, z + + smart_str_appends(&querystr, "' AND n.nspname = '"); + escaped = (char *)safe_emalloc(strlen(tmp_name), 2, 1); +- new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), NULL); ++ new_len = PQescapeStringConn(pg_link, escaped, tmp_name, strlen(tmp_name), &err); ++ if (err) { ++ php_error_docref(NULL, E_WARNING, "Escaping table namespace '%s' failed", table_name); ++ efree(src); ++ efree(escaped); ++ smart_str_free(&querystr); ++ return FAILURE; ++ } + if (new_len) { + smart_str_appendl(&querystr, escaped, new_len); + } +@@ -5850,7 +5874,7 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con + { + zend_string *field = NULL; + zval meta, *def, *type, *not_null, *has_default, *is_enum, *val, new_val; +- int err = 0, skip_field; ++ int err = 0, escape_err = 0, skip_field; + php_pgsql_data_type data_type; + + assert(pg_link != NULL); +@@ -6101,10 +6125,14 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con + /* 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), NULL); +- str = zend_string_truncate(str, ZSTR_LEN(str), 0); +- ZVAL_NEW_STR(&new_val, str); +- php_pgsql_add_quotes(&new_val, 1); ++ 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 { ++ str = zend_string_truncate(str, ZSTR_LEN(str), 0); ++ ZVAL_NEW_STR(&new_val, str); ++ php_pgsql_add_quotes(&new_val, 1); ++ } + } + break; + +@@ -6126,7 +6154,15 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con + } + PGSQL_CONV_CHECK_IGNORE(); + if (err) { +- php_error_docref(NULL, E_NOTICE, "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); ++ if (escape_err) { ++ php_error_docref(NULL, E_NOTICE, ++ "String value escaping failed for PostgreSQL '%s' (%s)", ++ Z_STRVAL_P(type), ZSTR_VAL(field)); ++ } else { ++ php_error_docref(NULL, E_NOTICE, ++ "Expects NULL, string, long or double value for PostgreSQL '%s' (%s)", ++ Z_STRVAL_P(type), ZSTR_VAL(field)); ++ } + } + break; + +@@ -6406,6 +6442,11 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con + #else + tmp = PQescapeBytea(Z_STRVAL_P(val), (unsigned char *)Z_STRLEN_P(val), &to_len); + #endif ++ if (tmp == NULL) { ++ php_error_docref(NULL, E_NOTICE, "Escaping value failed for %s field (%s)", Z_STRVAL_P(type), ZSTR_VAL(field)); ++ err = 1; ++ break; ++ } + ZVAL_STRINGL(&new_val, (char *)tmp, to_len - 1); /* PQescapeBytea's to_len includes additional '\0' */ + PQfreemem(tmp); + php_pgsql_add_quotes(&new_val, 1); +@@ -6488,6 +6529,12 @@ PHP_PGSQL_API int php_pgsql_convert(PGconn *pg_link, const char *table_name, con + zend_hash_update(Z_ARRVAL_P(result), field, &new_val); + } else { + char *escaped = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(field), ZSTR_LEN(field)); ++ if (escaped == NULL) { ++ /* This cannot fail because of invalid string but only due to failed memory allocation */ ++ php_error_docref(NULL, E_NOTICE, "Escaping field '%s' failed", ZSTR_VAL(field)); ++ err = 1; ++ break; ++ } + add_assoc_zval(result, escaped, &new_val); + PGSQLfree(escaped); + } +@@ -6566,7 +6613,7 @@ static int do_exec(smart_str *querystr, ExecStatusType expect, PGconn *pg_link, + } + /* }}} */ + +-static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */ ++static inline int build_tablename(smart_str *querystr, PGconn *pg_link, const char *table) /* {{{ */ + { + size_t table_len = strlen(table); + +@@ -6577,6 +6624,10 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const c + smart_str_appendl(querystr, table, len); + } else { + char *escaped = PGSQLescapeIdentifier(pg_link, table, len); ++ if (escaped == NULL) { ++ php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", table); ++ return FAILURE; ++ } + smart_str_appends(querystr, escaped); + PGSQLfree(escaped); + } +@@ -6589,11 +6640,17 @@ static inline void build_tablename(smart_str *querystr, PGconn *pg_link, const c + smart_str_appendl(querystr, after_dot, len); + } else { + char *escaped = PGSQLescapeIdentifier(pg_link, after_dot, len); ++ if (escaped == NULL) { ++ php_error_docref(NULL, E_NOTICE, "Failed to escape table name '%s'", table); ++ return FAILURE; ++ } + smart_str_appendc(querystr, '.'); + smart_str_appends(querystr, escaped); + PGSQLfree(escaped); + } + } ++ ++ return SUCCESS; + } + /* }}} */ + +@@ -6615,7 +6672,9 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var + ZVAL_UNDEF(&converted); + if (zend_hash_num_elements(Z_ARRVAL_P(var_array)) == 0) { + smart_str_appends(&querystr, "INSERT INTO "); +- build_tablename(&querystr, pg_link, table); ++ if (build_tablename(&querystr, pg_link, table) == FAILURE) { ++ goto cleanup; ++ } + smart_str_appends(&querystr, " DEFAULT VALUES"); + + goto no_values; +@@ -6631,7 +6690,9 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var + } + + smart_str_appends(&querystr, "INSERT INTO "); +- build_tablename(&querystr, pg_link, table); ++ if (build_tablename(&querystr, pg_link, table) == FAILURE) { ++ goto cleanup; ++ } + smart_str_appends(&querystr, " ("); + + ZEND_HASH_FOREACH_STR_KEY(Z_ARRVAL_P(var_array), fld) { +@@ -6641,6 +6702,10 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var + } + if (opt & PGSQL_DML_ESCAPE) { + tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); ++ if (tmp == NULL) { ++ php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); ++ goto cleanup; ++ } + smart_str_appends(&querystr, tmp); + PGSQLfree(tmp); + } else { +@@ -6652,15 +6717,19 @@ PHP_PGSQL_API int php_pgsql_insert(PGconn *pg_link, const char *table, zval *var + smart_str_appends(&querystr, ") VALUES ("); + + /* make values string */ +- ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(var_array), val) { ++ ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(var_array), fld, val) { + /* we can avoid the key_type check here, because we tested it in the other loop */ + switch (Z_TYPE_P(val)) { + case IS_STRING: + if (opt & PGSQL_DML_ESCAPE) { +- size_t new_len; +- char *tmp; +- tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); +- new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); ++ int error; ++ char *tmp = safe_emalloc(Z_STRLEN_P(val), 2, 1); ++ size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); ++ if (error) { ++ php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); ++ efree(tmp); ++ goto cleanup; ++ } + smart_str_appendc(&querystr, '\''); + smart_str_appendl(&querystr, tmp, new_len); + smart_str_appendc(&querystr, '\''); +@@ -6810,6 +6879,10 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, + } + if (opt & PGSQL_DML_ESCAPE) { + char *tmp = PGSQLescapeIdentifier(pg_link, ZSTR_VAL(fld), ZSTR_LEN(fld) + 1); ++ if (tmp == NULL) { ++ php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s'", ZSTR_VAL(fld)); ++ return -1; ++ } + smart_str_appends(querystr, tmp); + PGSQLfree(tmp); + } else { +@@ -6824,8 +6897,14 @@ static inline int build_assignment_string(PGconn *pg_link, smart_str *querystr, + switch (Z_TYPE_P(val)) { + case IS_STRING: + if (opt & PGSQL_DML_ESCAPE) { ++ int error; + char *tmp = (char *)safe_emalloc(Z_STRLEN_P(val), 2, 1); +- size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), NULL); ++ size_t new_len = PQescapeStringConn(pg_link, tmp, Z_STRVAL_P(val), Z_STRLEN_P(val), &error); ++ if (error) { ++ php_error_docref(NULL, E_NOTICE, "Failed to escape field '%s' value", ZSTR_VAL(fld)); ++ efree(tmp); ++ return -1; ++ } + smart_str_appendc(querystr, '\''); + smart_str_appendl(querystr, tmp, new_len); + smart_str_appendc(querystr, '\''); +@@ -6894,7 +6973,9 @@ PHP_PGSQL_API int php_pgsql_update(PGconn *pg_link, const char *table, zval *var + } + + smart_str_appends(&querystr, "UPDATE "); +- build_tablename(&querystr, pg_link, table); ++ if (build_tablename(&querystr, pg_link, table) == FAILURE) { ++ goto cleanup; ++ } + smart_str_appends(&querystr, " SET "); + + if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(var_array), 0, ",", 1, opt)) +@@ -6992,7 +7073,9 @@ PHP_PGSQL_API int php_pgsql_delete(PGconn *pg_link, const char *table, zval *ids + } + + smart_str_appends(&querystr, "DELETE FROM "); +- build_tablename(&querystr, pg_link, table); ++ if (build_tablename(&querystr, pg_link, table) == FAILURE) { ++ goto cleanup; ++ } + smart_str_appends(&querystr, " WHERE "); + + if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) +@@ -7130,7 +7213,9 @@ PHP_PGSQL_API int php_pgsql_result2array(PGresult *pg_result, zval *ret_array, l + } + + smart_str_appends(&querystr, "SELECT * FROM "); +- build_tablename(&querystr, pg_link, table); ++ if (build_tablename(&querystr, pg_link, table) == FAILURE) { ++ goto cleanup; ++ } + smart_str_appends(&querystr, " WHERE "); + + if (build_assignment_string(pg_link, &querystr, Z_ARRVAL_P(ids_array), 1, " AND ", sizeof(" AND ")-1, opt)) +diff --git a/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt +new file mode 100644 +index 00000000000..c1c5e05dce6 +--- /dev/null ++++ b/ext/pgsql/tests/ghsa-hrwm-9436-5mv3.phpt +@@ -0,0 +1,64 @@ ++--TEST-- ++#GHSA-hrwm-9436-5mv3: pgsql extension does not check for errors during escaping ++--EXTENSIONS-- ++pgsql ++--SKIPIF-- ++<?php include("skipif.inc"); ?> ++--FILE-- ++<?php ++ ++include 'config.inc'; ++define('FILE_NAME', __DIR__ . '/php.gif'); ++ ++$db = pg_connect($conn_str); ++pg_query($db, "DROP TABLE IF EXISTS ghsa_hrmw_9436_5mv3"); ++pg_query($db, "CREATE TABLE ghsa_hrmw_9436_5mv3 (bar text);"); ++ ++// pg_escape_literal/pg_escape_identifier ++ ++$invalid = "ABC\xff\x30';"; ++$flags = PGSQL_DML_NO_CONV | PGSQL_DML_ESCAPE; ++ ++var_dump(pg_insert($db, $invalid, ['bar' => 'test'])); // table name str escape in php_pgsql_meta_data ++var_dump(pg_insert($db, "$invalid.tbl", ['bar' => 'test'])); // schema name str escape in php_pgsql_meta_data ++var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid])); // converted value str escape in php_pgsql_convert ++var_dump(pg_insert($db, $invalid, [])); // ident escape in build_tablename ++var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', [$invalid => 'foo'], $flags)); // ident escape for field php_pgsql_insert ++var_dump(pg_insert($db, 'ghsa_hrmw_9436_5mv3', ['bar' => $invalid], $flags)); // str escape for field value in php_pgsql_insert ++var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], [$invalid => 'test'], $flags)); // ident escape in build_assignment_string ++var_dump(pg_update($db, 'ghsa_hrmw_9436_5mv3', ['bar' => 'val'], ['bar' => $invalid], $flags)); // invalid str escape in build_assignment_string ++var_dump(pg_escape_literal($db, $invalid)); // pg_escape_literal escape ++var_dump(pg_escape_identifier($db, $invalid)); // pg_escape_identifier escape ++ ++?> ++--EXPECTF-- ++ ++Warning: pg_insert(): Escaping table name 'ABC%s';' failed in %s on line %d ++bool(false) ++ ++Warning: pg_insert(): Escaping table namespace 'ABC%s';.tbl' failed in %s on line %d ++bool(false) ++ ++Notice: pg_insert(): String value escaping failed for PostgreSQL 'text' (bar) in %s on line %d ++bool(false) ++ ++Notice: pg_insert(): Failed to escape table name 'ABC%s';' in %s on line %d ++bool(false) ++ ++Notice: pg_insert(): Failed to escape field 'ABC%s';' in %s on line %d ++bool(false) ++ ++Notice: pg_insert(): Failed to escape field 'bar' value in %s on line %d ++bool(false) ++ ++Notice: pg_update(): Failed to escape field 'ABC%s';' in %s on line %d ++bool(false) ++ ++Notice: pg_update(): Failed to escape field 'bar' value in %s on line %d ++bool(false) ++ ++Warning: pg_escape_literal(): Failed to escape in %s on line %d ++bool(false) ++ ++Warning: pg_escape_identifier(): Failed to escape in %s on line %d ++bool(false) +-- +2.50.0 + +From d52bcc1e66edd421dfea1698b1f897ad26c5f15f Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 3 Jul 2025 09:32:25 +0200 +Subject: [PATCH 4/4] NEWS + +(cherry picked from commit 970548b94b7f23be32154d05a9545b10c98bfd62) +--- + NEWS | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/NEWS b/NEWS +index fda646c7010..a9dd716c003 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,20 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.1.33 ++ ++- PGSQL: ++ . Fixed GHSA-hrwm-9436-5mv3 (pgsql extension does not check for errors during ++ escaping). (CVE-2025-1735) (Jakub Zelenka) ++ ++- SOAP: ++ . Fixed GHSA-453j-q27h-5p8x (NULL Pointer Dereference in PHP SOAP Extension ++ via Large XML Namespace Prefix). (CVE-2025-6491) (Lekssays, nielsdos) ++ ++- Standard: ++ . Fixed GHSA-3cr5-j632-f35r (Null byte termination in hostnames). ++ (CVE-2025-1220) (Jakub Zelenka) ++ + Backported from 8.1.32 + + - LibXML: +-- +2.50.0 + diff --git a/php-cve-2025-1736.patch b/php-cve-2025-1736.patch new file mode 100644 index 0000000..eb33553 --- /dev/null +++ b/php-cve-2025-1736.patch @@ -0,0 +1,242 @@ +From 134f821622e2d2b68d66bea16e16c05b7b0f5114 Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Fri, 14 Feb 2025 19:17:22 +0100 +Subject: [PATCH 04/11] Fix GHSA-hgf5-96fm-v528: http user header check of crlf + +(cherry picked from commit 41d49abbd99dab06cdae4834db664435f8177174) +(cherry picked from commit 8f65ef50929f6781f4973325f9b619f02cce19d8) +--- + ext/standard/http_fopen_wrapper.c | 2 +- + .../tests/http/ghsa-hgf5-96fm-v528-001.phpt | 65 +++++++++++++++++++ + .../tests/http/ghsa-hgf5-96fm-v528-002.phpt | 62 ++++++++++++++++++ + .../tests/http/ghsa-hgf5-96fm-v528-003.phpt | 64 ++++++++++++++++++ + 4 files changed, 192 insertions(+), 1 deletion(-) + create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt + create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt + create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt + +diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c +index b64a7c95446..46f7c7ebcee 100644 +--- a/ext/standard/http_fopen_wrapper.c ++++ b/ext/standard/http_fopen_wrapper.c +@@ -109,7 +109,7 @@ static inline void strip_header(char *header_bag, char *lc_header_bag, + static zend_bool check_has_header(const char *headers, const char *header) { + const char *s = headers; + while ((s = strstr(s, header))) { +- if (s == headers || *(s-1) == '\n') { ++ if (s == headers || (*(s-1) == '\n' && *(s-2) == '\r')) { + return 1; + } + s++; +diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt +new file mode 100644 +index 00000000000..c8dcd47a4a4 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt +@@ -0,0 +1,65 @@ ++--TEST-- ++GHSA-hgf5-96fm-v528: Stream HTTP wrapper header check might omit basic auth header (incorrect inside pos) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ $result = fread($conn, 1024); ++ $encoded_result = base64_encode($result); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n"); ++ ++CODE; ++ ++$clientCode = <<<'CODE' ++ $opts = [ ++ "http" => [ ++ "method" => "GET", ++ "header" => "Cookie: foo=bar\nauthorization:x\r\n" ++ ] ++ ]; ++ $ctx = stream_context_create($opts); ++ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx)))); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++array(7) { ++ [0]=> ++ string(14) "GET / HTTP/1.%d" ++ [1]=> ++ string(33) "Authorization: Basic dXNlcjpwd2Q=" ++ [2]=> ++ string(21) "Host: 127.0.0.1:%d" ++ [3]=> ++ string(17) "Connection: close" ++ [4]=> ++ string(31) "Cookie: foo=bar ++authorization:x" ++ [5]=> ++ string(0) "" ++ [6]=> ++ string(0) "" ++} ++array(2) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++ [1]=> ++ string(38) "Content-Type: text/html; charset=utf-8" ++} +diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt +new file mode 100644 +index 00000000000..ca8f75f0327 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt +@@ -0,0 +1,62 @@ ++--TEST-- ++GHSA-hgf5-96fm-v528: Header parser of http stream wrapper does not handle folded headers (correct start pos) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ $result = fread($conn, 1024); ++ $encoded_result = base64_encode($result); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n"); ++ ++CODE; ++ ++$clientCode = <<<'CODE' ++ $opts = [ ++ "http" => [ ++ "method" => "GET", ++ "header" => "Authorization: Bearer x\r\n" ++ ] ++ ]; ++ $ctx = stream_context_create($opts); ++ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx)))); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++array(6) { ++ [0]=> ++ string(14) "GET / HTTP/1.%d" ++ [1]=> ++ string(21) "Host: 127.0.0.1:%d" ++ [2]=> ++ string(17) "Connection: close" ++ [3]=> ++ string(23) "Authorization: Bearer x" ++ [4]=> ++ string(0) "" ++ [5]=> ++ string(0) "" ++} ++array(2) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++ [1]=> ++ string(38) "Content-Type: text/html; charset=utf-8" ++} +diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt +new file mode 100644 +index 00000000000..4cfbc7ee804 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt +@@ -0,0 +1,64 @@ ++--TEST-- ++GHSA-hgf5-96fm-v528: Header parser of http stream wrapper does not handle folded headers (correct middle pos) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++ $ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ $result = fread($conn, 1024); ++ $encoded_result = base64_encode($result); ++ ++ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n"); ++ ++CODE; ++ ++$clientCode = <<<'CODE' ++ $opts = [ ++ "http" => [ ++ "method" => "GET", ++ "header" => "Cookie: x=y\r\nAuthorization: Bearer x\r\n" ++ ] ++ ]; ++ $ctx = stream_context_create($opts); ++ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx)))); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++array(7) { ++ [0]=> ++ string(14) "GET / HTTP/1.%d" ++ [1]=> ++ string(21) "Host: 127.0.0.1:%d" ++ [2]=> ++ string(17) "Connection: close" ++ [3]=> ++ string(11) "Cookie: x=y" ++ [4]=> ++ string(23) "Authorization: Bearer x" ++ [5]=> ++ string(0) "" ++ [6]=> ++ string(0) "" ++} ++array(2) { ++ [0]=> ++ string(15) "HTTP/1.0 200 Ok" ++ [1]=> ++ string(38) "Content-Type: text/html; charset=utf-8" ++} +-- +2.48.1 + diff --git a/php-cve-2025-1861.patch b/php-cve-2025-1861.patch new file mode 100644 index 0000000..b31e74f --- /dev/null +++ b/php-cve-2025-1861.patch @@ -0,0 +1,349 @@ +From 5418040dcaaca46965ed6f8a4ad1541709c32e9f Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Tue, 4 Mar 2025 09:01:34 +0100 +Subject: [PATCH 03/11] Fix GHSA-52jp-hrpf-2jff: http redirect location + truncation + +It converts the allocation of location to be on heap instead of stack +and errors if the location length is greater than 8086 bytes. + +(cherry picked from commit ac1a054bb3eb5994a199e8b18cca28cbabf5943e) +(cherry picked from commit adc7e9f20c9a9aab9cd23ca47ec3fb96287898ae) +--- + ext/standard/http_fopen_wrapper.c | 87 ++++++++++++------- + .../tests/http/ghsa-52jp-hrpf-2jff-001.phpt | 58 +++++++++++++ + .../tests/http/ghsa-52jp-hrpf-2jff-002.phpt | 55 ++++++++++++ + 3 files changed, 168 insertions(+), 32 deletions(-) + create mode 100644 ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt + create mode 100644 ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt + +diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c +index 071d6a4d119..b64a7c95446 100644 +--- a/ext/standard/http_fopen_wrapper.c ++++ b/ext/standard/http_fopen_wrapper.c +@@ -69,15 +69,16 @@ + + #include "php_fopen_wrappers.h" + +-#define HTTP_HEADER_BLOCK_SIZE 1024 +-#define PHP_URL_REDIRECT_MAX 20 +-#define HTTP_HEADER_USER_AGENT 1 +-#define HTTP_HEADER_HOST 2 +-#define HTTP_HEADER_AUTH 4 +-#define HTTP_HEADER_FROM 8 +-#define HTTP_HEADER_CONTENT_LENGTH 16 +-#define HTTP_HEADER_TYPE 32 +-#define HTTP_HEADER_CONNECTION 64 ++#define HTTP_HEADER_BLOCK_SIZE 1024 ++#define HTTP_HEADER_MAX_LOCATION_SIZE 8182 /* 8192 - 10 (size of "Location: ") */ ++#define PHP_URL_REDIRECT_MAX 20 ++#define HTTP_HEADER_USER_AGENT 1 ++#define HTTP_HEADER_HOST 2 ++#define HTTP_HEADER_AUTH 4 ++#define HTTP_HEADER_FROM 8 ++#define HTTP_HEADER_CONTENT_LENGTH 16 ++#define HTTP_HEADER_TYPE 32 ++#define HTTP_HEADER_CONNECTION 64 + + #define HTTP_WRAPPER_HEADER_INIT 1 + #define HTTP_WRAPPER_REDIRECTED 2 +@@ -121,17 +122,15 @@ typedef struct _php_stream_http_response_header_info { + size_t file_size; + zend_bool error; + zend_bool follow_location; +- char location[HTTP_HEADER_BLOCK_SIZE]; ++ char *location; ++ size_t location_len; + } php_stream_http_response_header_info; + + static void php_stream_http_response_header_info_init( + php_stream_http_response_header_info *header_info) + { +- header_info->transfer_encoding = NULL; +- header_info->file_size = 0; +- header_info->error = 0; ++ memset(header_info, 0, sizeof(php_stream_http_response_header_info)); + header_info->follow_location = 1; +- header_info->location[0] = '\0'; + } + + /* Trim white spaces from response header line and update its length */ +@@ -258,7 +257,22 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w + * RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */ + header_info->follow_location = 0; + } +- strlcpy(header_info->location, last_header_value, sizeof(header_info->location)); ++ size_t last_header_value_len = strlen(last_header_value); ++ if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) { ++ header_info->error = 1; ++ php_stream_wrapper_log_error(wrapper, options, ++ "HTTP Location header size is over the limit of %d bytes", ++ HTTP_HEADER_MAX_LOCATION_SIZE); ++ zend_string_efree(last_header_line_str); ++ return NULL; ++ } ++ if (header_info->location_len == 0) { ++ header_info->location = emalloc(last_header_value_len + 1); ++ } else if (header_info->location_len <= last_header_value_len) { ++ header_info->location = erealloc(header_info->location, last_header_value_len + 1); ++ } ++ header_info->location_len = last_header_value_len; ++ memcpy(header_info->location, last_header_value, last_header_value_len + 1); + } else if (!strncasecmp(last_header_line, "Content-Type:", sizeof("Content-Type:")-1)) { + php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, last_header_value, 0); + } else if (!strncasecmp(last_header_line, "Content-Length:", sizeof("Content-Length:")-1)) { +@@ -541,6 +555,8 @@ finish: + } + } + ++ php_stream_http_response_header_info_init(&header_info); ++ + if (stream == NULL) + goto out; + +@@ -918,8 +934,6 @@ finish: + } + } + +- php_stream_http_response_header_info_init(&header_info); +- + /* read past HTTP headers */ + while (!php_stream_eof(stream)) { + size_t http_header_line_length; +@@ -989,12 +1003,12 @@ finish: + last_header_line_str, NULL, NULL, response_code, response_header, &header_info); + } + +- if (!reqok || (header_info.location[0] != '\0' && header_info.follow_location)) { ++ if (!reqok || (header_info.location != NULL && header_info.follow_location)) { + if (!header_info.follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) { + goto out; + } + +- if (header_info.location[0] != '\0') ++ if (header_info.location != NULL) + php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, header_info.location, 0); + + php_stream_close(stream); +@@ -1005,18 +1019,17 @@ finish: + header_info.transfer_encoding = NULL; + } + +- if (header_info.location[0] != '\0') { ++ if (header_info.location != NULL) { + +- char new_path[HTTP_HEADER_BLOCK_SIZE]; +- char loc_path[HTTP_HEADER_BLOCK_SIZE]; ++ char *new_path = NULL; + +- *new_path='\0'; + if (strlen(header_info.location) < 8 || + (strncasecmp(header_info.location, "http://", sizeof("http://")-1) && + strncasecmp(header_info.location, "https://", sizeof("https://")-1) && + strncasecmp(header_info.location, "ftp://", sizeof("ftp://")-1) && + strncasecmp(header_info.location, "ftps://", sizeof("ftps://")-1))) + { ++ char *loc_path = NULL; + if (*header_info.location != '/') { + if (*(header_info.location+1) != '\0' && resource->path) { + char *s = strrchr(ZSTR_VAL(resource->path), '/'); +@@ -1034,31 +1047,35 @@ finish: + if (resource->path && + ZSTR_VAL(resource->path)[0] == '/' && + ZSTR_VAL(resource->path)[1] == '\0') { +- snprintf(loc_path, sizeof(loc_path) - 1, "%s%s", +- ZSTR_VAL(resource->path), header_info.location); ++ spprintf(&loc_path, 0, "%s%s", ZSTR_VAL(resource->path), header_info.location); + } else { +- snprintf(loc_path, sizeof(loc_path) - 1, "%s/%s", +- ZSTR_VAL(resource->path), header_info.location); ++ spprintf(&loc_path, 0, "%s/%s", ZSTR_VAL(resource->path), header_info.location); + } + } else { +- snprintf(loc_path, sizeof(loc_path) - 1, "/%s", header_info.location); ++ spprintf(&loc_path, 0, "/%s", header_info.location); + } + } else { +- strlcpy(loc_path, header_info.location, sizeof(loc_path)); ++ loc_path = header_info.location; ++ header_info.location = NULL; + } + if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) { +- snprintf(new_path, sizeof(new_path) - 1, "%s://%s:%d%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), resource->port, loc_path); ++ spprintf(&new_path, 0, "%s://%s:%d%s", ZSTR_VAL(resource->scheme), ++ ZSTR_VAL(resource->host), resource->port, loc_path); + } else { +- snprintf(new_path, sizeof(new_path) - 1, "%s://%s%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), loc_path); ++ spprintf(&new_path, 0, "%s://%s%s", ZSTR_VAL(resource->scheme), ++ ZSTR_VAL(resource->host), loc_path); + } ++ efree(loc_path); + } else { +- strlcpy(new_path, header_info.location, sizeof(new_path)); ++ new_path = header_info.location; ++ header_info.location = NULL; + } + + php_url_free(resource); + /* check for invalid redirection URLs */ + if ((resource = php_url_parse(new_path)) == NULL) { + php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); ++ efree(new_path); + goto out; + } + +@@ -1070,6 +1087,7 @@ finish: + while (s < e) { \ + if (iscntrl(*s)) { \ + php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \ ++ efree(new_path); \ + goto out; \ + } \ + s++; \ +@@ -1085,6 +1103,7 @@ finish: + stream = php_stream_url_wrap_http_ex( + wrapper, new_path, mode, options, opened_path, context, + --redirect_max, HTTP_WRAPPER_REDIRECTED, response_header STREAMS_CC); ++ efree(new_path); + } else { + php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line); + } +@@ -1097,6 +1116,10 @@ out: + efree(http_header_line); + } + ++ if (header_info.location != NULL) { ++ efree(header_info.location); ++ } ++ + if (resource) { + php_url_free(resource); + } +diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt +new file mode 100644 +index 00000000000..46d77ec4aff +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt +@@ -0,0 +1,58 @@ ++--TEST-- ++GHSA-52jp-hrpf-2jff: HTTP stream wrapper truncate redirect location to 1024 bytes (success) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++$ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ $loc = str_repeat("y", 8000); ++ fwrite($conn, "HTTP/1.0 301 Ok\r\nContent-Type: text/html;\r\nLocation: $loc\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ case STREAM_NOTIFY_REDIRECTED: ++ echo "Redirected: "; ++ var_dump($message); ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++Found the mime-type: text/html; ++Redirected: string(8000) "%s" ++ ++Warning: file_get_contents(http://127.0.0.1:%d): failed to open stream: %s ++string(0) "" ++array(3) { ++ [0]=> ++ string(15) "HTTP/1.0 301 Ok" ++ [1]=> ++ string(24) "Content-Type: text/html;" ++ [2]=> ++ string(8010) "Location: %s" ++} +diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt +new file mode 100644 +index 00000000000..d25c89d06e5 +--- /dev/null ++++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt +@@ -0,0 +1,55 @@ ++--TEST-- ++GHSA-52jp-hrpf-2jff: HTTP stream wrapper truncate redirect location to 1024 bytes (over limit) ++--FILE-- ++<?php ++$serverCode = <<<'CODE' ++$ctxt = stream_context_create([ ++ "socket" => [ ++ "tcp_nodelay" => true ++ ] ++ ]); ++ ++ $server = stream_socket_server( ++ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt); ++ phpt_notify_server_start($server); ++ ++ $conn = stream_socket_accept($server); ++ ++ phpt_notify(WORKER_DEFAULT_NAME, "server-accepted"); ++ ++ $loc = str_repeat("y", 9000); ++ fwrite($conn, "HTTP/1.0 301 Ok\r\nContent-Type: text/html;\r\nLocation: $loc\r\n\r\nbody\r\n"); ++CODE; ++ ++$clientCode = <<<'CODE' ++ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) { ++ switch($notification_code) { ++ case STREAM_NOTIFY_MIME_TYPE_IS: ++ echo "Found the mime-type: ", $message, PHP_EOL; ++ break; ++ case STREAM_NOTIFY_REDIRECTED: ++ echo "Redirected: "; ++ var_dump($message); ++ } ++ } ++ ++ $ctx = stream_context_create(); ++ stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); ++ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); ++ var_dump($http_response_header); ++CODE; ++ ++include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); ++ServerClientTestCase::getInstance()->run($clientCode, $serverCode); ++?> ++--EXPECTF-- ++Found the mime-type: text/html; ++ ++Warning: file_get_contents(http://127.0.0.1:%d): failed to open stream: HTTP Location header size is over the limit of 8182 bytes in %s ++string(0) "" ++array(2) { ++ [0]=> ++ string(15) "HTTP/1.0 301 Ok" ++ [1]=> ++ string(24) "Content-Type: text/html;" ++} +-- +2.48.1 + diff --git a/php-cve-2025-6491.patch b/php-cve-2025-6491.patch new file mode 100644 index 0000000..ec8ce61 --- /dev/null +++ b/php-cve-2025-6491.patch @@ -0,0 +1,103 @@ +From c13a3b2a3710c66231f0cad16ff74ef75c8672a7 Mon Sep 17 00:00:00 2001 +From: Ahmed Lekssays <lekssaysahmed@gmail.com> +Date: Tue, 3 Jun 2025 09:00:55 +0000 +Subject: [PATCH 1/4] Fix GHSA-453j-q27h-5p8x + +Libxml versions prior to 2.13 cannot correctly handle a call to +xmlNodeSetName() with a name longer than 2G. It will leave the node +object in an invalid state with a NULL name. This later causes a NULL +pointer dereference when using the name during message serialization. + +To solve this, implement a workaround that resets the name to the +sentinel name if this situation arises. + +Versions of libxml of 2.13 and higher are not affected. + +This can be exploited if a SoapVar is created with a fully qualified +name that is longer than 2G. This would be possible if some application +code uses a namespace prefix from an untrusted source like from a remote +SOAP service. + +Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +(cherry picked from commit 9cb3d8d200f0c822b17bda35a2a67a97b039d3e1) +(cherry picked from commit 1b7410a57f8a5fd1dd43854bcf7b9200517c9fd2) +--- + ext/soap/soap.c | 6 ++-- + ext/soap/tests/soap_qname_crash.phpt | 48 ++++++++++++++++++++++++++++ + 2 files changed, 52 insertions(+), 2 deletions(-) + create mode 100644 ext/soap/tests/soap_qname_crash.phpt + +diff --git a/ext/soap/soap.c b/ext/soap/soap.c +index 7429aebbf70..94f1db526c6 100644 +--- a/ext/soap/soap.c ++++ b/ext/soap/soap.c +@@ -4457,8 +4457,10 @@ static xmlNodePtr serialize_zval(zval *val, sdlParamPtr param, char *paramName, + } + xmlParam = master_to_xml(enc, val, style, parent); + zval_ptr_dtor(&defval); +- if (!strcmp((char*)xmlParam->name, "BOGUS")) { +- xmlNodeSetName(xmlParam, BAD_CAST(paramName)); ++ if (xmlParam != NULL) { ++ if (xmlParam->name == NULL || strcmp((char*)xmlParam->name, "BOGUS") == 0) { ++ xmlNodeSetName(xmlParam, BAD_CAST(paramName)); ++ } + } + return xmlParam; + } +diff --git a/ext/soap/tests/soap_qname_crash.phpt b/ext/soap/tests/soap_qname_crash.phpt +new file mode 100644 +index 00000000000..7a1bf026022 +--- /dev/null ++++ b/ext/soap/tests/soap_qname_crash.phpt +@@ -0,0 +1,48 @@ ++--TEST-- ++Test SoapClient with excessively large QName prefix in SoapVar ++--EXTENSIONS-- ++soap ++--SKIPIF-- ++<?php ++if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ++?> ++--INI-- ++memory_limit=8G ++--FILE-- ++<?php ++ ++class TestSoapClient extends SoapClient { ++ public function __doRequest( ++ $request, ++ $location, ++ $action, ++ $version, ++ $one_way = false ++ ): ?string { ++ die($request); ++ } ++} ++ ++$prefix = str_repeat("A", 2 * 1024 * 1024 * 1024); ++$qname = "{$prefix}:tag"; ++ ++echo "Attempting to create SoapVar with very large QName\n"; ++ ++$var = new SoapVar("value", XSD_QNAME, null, null, $qname); ++ ++echo "Attempting encoding\n"; ++ ++$options = [ ++ 'location' => 'http://127.0.0.1/', ++ 'uri' => 'urn:dummy', ++ 'trace' => 1, ++ 'exceptions' => true, ++]; ++$client = new TestSoapClient(null, $options); ++$client->__soapCall("DummyFunction", [$var]); ++?> ++--EXPECT-- ++Attempting to create SoapVar with very large QName ++Attempting encoding ++<?xml version="1.0" encoding="UTF-8"?> ++<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:dummy" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:DummyFunction><param0 xsi:type="xsd:QName">value</param0></ns1:DummyFunction></SOAP-ENV:Body></SOAP-ENV:Envelope> +-- +2.50.0 + diff --git a/php-fpm.service b/php-fpm.service index 687dfc0..0712a11 100644 --- a/php-fpm.service +++ b/php-fpm.service @@ -4,7 +4,7 @@ [Unit] Description=The PHP FastCGI Process Manager -After=syslog.target network.target +After=network.target [Service] Type=notify diff --git a/php-ghsa-4w77-75f9-2c8w.patch b/php-ghsa-4w77-75f9-2c8w.patch new file mode 100644 index 0000000..4886973 --- /dev/null +++ b/php-ghsa-4w77-75f9-2c8w.patch @@ -0,0 +1,135 @@ +From 56488a8a4ec68e58eecc9e78dd75e41adf56984c Mon Sep 17 00:00:00 2001 +From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> +Date: Sat, 9 Nov 2024 15:29:52 +0100 +Subject: [PATCH 6/7] Fix GHSA-4w77-75f9-2c8w + +(cherry picked from commit 7dd336ae838bbf2c62dc47e3c900d657d3534c02) +(cherry picked from commit 462092a48aa0dbad24d9fa8a4a9d418faa14d309) +--- + sapi/cli/php_cli_server.c | 6 +--- + sapi/cli/tests/ghsa-4w77-75f9-2c8w.phpt | 41 +++++++++++++++++++++++++ + 2 files changed, 42 insertions(+), 5 deletions(-) + create mode 100644 sapi/cli/tests/ghsa-4w77-75f9-2c8w.phpt + +diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c +index c3097861e3f..8717dc57418 100644 +--- a/sapi/cli/php_cli_server.c ++++ b/sapi/cli/php_cli_server.c +@@ -1923,8 +1923,6 @@ static size_t php_cli_server_client_send_through(php_cli_server_client *client, + + static void php_cli_server_client_populate_request_info(const php_cli_server_client *client, sapi_request_info *request_info) /* {{{ */ + { +- char *val; +- + request_info->request_method = php_http_method_str(client->request.request_method); + request_info->proto_num = client->request.protocol_version; + request_info->request_uri = client->request.request_uri; +@@ -1932,9 +1930,7 @@ static void php_cli_server_client_populate_request_info(const php_cli_server_cli + request_info->query_string = client->request.query_string; + request_info->content_length = client->request.content_len; + request_info->auth_user = request_info->auth_password = request_info->auth_digest = NULL; +- if (NULL != (val = zend_hash_str_find_ptr(&client->request.headers, "content-type", sizeof("content-type")-1))) { +- request_info->content_type = val; +- } ++ request_info->content_type = zend_hash_str_find_ptr(&client->request.headers, "content-type", sizeof("content-type")-1); + } /* }}} */ + + static void destroy_request_info(sapi_request_info *request_info) /* {{{ */ +diff --git a/sapi/cli/tests/ghsa-4w77-75f9-2c8w.phpt b/sapi/cli/tests/ghsa-4w77-75f9-2c8w.phpt +new file mode 100644 +index 00000000000..80944c3d14f +--- /dev/null ++++ b/sapi/cli/tests/ghsa-4w77-75f9-2c8w.phpt +@@ -0,0 +1,41 @@ ++--TEST-- ++GHSA-4w77-75f9-2c8w (Heap-Use-After-Free in sapi_read_post_data Processing in CLI SAPI Interface) ++--INI-- ++allow_url_fopen=1 ++--SKIPIF-- ++<?php ++include "skipif.inc"; ++?> ++--FILE-- ++<?php ++include "php_cli_server.inc"; ++ ++$serverCode = <<<'CODE' ++var_dump(file_get_contents('php://input')); ++CODE; ++ ++php_cli_server_start($serverCode, null, []); ++ ++$options = [ ++ "http" => [ ++ "method" => "POST", ++ "header" => "Content-Type: application/x-www-form-urlencoded", ++ "content" => "AAAAA", ++ ], ++]; ++$context = stream_context_create($options); ++ ++echo file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS . "/", false, $context); ++ ++$options = [ ++ "http" => [ ++ "method" => "POST", ++ ], ++]; ++$context = stream_context_create($options); ++ ++echo file_get_contents("http://" . PHP_CLI_SERVER_ADDRESS . "/", false, $context); ++?> ++--EXPECT-- ++string(5) "AAAAA" ++string(0) "" +-- +2.47.0 + +From d8d682d3d6a4d027771806c8fc77128cae078d29 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Fri, 22 Nov 2024 08:58:10 +0100 +Subject: [PATCH 7/7] NEWS for 8.1.31 backports + +(cherry picked from commit 22bdb43da0ecd6e72d63b63aa6c1f3a25d1bca3a) +--- + NEWS | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/NEWS b/NEWS +index 62616d6312d..f600d6aea65 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,30 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.1.31 ++ ++- CLI: ++ . Fixed bug GHSA-4w77-75f9-2c8w (Heap-Use-After-Free in sapi_read_post_data ++ Processing in CLI SAPI Interface). (nielsdos) ++ ++- LDAP: ++ . Fixed bug GHSA-g665-fm4p-vhff (OOB access in ldap_escape). (CVE-2024-8932) ++ (nielsdos) ++ ++- PDO DBLIB: ++ . Fixed bug GHSA-5hqh-c84r-qjcv (Integer overflow in the dblib quoter causing ++ OOB writes). (CVE-2024-11236) (nielsdos) ++ ++- PDO Firebird: ++ . Fixed bug GHSA-5hqh-c84r-qjcv (Integer overflow in the firebird quoter ++ causing OOB writes). (CVE-2024-11236) (nielsdos) ++ ++- Streams: ++ . Fixed bug GHSA-c5f2-jwm7-mmq2 (Configuring a proxy in a stream context ++ might allow for CRLF injection in URIs). (CVE-2024-11234) (Jakub Zelenka) ++ . Fixed bug GHSA-r977-prxv-hc43 (Single byte overread with ++ convert.quoted-printable-decode filter). (CVE-2024-11233) (nielsdos) ++ + Backported from 8.1.30 + + - CGI: +-- +2.47.0 + diff --git a/php-keyring.gpg b/php-keyring.gpg index 870d816..0774fa8 100644 --- a/php-keyring.gpg +++ b/php-keyring.gpg @@ -1,415 +1,780 @@ -----BEGIN PGP PUBLIC KEY BLOCK----- -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 -bh57GXdFtudEaKvQbGW9yalYwuj7Yip5XJGttg== -=XZOV +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 +5zO4pPQ1rIGTN4CBa7QcJH3fc3i9rYMYAtuVlpCUmDMEYHHgIBYJKwYBBAHaRw8B +AQdAOm0RDYTmM6omlqoTx6Wy65agVJ4q8EPGczNnYOrHyo20HUpha3ViIFplbGVu +a2EgPGJ1a2thQHBocC5uZXQ+iJMEExYIADsCGwMFCwkIBwIGFQoJCAsCBBYCAwEC +HgECF4AWIQTCjZN1dWA+tKu3JYYcB3ncXAqd5AUCYHHi+QIZAQAKCRAcB3ncXAqd +5MZdAP0eAbdHgjHqKYol3QaXRhErAvvLHPSEXN5cAImF3zIICgD+NJZZMQDXd1OO +KDt+YLiIqIq2ibRhOP5mZryPCGkq9QG0J0pha3ViIFplbGVua2EgPGpha3ViLm9w +ZW5zc2xAZ21haWwuY29tPoiQBBMWCAA4FiEEwo2TdXVgPrSrtyWGHAd53FwKneQF +AmBx4owCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQHAd53FwKneS0NwEA +t0IRpoCN/JNwg1TIseybpmC65nSzYVyX10xe4Ji50dcBAI6TYA+47z6F4IVRg2c8 +Vtg1xktot7b/tKn6hgdv59sLtCdKYWt1YiBaZWxlbmthIDxqYWt1Yi56ZWxlbmth +QGdtYWlsLmNvbT6IkAQTFggAOBYhBMKNk3V1YD60q7clhhwHedxcCp3kBQJgceAg +AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEBwHedxcCp3k+3MBAKuK15Xy +Hw+sdFATFuW9vN+lXqHnYid2jRzQUoh5wG/FAQDcBXQgXRfe72XRoxY4AE3E4onG +MfzW3kbDKDVF7o4zDrg4BGBx4CASCisGAQQBl1UBBQEBB0Bm2FjWsSDUwL5nbAht +l+rwnGLKbWiVYrwHQocgKFj6GwMBCAeIeAQYFggAIBYhBMKNk3V1YD60q7clhhwH +edxcCp3kBQJgceAgAhsMAAoJEBwHedxcCp3k8ZwA/jLRHUBKIeRAxeGJhoqJRr2o +TxrP887FuX9/ikSX21bcAQD90AB2fu6dpAkOFyZzX9UdE7QxtBhHVWRx3DRr4P/c +DpgzBGYgDVwWCSsGAQQB2kcPAQEHQET/J56AhF3VoW7XvfydS3kGVTEN7zzOwA06 +KmRjb1w3tCZDYWx2aW4gQnVja2xleSAoUEhQKSA8Y2FsdmluYkBwaHAubmV0PoiT +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 +=ilrq -----END PGP PUBLIC KEY BLOCK----- @@ -49,8 +49,10 @@ %global mysql_sock %(mysql_config --socket 2>/dev/null || echo /var/lib/mysql/mysql.sock) -%global oraclever 21.5 -%global oraclelib 21.1 +%global oraclever 23.8 +%global oraclemax 24 +%global oraclelib 23.1 +%global oracledir 23 # Build for LiteSpeed Web Server (LSAPI) %global with_lsws 1 @@ -66,12 +68,18 @@ # Optional components; pass "--with mssql" etc to rpmbuild. %global with_oci8 %{?_with_oci8:1}%{!?_with_oci8:0} %global with_imap 1 -%global with_interbase 1 %global with_freetds 1 %global with_tidy 1 %global with_sqlite3 1 %global with_enchant 1 +# Build firebird extensions, you can disable using --without firebird +%if 0%{?rhel} == 10 +%bcond_with firebird +%else +%bcond_without firebird +%endif + %if 0%{?fedora} >= 27 || 0%{?rhel} >= 8 %global with_libpcre 1 %else @@ -104,13 +112,13 @@ #global gh_date 20190612 %global gh_owner php %global gh_project php-src -%global upver 7.4.28 +%global upver 7.4.33 #global rcver RC1 Summary: PHP scripting language for creating dynamic web sites Name: %{?scl_prefix}php Version: %{upver}%{?rcver:~%{rcver}}%{?gh_date:.%{gh_date}} -Release: 2%{?dist} +Release: 24%{?dist} # All files licensed under PHP version 3.01, except # Zend is licensed under Zend # TSRM is licensed under BSD @@ -151,7 +159,11 @@ Patch1: php-7.4.0-httpd.patch Patch5: php-7.2.0-includedir.patch Patch6: php-7.4.0-embed.patch Patch8: php-7.2.0-libdb.patch -Patch9: php-7.0.7-curl.patch +Patch10: php-7.4.33-gcc14.patch +# For recent ICU from 8.2 +Patch11: php-7.4.33-icu.patch +# Fix strict prototypes from 8.1 +Patch12: php-7.4.33-proto.patch # Functional changes Patch42: php-7.3.3-systzdata-v19.patch @@ -175,10 +187,44 @@ Patch91: php-7.2.0-oci8conf.patch # Upstream fixes (100+) # Security fixes (200+) +Patch200: php-cve-2022-31631.patch +Patch201: php-cve-2023-0567.patch +Patch202: php-cve-2023-0568.patch +Patch203: php-cve-2023-0662.patch +Patch204: php-cve-2023-3247.patch +Patch205: php-cve-2023-3823.patch +Patch206: php-cve-2023-3824.patch +Patch207: php-cve-2024-2756.patch +Patch208: php-cve-2024-3096.patch +Patch209: php-cve-2024-5458.patch +Patch210: php-cve-2024-8925.patch +Patch211: php-cve-2024-8926.patch +Patch212: php-cve-2024-8927.patch +Patch213: php-cve-2024-9026.patch +Patch214: php-cve-2024-11236.patch +Patch215: php-cve-2024-11234.patch +Patch216: php-cve-2024-8932.patch +Patch217: php-cve-2024-11233.patch +Patch218: php-ghsa-4w77-75f9-2c8w.patch +Patch219: php-cve-2024-8929.patch +Patch220: php-cve-2025-1217.patch +Patch221: php-cve-2025-1734.patch +Patch222: php-cve-2025-1861.patch +Patch223: php-cve-2025-1736.patch +Patch224: php-cve-2025-1219.patch +Patch225: php-cve-2025-6491.patch +Patch226: php-cve-2025-1220.patch +Patch227: php-cve-2025-1735.patch # Fixes for tests (300+) # Factory is droped from system tzdata Patch300: php-7.0.10-datetests.patch +# Make test slower +Patch301: php-7.4.33-tests.patch +# For zlib-ng +Patch302: php-7.4.33-zlib-tests.patch +# for pcre2 10.45 +Patch303: php-7.4.33-pcretests.patch # WIP @@ -215,6 +261,9 @@ BuildRequires: libtool BuildRequires: libtool-ltdl-devel %if %{with_dtrace} BuildRequires: %{?dtsprefix}systemtap-sdt-devel +%if 0%{?fedora} >= 41 +BuildRequires: %{?dtsprefix}systemtap-sdt-dtrace +%endif %endif %if 0%{?gh_date} BuildRequires: bison @@ -300,7 +349,6 @@ Group: Development/Languages Summary: PHP FastCGI Process Manager BuildRequires: libacl-devel BuildRequires: pkgconfig(libsystemd) >= 209 -Requires(pre): %{_root_sbindir}/useradd Requires: %{?scl_prefix}php-common%{?_isa} = %{version}-%{release} %{?systemd_requires} # This is actually needed for the %%triggerun script but Requires(triggerun) @@ -313,6 +361,8 @@ Requires(pre): httpd-filesystem # For php.conf in /etc/httpd/conf.d # and version 2.4.10 for proxy support in SetHandler Requires: httpd-filesystem >= 2.4.10 +%else +Requires(pre): %{_root_sbindir}/useradd %endif %description fpm @@ -352,6 +402,13 @@ Summary: Common files for PHP # fileinfo is licensed under PHP version 3.0 # regex, libmagic are licensed under BSD License: PHP and BSD + +Requires: tzdata +%if %{with_libpcre} +%global pcre2_buildver %(pkg-config --silence-errors --modversion libpcre2-8 2>/dev/null || echo 10.30) +Requires: pcre2%{?_isa} >= %{pcre2_buildver} +%endif + # ABI/API check - Arch specific Provides: %{?scl_prefix}php(api) = %{apiver}-%{__isa_bits} Provides: %{?scl_prefix}php(zend-abi) = %{zendver}-%{__isa_bits} @@ -580,7 +637,7 @@ BuildRequires: pkgconfig(libxml-2.0) The %{?scl_prefix}php-soap package contains a dynamic shared object that will add support to PHP for using the SOAP web services protocol. -%if %{with_interbase} +%if %{with firebird} %package pdo-firebird Summary: PDO driver for Interbase/Firebird databases Group: Development/Languages @@ -612,7 +669,7 @@ Summary: A module for PHP applications that use OCI8 databases Group: Development/Languages # All files licensed under PHP version 3.01 License: PHP -BuildRequires: oracle-instantclient-devel >= %{oraclever} +BuildRequires: (oracle-instantclient-devel >= %{oraclever} with oracle-instantclient-devel < %{oraclemax}) Requires: %{?scl_prefix}php-pdo%{?_isa} = %{version}-%{release} Provides: %{?scl_prefix}php_database Provides: %{?scl_prefix}php-pdo_oci @@ -621,8 +678,6 @@ Obsoletes: %{?scl_prefix}php-pecl-oci8 <= %{oci8ver} Conflicts: %{?scl_prefix}php-pecl-oci8 > %{oci8ver} Provides: %{?scl_prefix}php-pecl(oci8) = %{oci8ver} Provides: %{?scl_prefix}php-pecl(oci8)%{?_isa} = %{oci8ver} -# Should requires libclntsh.so.18.3, but it's not provided by Oracle RPM. -AutoReq: 0 %description oci8 The %{?scl_prefix}php-oci8 packages provides the OCI8 extension version %{oci8ver} @@ -632,13 +687,9 @@ The extension is linked with Oracle client libraries %{oraclever} (Oracle Instant Client). For details, see Oracle's note "Oracle Client / Server Interoperability Support" (ID 207303.1). -You must install libclntsh.so.%{oraclelib} to use this package, provided -in the database installation, or in the free Oracle Instant Client -available from Oracle. - -Notice: -- %{?scl_prefix}php-oci8 provides oci8 and pdo_oci extensions from php sources. -- %{?scl_prefix}php-pecl-oci8 only provides oci8 extension. +You must install libclntsh.so.%{oraclelib} to use this package, +provided by Oracle Instant Client RPM available from Oracle on: +https://www.oracle.com/database/technologies/instant-client/downloads.html Documentation is at http://php.net/oci8 and http://php.net/pdo_oci %endif @@ -722,7 +773,7 @@ License: PHP and BSD %endif Requires: %{?scl_prefix}php-common%{?_isa} = %{version}-%{release} %if %{with_libgd} -BuildRequires: pkgconfig(gdlib) >= 2.3 +BuildRequires: pkgconfig(gdlib) >= 2.3.3 %else # Required to build the bundled GD library BuildRequires: pkgconfig(zlib) @@ -828,9 +879,9 @@ Group: System Environment/Libraries # All files licensed under PHP version 3.01 License: PHP Requires: %{?scl_prefix}php-common%{?_isa} = %{version}-%{release} -BuildRequires: pkgconfig(icu-i18n) >= 69 -BuildRequires: pkgconfig(icu-io) >= 69 -BuildRequires: pkgconfig(icu-uc) >= 69 +BuildRequires: pkgconfig(icu-i18n) >= 74 +BuildRequires: pkgconfig(icu-io) >= 74 +BuildRequires: pkgconfig(icu-uc) >= 74 %description intl The %{?scl_prefix}php-intl package contains a dynamic shared object that will add @@ -923,38 +974,65 @@ in pure PHP. %setup -q -n php-%{upver}%{?rcver} %endif -%patch1 -p1 -b .mpmcheck -%patch5 -p1 -b .includedir -%patch6 -p1 -b .embed -%patch8 -p1 -b .libdb -%if 0%{?rhel} -%patch9 -p1 -b .curltls -%endif +%patch -P1 -p1 -b .mpmcheck +%patch -P5 -p1 -b .includedir +%patch -P6 -p1 -b .embed +%patch -P8 -p1 -b .libdb +%patch -P10 -p1 -b .gcc14 +%patch -P11 -p1 -b .icu74 +%patch -P12 -p1 -b .proto -%if 0%{?fedora} >= 28 || 0%{?rhel} >= 6 -%patch42 -p1 -b .systzdata -%endif -%patch43 -p1 -b .headers +%patch -P42 -p1 -b .systzdata +%patch -P43 -p1 -b .headers sed -e 's/php-devel/%{?scl_prefix}php-devel/' -i scripts/phpize.in -%if 0%{?fedora} >= 18 || 0%{?rhel} >= 7 -%patch45 -p1 -b .ldap_r -%endif -%patch46 -p1 -b .argon2 -%patch47 -p1 -b .phpinfo -%patch48 -p1 -b .snmp +%patch -P45 -p1 -b .ldap_r +%patch -P46 -p1 -b .argon2 +%patch -P47 -p1 -b .phpinfo +%patch -P48 -p1 -b .snmp %if 0%{?fedora} >= 36 || 0%{?rhel} >= 9 -%patch50 -p1 -b .openssl3 +%patch -P50 -p1 -b .openssl3 rm ext/openssl/tests/p12_with_extra_certs.p12 %endif -%patch91 -p1 -b .remi-oci8 +%patch -P91 -p1 -b .remi-oci8 # upstream patches # security patches +%patch -P200 -p1 -b .cve31631 +%patch -P201 -p1 -b .cve0567 +%patch -P202 -p1 -b .cve0568 +%patch -P203 -p1 -b .cve0662 +%patch -P204 -p1 -b .cve3247 +%patch -P205 -p1 -b .cve3823 +%patch -P206 -p1 -b .cve3824 +%patch -P207 -p1 -b .cve2756 +%patch -P208 -p1 -b .cve3096 +%patch -P209 -p1 -b .cve5458 +%patch -P210 -p1 -b .cve8925 +%patch -P211 -p1 -b .cve8926 +%patch -P212 -p1 -b .cve8927 +%patch -P213 -p1 -b .cve9026 +%patch -P214 -p1 -b .cve11236 +%patch -P215 -p1 -b .cve11234 +%patch -P216 -p1 -b .cve8932 +%patch -P217 -p1 -b .cve11233 +%patch -P218 -p1 -b .ghsa4w77 +%patch -P219 -p1 -b .cve8929 +%patch -P220 -p1 -b .cve1217 +%patch -P221 -p1 -b .cve1734 +%patch -P222 -p1 -b .cve1861 +%patch -P223 -p1 -b .cve1736 +%patch -P224 -p1 -b .cve1219 +%patch -P225 -p1 -b .cve6491 +%patch -P226 -p1 -b .cve1220 +%patch -P227 -p1 -b .cve1735 # Fixes for tests -%patch300 -p1 -b .datetests +%patch -P300 -p1 -b .datetests +%patch -P301 -p1 -b .tests +%patch -P302 -p1 -b .zlibng +%patch -P303 -p1 -b .pcretests # WIP patch @@ -977,14 +1055,12 @@ mkdir \ # ----- Manage known as failed test ------- # affected by systzdata patch rm ext/date/tests/timezone_location_get.phpt -rm ext/date/tests/timezone_version_get.phpt -rm ext/date/tests/timezone_version_get_basic1.phpt %if 0%{?fedora} < 28 # need tzdata 2018i rm ext/date/tests/bug33414-1.phpt -rm ext/date/tests/bug33415-2.phpt rm ext/date/tests/date_modify-1.phpt %endif +rm ext/date/tests/bug33415-2.phpt # too fast builder rm ext/date/tests/bug73837.phpt # fails sometime @@ -998,6 +1074,13 @@ rm Zend/tests/bug68412.phpt rm sapi/cli/tests/upload_2G.phpt # tar issue rm ext/zlib/tests/004-mb.phpt +# Known to fail +%if 0%{?rhel} == 8 +rm ext/openssl/tests/openssl_error_string_basic.phpt +rm ext/openssl/tests/openssl_open_basic.phpt +%endif +rm ext/openssl/tests/openssl_private_decrypt_basic.phpt +rm ext/openssl/tests/openssl_x509_parse_basic.phpt # Safety check for API version change. pver=$(sed -n '/#define PHP_VERSION /{s/.* "//;s/".*$//;p}' main/php_version.h) @@ -1222,10 +1305,10 @@ build --libdir=%{_libdir}/php \ --with-mysqli=shared,mysqlnd \ --with-mysql-sock=%{mysql_sock} \ %if %{with_oci8} - --with-oci8=shared,instantclient,%{_root_libdir}/oracle/%{oraclever}/client64/lib,%{oraclever} \ - --with-pdo-oci=shared,instantclient,%{_root_libdir}/oracle/%{oraclever}/client64/lib,%{oraclever} \ + --with-oci8=shared,instantclient,%{_root_prefix}/lib/oracle/%{oracledir}/client64/lib,%{oraclever} \ + --with-pdo-oci=shared,instantclient,%{_root_prefix}/lib/oracle/%{oracledir}/client64/lib,%{oraclever} \ %endif -%if %{with_interbase} +%if %{with firebird} --with-pdo-firebird=shared \ %endif --enable-dom=shared \ @@ -1437,9 +1520,9 @@ mv $RPM_BUILD_ROOT%{_sysconfdir}/php-fpm.d/www.conf.default . # install systemd unit files and scripts for handling server startup install -Dm 644 %{SOURCE6} $RPM_BUILD_ROOT%{_unitdir}/%{?scl_prefix}php-fpm.service %if 0%{?fedora} >= 27 || 0%{?rhel} >= 8 -install -Dm 644 %{SOURCE12} $RPM_BUILD_ROOT%{_unitdir}/httpd.service.d/%{?scl_prefix}php-fpm.conf -install -Dm 644 %{SOURCE12} $RPM_BUILD_ROOT%{_unitdir}/nginx.service.d/%{?scl_prefix}php-fpm.conf -sed -e 's/php-fpm/%{?scl_prefix}php-fpm/' -i $RPM_BUILD_ROOT%{_unitdir}/*.service.d/%{?scl_prefix}php-fpm.conf +install -Dm 644 %{SOURCE12} $RPM_BUILD_ROOT%{_root_sysconfdir}/systemd/system/httpd.service.d/%{?scl_prefix}php-fpm.conf +install -Dm 644 %{SOURCE12} $RPM_BUILD_ROOT%{_root_sysconfdir}/systemd/system/nginx.service.d/%{?scl_prefix}php-fpm.conf +sed -e 's/php-fpm/%{?scl_prefix}php-fpm/' -i $RPM_BUILD_ROOT%{_root_sysconfdir}/systemd/system/*.service.d/%{?scl_prefix}php-fpm.conf %endif sed -e 's:/run:%{_localstatedir}/run:' \ -e 's:/etc/sysconfig:%{_sysconfdir}/sysconfig:' \ @@ -1530,7 +1613,7 @@ for mod in pgsql odbc ldap snmp \ %if %{with_oci8} oci8 pdo_oci \ %endif -%if %{with_interbase} +%if %{with firebird} pdo_firebird \ %endif %if %{with_freetds} @@ -1671,6 +1754,19 @@ fi %endif +%posttrans common +cat << EOF +===================================================================== + + WARNING : PHP 7.4 have reached its "End of Life" in + November 2022. Even, if this package includes some of + the important security fixes, backported from 8.1, the + UPGRADE to a maintained version is very strongly RECOMMENDED. + +===================================================================== +EOF + + %{!?_licensedir:%global license %%doc} %files @@ -1750,8 +1846,8 @@ fi %{_unitdir}/%{?scl_prefix}php-fpm.service %dir %{_root_sysconfdir}/systemd/system/%{?scl_prefix}php-fpm.service.d %if 0%{?fedora} >= 27 || 0%{?rhel} >= 8 -%{_unitdir}/httpd.service.d/%{?scl_prefix}php-fpm.conf -%{_unitdir}/nginx.service.d/%{?scl_prefix}php-fpm.conf +%config(noreplace) %{_root_sysconfdir}/systemd/system/httpd.service.d/%{?scl_prefix}php-fpm.conf +%config(noreplace) %{_root_sysconfdir}/systemd/system/nginx.service.d/%{?scl_prefix}php-fpm.conf %endif %{_sbindir}/php-fpm %dir %{_sysconfdir}/php-fpm.d @@ -1808,7 +1904,7 @@ fi %files pspell -f files.pspell %files intl -f files.intl %files process -f files.process -%if %{with_interbase} +%if %{with firebird} %files pdo-firebird -f files.pdo_firebird %endif %if %{with_enchant} @@ -1830,6 +1926,151 @@ fi %changelog +* Thu Jul 3 2025 Remi Collet <remi@remirepo.net> - 7.4.33-24 +- Fix pgsql extension does not check for errors during escaping + CVE-2025-1735 +- Fix NULL Pointer Dereference in PHP SOAP Extension via Large XML Namespace Prefix + CVE-2025-6491 +- Fix Null byte termination in hostnames + CVE-2025-1220 + +* Mon Mar 17 2025 Remi Collet <remi@remirepo.net> - 7.4.33-23 +- Fix libxml streams use wrong `content-type` header when requesting a redirected resource + CVE-2025-1219 +- Fix Stream HTTP wrapper header check might omit basic auth header + CVE-2025-1736 +- Fix Stream HTTP wrapper truncate redirect location to 1024 bytes + CVE-2025-1861 +- Fix Streams HTTP wrapper does not fail for headers without colon + CVE-2025-1734 +- Fix Header parser of `http` stream wrapper does not handle folded headers + CVE-2025-1217 +- use oracle client library version 23.7 on x86_64 and aarch64 + +* Thu Feb 13 2025 Remi Collet <remi@remirepo.net> - 7.4.33-22 +- backport fix for ICU 74+ +- backport fix strict prototypes + +* Wed Nov 27 2024 Remi Collet <remi@remirepo.net> - 7.4.33-21 +- Fix Leak partial content of the heap through heap buffer over-read + CVE-2024-8929 + +* Fri Nov 22 2024 Remi Collet <remi@remirepo.net> - 7.4.33-20 +- Fix Heap-Use-After-Free in sapi_read_post_data Processing in CLI SAPI Interface + GHSA-4w77-75f9-2c8w +- Fix OOB access in ldap_escape + CVE-2024-8932 +- Fix Integer overflow in the dblib/firebird quoter causing OOB writes + CVE-2024-11236 +- Fix Configuring a proxy in a stream context might allow for CRLF injection in URIs + CVE-2024-11234 +- Fix Single byte overread with convert.quoted-printable-decode filter + CVE-2024-11233 + +* Fri Nov 15 2024 Remi Collet <remi@remirepo.net> - 7.4.33-19 +- disable firebird on EL-10 + +* Thu Sep 26 2024 Remi Collet <remi@remirepo.net> - 7.4.33-18 +- Fix Bypass of CVE-2012-1823, Argument Injection in PHP-CGI + CVE-2024-4577 +- Fix Bypass of CVE-2024-4577, Parameter Injection Vulnerability + CVE-2024-8926 +- Fix cgi.force_redirect configuration is bypassable due to the environment variable collision + CVE-2024-8927 +- Fix Logs from childrens may be altered + CVE-2024-9026 +- Fix Erroneous parsing of multipart form data + CVE-2024-8925 +- use ICU 74.2 + +* Mon Aug 26 2024 Remi Collet <remi@remirepo.net> - 7.4.33-17 +- add backport for https://bugs.php.net/79589 + error:14095126:SSL routines:ssl3_read_n:unexpected eof while reading + +* Wed Jul 31 2024 Remi Collet <remi@remirepo.net> - 7.4.33-16 +- use oracle client library version 23.5 on x86_64 + +* Tue Jun 4 2024 Remi Collet <remi@remirepo.net> - 7.4.33-15 +- Fix filter bypass in filter_var FILTER_VALIDATE_URL + CVE-2024-5458 + +* Wed Apr 10 2024 Remi Collet <remi@remirepo.net> - 7.4.33-14 +- Fix __Host-/__Secure- cookie bypass due to partial CVE-2022-31629 fix + CVE-2024-2756 +- Fix password_verify can erroneously return true opening ATO risk + CVE-2024-3096 + +* Wed Mar 6 2024 Remi Collet <remi@remirepo.net> - 7.4.33-13 +- patch test suite for zlib-ng + +* Mon Feb 19 2024 Remi Collet <remi@remirepo.net> - 7.4.33-12 +- more build patch for GCC 14 + +* Wed Feb 14 2024 Remi Collet <remi@remirepo.net> - 7.4.33-11 +- add build patch for GCC 14 +- use oracle client library version 21.13 on x86_64 + +* Tue Dec 12 2023 Remi Collet <remi@remirepo.net> - 7.4.33-10 +- use ICU 73.2 +- use oracle client library version 21.12 on x86_64, 19.19 on aarch64 +- add fixes for libxml 2.11 and 2.12 from 8.1 + +* Thu Sep 21 2023 Remi Collet <remi@remirepo.net> - 7.4.33-9 +- use oracle client library version 21.11 on x86_64, 19.19 on aarch64 +- use official Oracle Instant Client RPM + +* Tue Aug 1 2023 Remi Collet <remi@remirepo.net> - 7.4.33-8 +- Fix Security issue with external entity loading in XML without enabling it + GHSA-3qrf-m4j2-pcrr CVE-2023-3823 +- Fix Buffer mismanagement in phar_dir_read() + GHSA-jqcx-ccgc-xwhv CVE-2023-3824 +- move httpd/nginx wants directive to config files in /etc + +* Tue Jun 6 2023 Remi Collet <remi@remirepo.net> - 7.4.33-7 +- Fix Missing error check and insufficient random bytes in HTTP Digest + authentication for SOAP + GHSA-76gg-c692-v2mw CVE-2023-3247 + +* Fri Apr 14 2023 Remi Collet <remi@remirepo.net> - 7.4.33-6 +- use ICU 72.1 +- use oracle client library version 21.10 +- fix possible buffer overflow in date +- define %%php74___phpize and %%php74___phpconfig + +* Tue Feb 21 2023 Remi Collet <remi@remirepo.net> - 7.4.33-5 +- F38: enable imap extension + +* Tue Feb 14 2023 Remi Collet <remi@remirepo.net> - 7.4.33-4 +- fix #81744: Password_verify() always return true with some hash + CVE-2023-0567 +- fix #81746: 1-byte array overrun in common path resolve code + CVE-2023-0568 +- fix DOS vulnerability when parsing multipart request body + CVE-2023-0662 +- add dependency on pcre2 minimal version + +* Mon Dec 19 2022 Remi Collet <remi@remirepo.net> - 7.4.33-2 +- pdo: fix #81740: PDO::quote() may return unquoted string + CVE-2022-31631 +- use oracle client library version 21.8 + +* Tue Nov 1 2022 Remi Collet <remi@remirepo.net> - 7.4.33-1 +- Update to 7.4.33 - http://www.php.net/releases/7_4_33.php + +* Wed Oct 26 2022 Remi Collet <remi@remirepo.net> - 7.4.32-2 +- add upstream fix for CVE-2022-31630 and CVE-2022-37454 + +* Wed Sep 28 2022 Remi Collet <remi@remirepo.net> - 7.4.32-1 +- Update to 7.4.32 - http://www.php.net/releases/7_4_32.php +- use ICU 71.1 + +* Tue Jun 7 2022 Remi Collet <remi@remirepo.net> - 7.4.30-1 +- Update to 7.4.30 - http://www.php.net/releases/7_4_30.php +- use oracle client library version 21.6 + +* Tue Apr 12 2022 Remi Collet <remi@remirepo.net> - 7.4.29-1 +- Update to 7.4.29 - http://www.php.net/releases/7_4_29.php + * Tue Feb 22 2022 Remi Collet <remi@remirepo.net> - 7.4.28-2 - retrieve tzdata version - use oracle client library version 21.5 |