diff options
41 files changed, 8309 insertions, 163 deletions
@@ -1,17 +1,15 @@ -===== 7.3.8 (2019-08-01) +===== 7.3.33-18 (2024-11-28) -$ grep -ar 'Tests failed' /var/lib/mock/scl73*/build.log +$ grep -ar 'Tests failed' /var/lib/mock/*/build.log -/var/lib/mock/scl73el6x/build.log:Tests failed : 0 -/var/lib/mock/scl73el7x/build.log:Tests failed : 0 -/var/lib/mock/scl73el8x/build.log:Tests failed : 13 -/var/lib/mock/scl73fc28x/build.log:Tests failed : 0 -/var/lib/mock/scl73fc29x/build.log:Tests failed : 0 -/var/lib/mock/scl73fc30x/build.log:Tests failed : 0 +/var/lib/mock/scl73el8a/build.log:Tests failed : 17 +/var/lib/mock/scl73el8x/build.log:Tests failed : 18 -el8x - 5 buildroot issue (openssl) +x86_64: + 3 Zend/tests/bug74093.phpt (too fast builder) +el8: + 2 buildroot issue with strict openssl policy (fixed in 7.4) (1) proc_open give erratic test results :( @@ -14,3 +14,7 @@ %@SCL@__php @BINDIR@/php +%@SCL@__phpize @BINDIR@/phpize + +%@SCL@__phpconfig @BINDIR@/php-config + diff --git a/php-5.6.3-embed.patch b/php-5.6.3-embed.patch index d9c04f6..38eea1d 100644 --- a/php-5.6.3-embed.patch +++ b/php-5.6.3-embed.patch @@ -18,7 +18,7 @@ diff -up php-5.5.30/scripts/php-config.in.old php-5.5.30/scripts/php-config.in php_cgi_binary=NONE configure_options="@CONFIGURE_OPTIONS@" -php_sapis="@PHP_INSTALLED_SAPIS@" -+php_sapis="apache2handler embed fpm @PHP_INSTALLED_SAPIS@" ++php_sapis="apache2handler embed fpm phpdbg @PHP_INSTALLED_SAPIS@" # Set php_cli_binary and php_cgi_binary if available for sapi in $php_sapis; do diff --git a/php-5.6.3-phpinfo.patch b/php-5.6.3-phpinfo.patch deleted file mode 100644 index 086be15..0000000 --- a/php-5.6.3-phpinfo.patch +++ /dev/null @@ -1,29 +0,0 @@ - -Drop "Configure Command" from phpinfo as it doesn't -provide any useful information. -The available extensions are not related to this command. - -diff -up php-7.0.0RC1/ext/standard/info.c.phpinfo php-7.0.0RC1/ext/standard/info.c ---- php-7.0.0RC1/ext/standard/info.c.phpinfo 2015-08-18 23:39:24.000000000 +0200 -+++ php-7.0.0RC1/ext/standard/info.c 2015-08-22 07:56:18.344761928 +0200 -@@ -870,9 +870,6 @@ PHPAPI void php_print_info(int flag) - #ifdef ARCHITECTURE - php_info_print_table_row(2, "Architecture", ARCHITECTURE); - #endif --#ifdef CONFIGURE_COMMAND -- php_info_print_table_row(2, "Configure Command", CONFIGURE_COMMAND ); --#endif - - if (sapi_module.pretty_name) { - php_info_print_table_row(2, "Server API", sapi_module.pretty_name ); -diff -up php-7.0.0RC1/ext/standard/tests/general_functions/phpinfo.phpt.phpinfo php-7.0.0RC1/ext/standard/tests/general_functions/phpinfo.phpt ---- php-7.0.0RC1/ext/standard/tests/general_functions/phpinfo.phpt.phpinfo 2015-08-18 23:39:22.000000000 +0200 -+++ php-7.0.0RC1/ext/standard/tests/general_functions/phpinfo.phpt 2015-08-22 07:56:18.344761928 +0200 -@@ -20,7 +20,6 @@ PHP Version => %s - - System => %s - Build Date => %s%a --Configure Command => %s - Server API => Command Line Interface - Virtual Directory Support => %s - Configuration File (php.ini) Path => %s diff --git a/php-7.2.0-oci8conf.patch b/php-7.2.0-oci8conf.patch index 0ad16a1..d026575 100644 --- a/php-7.2.0-oci8conf.patch +++ b/php-7.2.0-oci8conf.patch @@ -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.2.4-fixheader.patch b/php-7.2.4-fixheader.patch deleted file mode 100644 index 52a4121..0000000 --- a/php-7.2.4-fixheader.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -up php-7.2.4RC1/configure.ac.fixheader php-7.2.4RC1/configure.ac ---- php-7.2.4RC1/configure.ac.fixheader 2018-03-13 12:42:47.594623100 +0100 -+++ php-7.2.4RC1/configure.ac 2018-03-13 12:43:35.591871825 +0100 -@@ -1275,7 +1275,7 @@ PHP_BUILD_DATE=`date -u +%Y-%m-%d` - fi - AC_DEFINE_UNQUOTED(PHP_BUILD_DATE,"$PHP_BUILD_DATE",[PHP build date]) - --PHP_UNAME=`uname -a | xargs` -+PHP_UNAME=`uname | xargs` - AC_DEFINE_UNQUOTED(PHP_UNAME,"$PHP_UNAME",[uname -a output]) - PHP_OS=`uname | xargs` - AC_DEFINE_UNQUOTED(PHP_OS,"$PHP_OS",[uname output]) diff --git a/php-7.3.20-fixheader.patch b/php-7.3.20-fixheader.patch new file mode 100644 index 0000000..6bbdfbd --- /dev/null +++ b/php-7.3.20-fixheader.patch @@ -0,0 +1,13 @@ +diff -up ./configure.ac.fixheader ./configure.ac +--- ./configure.ac.fixheader 2020-07-06 16:04:56.069183751 +0200 ++++ ./configure.ac 2020-07-06 16:05:52.044046238 +0200 +@@ -1350,7 +1350,8 @@ PHP_BUILD_DATE=`date -u +%Y-%m-%d` + fi + AC_DEFINE_UNQUOTED(PHP_BUILD_DATE,"$PHP_BUILD_DATE",[PHP build date]) + +-PHP_UNAME=`uname -a | xargs` ++UNAME=`uname -a | xargs` ++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]) diff --git a/php-7.3.20-phpinfo.patch b/php-7.3.20-phpinfo.patch new file mode 100644 index 0000000..8118732 --- /dev/null +++ b/php-7.3.20-phpinfo.patch @@ -0,0 +1,76 @@ + +Drop "Configure Command" from phpinfo as it doesn't +provide any useful information. +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 +@@ -829,9 +829,6 @@ PHPAPI void php_print_info(int flag) + #ifdef ARCHITECTURE + php_info_print_table_row(2, "Architecture", ARCHITECTURE); + #endif +-#ifdef CONFIGURE_COMMAND +- php_info_print_table_row(2, "Configure Command", CONFIGURE_COMMAND ); +-#endif + + if (sapi_module.pretty_name) { + php_info_print_table_row(2, "Server API", sapi_module.pretty_name ); +diff -up a/ext/standard/tests/general_functions/phpinfo.phpt.phpinfo b/ext/standard/tests/general_functions/phpinfo.phpt +--- a/ext/standard/tests/general_functions/phpinfo.phpt.phpinfo 2015-08-18 23:39:22.000000000 +0200 ++++ b/ext/standard/tests/general_functions/phpinfo.phpt 2015-08-22 07:56:18.344761928 +0200 +@@ -20,7 +20,6 @@ PHP Version => %s + + System => %s + Build Date => %s%a +-Configure Command => %s + Server API => Command Line Interface + Virtual Directory Support => %s + Configuration File (php.ini) Path => %s + + +Backported from 8.0: + +From ad0d2e438fddc089917e71e5d8909d145db9da8a Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Fri, 3 Jul 2020 10:08:09 +0200 +Subject: [PATCH] display info about system used to build and its provider + +--- + configure.ac | 5 +++++ + ext/standard/info.c | 6 ++++++ + 2 files changed, 11 insertions(+) + +diff --git a/configure.ac b/configure.ac +index d9e6329314a3..77f12a55569a 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1355,6 +1355,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]) ++PHP_BUILD_SYSTEM=${PHP_BUILD_SYSTEM:-$PHP_UNAME} ++AC_DEFINE_UNQUOTED(PHP_BUILD_SYSTEM,"$PHP_BUILD_SYSTEM",[builder uname output]) ++if test -n "${PHP_BUILD_PROVIDER}"; then ++ AC_DEFINE_UNQUOTED(PHP_BUILD_PROVIDER,"$PHP_BUILD_PROVIDER",[build provider]) ++fi + + PHP_SUBST_OLD(PHP_INSTALLED_SAPIS) + +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 +@@ -823,6 +823,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__); ++#ifdef PHP_BUILD_SYSTEM ++ php_info_print_table_row(2, "Build System", PHP_BUILD_SYSTEM); ++#endif ++#ifdef PHP_BUILD_PROVIDER ++ php_info_print_table_row(2, "Build Provider", PHP_BUILD_PROVIDER); ++#endif + #ifdef COMPILER + php_info_print_table_row(2, "Compiler", COMPILER); + #endif diff --git a/php-7.3.24-fpm.patch b/php-7.3.24-fpm.patch new file mode 100644 index 0000000..1165970 --- /dev/null +++ b/php-7.3.24-fpm.patch @@ -0,0 +1,219 @@ +Fix for https://bugs.php.net/74083 master PHP-fpm is stopped on multiple reloads +backported for 7.4 from + +From ae5154c6c6af7ba7c592f8af006b7cadd0d66d6e Mon Sep 17 00:00:00 2001 +From: Maksim Nikulin <mnikulin@plesk.com> +Date: Wed, 24 Jul 2019 16:50:57 +0700 +Subject: [PATCH] Block signals during fpm master initialization + +From e37bd5dcc2e8f269c6031d86429311c8cf243060 Mon Sep 17 00:00:00 2001 +From: Maksim Nikulin <mnikulin@plesk.com> +Date: Mon, 21 Oct 2019 14:23:29 +0700 +Subject: [PATCH] Do not let PHP-FPM children miss SIGTERM, SIGQUIT + + +diff -up ./sapi/fpm/fpm/fpm_children.c.fpmsig ./sapi/fpm/fpm/fpm_children.c +--- ./sapi/fpm/fpm/fpm_children.c.fpmsig 2020-10-23 10:36:31.423925856 +0200 ++++ ./sapi/fpm/fpm/fpm_children.c 2020-10-23 10:36:38.872900642 +0200 +@@ -404,6 +404,11 @@ int fpm_children_make(struct fpm_worker_ + return 2; + } + ++ zlog(ZLOG_DEBUG, "blocking signals before child birth"); ++ if (0 > fpm_signals_child_block()) { ++ zlog(ZLOG_WARNING, "child may miss signals"); ++ } ++ + pid = fork(); + + switch (pid) { +@@ -415,12 +420,16 @@ int fpm_children_make(struct fpm_worker_ + return 0; + + case -1 : ++ zlog(ZLOG_DEBUG, "unblocking signals"); ++ fpm_signals_unblock(); + zlog(ZLOG_SYSERROR, "fork() failed"); + + fpm_resources_discard(child); + return 2; + + default : ++ zlog(ZLOG_DEBUG, "unblocking signals, child born"); ++ fpm_signals_unblock(); + child->pid = pid; + fpm_clock_get(&child->started); + fpm_parent_resources_use(child); +diff -up ./sapi/fpm/fpm/fpm_main.c.fpmsig ./sapi/fpm/fpm/fpm_main.c +--- ./sapi/fpm/fpm/fpm_main.c.fpmsig 2020-10-13 11:27:02.000000000 +0200 ++++ ./sapi/fpm/fpm/fpm_main.c 2020-10-23 10:36:38.873900639 +0200 +@@ -90,6 +90,7 @@ int __riscosify_control = __RISCOSIFY_ST + #include "fpm.h" + #include "fpm_request.h" + #include "fpm_status.h" ++#include "fpm_signals.h" + #include "fpm_conf.h" + #include "fpm_php.h" + #include "fpm_log.h" +@@ -1584,6 +1585,11 @@ int main(int argc, char *argv[]) + closes it. in apache|apxs mode apache + does that for us! thies@thieso.net + 20000419 */ ++ ++ if (0 > fpm_signals_init_mask() || 0 > fpm_signals_block()) { ++ zlog(ZLOG_WARNING, "Could die in the case of too early reload signal"); ++ } ++ zlog(ZLOG_DEBUG, "Blocked some signals"); + #endif + #endif + +diff -up ./sapi/fpm/fpm/fpm_process_ctl.c.fpmsig ./sapi/fpm/fpm/fpm_process_ctl.c +--- ./sapi/fpm/fpm/fpm_process_ctl.c.fpmsig 2020-10-13 11:27:02.000000000 +0200 ++++ ./sapi/fpm/fpm/fpm_process_ctl.c 2020-10-23 10:36:11.921991864 +0200 +@@ -77,6 +77,10 @@ static void fpm_pctl_exit() /* {{{ */ + + static void fpm_pctl_exec() /* {{{ */ + { ++ zlog(ZLOG_DEBUG, "Blocking some signals before reexec"); ++ if (0 > fpm_signals_block()) { ++ zlog(ZLOG_WARNING, "concurrent reloads may be unstable"); ++ } + + zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\"" + "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" +diff -up ./sapi/fpm/fpm/fpm_signals.c.fpmsig ./sapi/fpm/fpm/fpm_signals.c +--- ./sapi/fpm/fpm/fpm_signals.c.fpmsig 2020-10-13 11:27:02.000000000 +0200 ++++ ./sapi/fpm/fpm/fpm_signals.c 2020-10-23 10:36:38.873900639 +0200 +@@ -19,6 +19,8 @@ + #include "zlog.h" + + static int sp[2]; ++static sigset_t block_sigset; ++static sigset_t child_block_sigset; + + const char *fpm_signal_names[NSIG + 1] = { + #ifdef SIGHUP +@@ -165,8 +167,11 @@ static void sig_handler(int signo) /* {{ + int saved_errno; + + if (fpm_globals.parent_pid != getpid()) { +- /* prevent a signal race condition when child process +- have not set up it's own signal handler yet */ ++ /* Avoid using of signal handlers from the master process in a worker ++ before the child sets up its own signal handlers. ++ Normally it is prevented by the sigprocmask() calls ++ around fork(). This execution branch is a last resort trap ++ that has no protection against #76601. */ + return; + } + +@@ -210,6 +215,11 @@ int fpm_signals_init_main() /* {{{ */ + zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()"); + return -1; + } ++ ++ zlog(ZLOG_DEBUG, "Unblocking all signals"); ++ if (0 > fpm_signals_unblock()) { ++ return -1; ++ } + return 0; + } + /* }}} */ +@@ -241,6 +251,10 @@ int fpm_signals_init_child() /* {{{ */ + } + + zend_signal_init(); ++ ++ if (0 > fpm_signals_unblock()) { ++ return -1; ++ } + return 0; + } + /* }}} */ +@@ -250,3 +264,72 @@ int fpm_signals_get_fd() /* {{{ */ + return sp[0]; + } + /* }}} */ ++ ++int fpm_signals_init_mask() /* {{{ */ ++{ ++ /* Subset of signals from fpm_signals_init_main() and fpm_got_signal() ++ blocked to avoid unexpected death during early init ++ or during reload just after execvp() or fork */ ++ int init_signal_array[] = { SIGUSR1, SIGUSR2, SIGCHLD }; ++ size_t size = sizeof(init_signal_array)/sizeof(init_signal_array[0]); ++ size_t i = 0; ++ if (0 > sigemptyset(&block_sigset) || ++ 0 > sigemptyset(&child_block_sigset)) { ++ zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigemptyset()"); ++ return -1; ++ } ++ for (i = 0; i < size; ++i) { ++ int sig_i = init_signal_array[i]; ++ if (0 > sigaddset(&block_sigset, sig_i) || ++ 0 > sigaddset(&child_block_sigset, sig_i)) { ++ if (sig_i <= NSIG && fpm_signal_names[sig_i] != NULL) { ++ zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigaddset(%s)", ++ fpm_signal_names[sig_i]); ++ } else { ++ zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigaddset(%d)", sig_i); ++ } ++ return -1; ++ } ++ } ++ if (0 > sigaddset(&child_block_sigset, SIGTERM) || ++ 0 > sigaddset(&child_block_sigset, SIGQUIT)) { ++ zlog(ZLOG_SYSERROR, "failed to prepare child signal block mask: sigaddset()"); ++ return -1; ++ } ++ return 0; ++} ++/* }}} */ ++ ++int fpm_signals_block() /* {{{ */ ++{ ++ if (0 > sigprocmask(SIG_BLOCK, &block_sigset, NULL)) { ++ zlog(ZLOG_SYSERROR, "failed to block signals"); ++ return -1; ++ } ++ return 0; ++} ++/* }}} */ ++ ++int fpm_signals_child_block() /* {{{ */ ++{ ++ if (0 > sigprocmask(SIG_BLOCK, &child_block_sigset, NULL)) { ++ zlog(ZLOG_SYSERROR, "failed to block child signals"); ++ return -1; ++ } ++ return 0; ++} ++/* }}} */ ++ ++int fpm_signals_unblock() /* {{{ */ ++{ ++ /* Ensure that during reload after upgrade all signals are unblocked. ++ block_sigset could have different value before execve() */ ++ sigset_t all_signals; ++ sigfillset(&all_signals); ++ if (0 > sigprocmask(SIG_UNBLOCK, &all_signals, NULL)) { ++ zlog(ZLOG_SYSERROR, "failed to unblock signals"); ++ return -1; ++ } ++ return 0; ++} ++/* }}} */ +diff -up ./sapi/fpm/fpm/fpm_signals.h.fpmsig ./sapi/fpm/fpm/fpm_signals.h +--- ./sapi/fpm/fpm/fpm_signals.h.fpmsig 2020-10-13 11:27:02.000000000 +0200 ++++ ./sapi/fpm/fpm/fpm_signals.h 2020-10-23 10:36:38.873900639 +0200 +@@ -8,6 +8,10 @@ + int fpm_signals_init_main(); + int fpm_signals_init_child(); + int fpm_signals_get_fd(); ++int fpm_signals_init_mask(); ++int fpm_signals_block(); ++int fpm_signals_child_block(); ++int fpm_signals_unblock(); + + extern const char *fpm_signal_names[NSIG + 1]; + diff --git a/php-7.3.3-systzdata-v18.patch b/php-7.3.3-systzdata-v19.patch index eac3cc3..17a01fe 100644 --- a/php-7.3.3-systzdata-v18.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: +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) r16: adapt for timelib 2017.06 (in 7.2.3RC1) @@ -29,10 +30,11 @@ r3: fix a crash if /usr/share/zoneinfo doesn't exist (Raphael Geissert) r2: add filesystem trawl to set up name alias index r1: initial revision -diff -up php-7.3.3RC1/ext/date/config0.m4.systzdata php-7.3.3RC1/ext/date/config0.m4 ---- php-7.3.3RC1/ext/date/config0.m4.systzdata 2019-02-19 14:57:51.314601701 +0100 -+++ php-7.3.3RC1/ext/date/config0.m4 2019-02-19 14:58:29.050812587 +0100 -@@ -9,6 +9,19 @@ io.h +diff --git a/ext/date/config0.m4 b/ext/date/config0.m4 +index 20e4164aaa..a61243646d 100644 +--- a/ext/date/config0.m4 ++++ b/ext/date/config0.m4 +@@ -4,6 +4,19 @@ AC_CHECK_HEADERS([io.h]) dnl Check for strtoll, atoll AC_CHECK_FUNCS(strtoll atoll) @@ -52,10 +54,11 @@ diff -up php-7.3.3RC1/ext/date/config0.m4.systzdata php-7.3.3RC1/ext/date/config PHP_DATE_CFLAGS="-I@ext_builddir@/lib -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 -DHAVE_TIMELIB_CONFIG_H=1" timelib_sources="lib/astro.c lib/dow.c lib/parse_date.c lib/parse_tz.c lib/timelib.c lib/tm2unixtime.c lib/unixtime2tm.c lib/parse_iso_intervals.c lib/interval.c" -diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/lib/parse_tz.c ---- php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata 2019-02-19 12:18:27.000000000 +0100 -+++ php-7.3.3RC1/ext/date/lib/parse_tz.c 2019-02-19 14:57:20.397428931 +0100 -@@ -25,8 +25,21 @@ +diff --git a/ext/date/lib/parse_tz.c b/ext/date/lib/parse_tz.c +index 020da3135e..12e68ef043 100644 +--- a/ext/date/lib/parse_tz.c ++++ b/ext/date/lib/parse_tz.c +@@ -26,8 +26,21 @@ #include "timelib.h" #include "timelib_private.h" @@ -77,7 +80,7 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li #if (defined(__APPLE__) || defined(__APPLE_CC__)) && (defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__)) # if defined(__LITTLE_ENDIAN__) -@@ -87,6 +100,11 @@ static int read_php_preamble(const unsig +@@ -88,6 +101,11 @@ static int read_php_preamble(const unsigned char **tzf, timelib_tzinfo *tz) { uint32_t version; @@ -89,7 +92,7 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li /* read ID */ version = (*tzf)[3] - '0'; *tzf += 4; -@@ -411,7 +429,429 @@ void timelib_dump_tzinfo(timelib_tzinfo +@@ -412,7 +430,467 @@ void timelib_dump_tzinfo(timelib_tzinfo *tz) } } @@ -320,6 +323,44 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li +} + + ++/* Retrieve tzdata version. */ ++static void retrieve_zone_version(timelib_tzdb *db) ++{ ++ static char buf[30]; ++ char path[PATH_MAX]; ++ FILE *fp; ++ ++ strncpy(path, ZONEINFO_PREFIX "/tzdata.zi", sizeof(path)); ++ ++ fp = fopen(path, "r"); ++ if (fp) { ++ if (fgets(buf, sizeof(buf), fp)) { ++ if (!memcmp(buf, "# version ", 10) && ++ isdigit(buf[10]) && ++ isdigit(buf[11]) && ++ isdigit(buf[12]) && ++ isdigit(buf[13]) && ++ islower(buf[14])) { ++ if (buf[14] >= 't') { /* 2022t = 2022.20 */ ++ buf[17] = 0; ++ buf[16] = buf[14] - 't' + '0'; ++ buf[15] = '2'; ++ } else if (buf[14] >= 'j') { /* 2022j = 2022.10 */ ++ buf[17] = 0; ++ buf[16] = buf[14] - 'j' + '0'; ++ buf[15] = '1'; ++ } else { /* 2022a = 2022.1 */ ++ buf[16] = 0; ++ buf[15] = buf[14] - 'a' + '1'; ++ } ++ buf[14] = '.'; ++ db->version = buf+10; ++ } ++ } ++ fclose(fp); ++ } ++} ++ +/* Create the zone identifier index by trawling the filesystem. */ +static void create_zone_index(timelib_tzdb *db) +{ @@ -411,7 +452,7 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li + 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); + @@ -520,7 +561,7 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li { int left = 0, right = tzdb->index_size - 1; -@@ -437,9 +877,48 @@ static int seek_to_tz_position(const uns +@@ -438,9 +916,49 @@ static int seek_to_tz_position(const unsigned char **tzf, char *timezone, const return 0; } @@ -557,6 +598,7 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li + tmp->version = "0.system"; + tmp->data = NULL; + create_zone_index(tmp); ++ retrieve_zone_version(tmp); + system_location_table = create_location_table(); + fake_data_segment(tmp, system_location_table); + timezonedb_system = tmp; @@ -569,7 +611,7 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li } const timelib_tzdb_index_entry *timelib_timezone_identifiers_list(const timelib_tzdb *tzdb, int *count) -@@ -451,7 +930,30 @@ const timelib_tzdb_index_entry *timelib_ +@@ -452,7 +970,30 @@ const timelib_tzdb_index_entry *timelib_timezone_identifiers_list(const timelib_ int timelib_timezone_id_is_valid(char *timezone, const timelib_tzdb *tzdb) { const unsigned char *tzf; @@ -601,7 +643,7 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li } static int skip_64bit_preamble(const unsigned char **tzf, timelib_tzinfo *tz) -@@ -493,12 +995,14 @@ static timelib_tzinfo* timelib_tzinfo_ct +@@ -494,12 +1035,14 @@ static timelib_tzinfo* timelib_tzinfo_ctor(char *name) timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb, int *error_code) { const unsigned char *tzf; @@ -617,7 +659,7 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li tmp = timelib_tzinfo_ctor(timezone); version = read_preamble(&tzf, tmp, &type); -@@ -537,11 +1041,36 @@ timelib_tzinfo *timelib_parse_tzfile(cha +@@ -534,11 +1077,36 @@ timelib_tzinfo *timelib_parse_tzfile(char *timezone, const timelib_tzdb *tzdb, i } skip_posix_string(&tzf, tmp); @@ -654,3 +696,19 @@ diff -up php-7.3.3RC1/ext/date/lib/parse_tz.c.systzdata php-7.3.3RC1/ext/date/li } else { *error_code = TIMELIB_ERROR_NO_SUCH_TIMEZONE; tmp = NULL; +diff --git a/ext/date/php_date.c b/ext/date/php_date.c +index e1a427c5ca..465906fa2b 100644 +--- a/ext/date/php_date.c ++++ b/ext/date/php_date.c +@@ -951,7 +951,11 @@ PHP_MINFO_FUNCTION(date) + php_info_print_table_row(2, "date/time support", "enabled"); + php_info_print_table_row(2, "timelib version", TIMELIB_ASCII_VERSION); + php_info_print_table_row(2, "\"Olson\" Timezone Database Version", tzdb->version); ++#ifdef HAVE_SYSTEM_TZDATA ++ php_info_print_table_row(2, "Timezone Database", "system"); ++#else + php_info_print_table_row(2, "Timezone Database", php_date_global_timezone_db_enabled ? "external" : "internal"); ++#endif + php_info_print_table_row(2, "Default timezone", guess_timezone(tzdb)); + php_info_print_table_end(); + diff --git a/php-bug80682.patch b/php-bug80682.patch new file mode 100644 index 0000000..38f908b --- /dev/null +++ b/php-bug80682.patch @@ -0,0 +1,30 @@ +From 067f7e4150d8f9bddf6b198c9c7826565ee549b0 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@php.net> +Date: Thu, 28 Jan 2021 16:24:39 +0100 +Subject: [PATCH] Fix #80682 opcache doesn't honour pcre.jit option + +--- + ext/opcache/zend_accelerator_blacklist.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/ext/opcache/zend_accelerator_blacklist.c b/ext/opcache/zend_accelerator_blacklist.c +index 889fcabd7988..5c6bd76821a5 100644 +--- a/ext/opcache/zend_accelerator_blacklist.c ++++ b/ext/opcache/zend_accelerator_blacklist.c +@@ -185,10 +185,12 @@ static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist) + return; + } + #ifdef HAVE_PCRE_JIT_SUPPORT +- if (0 > pcre2_jit_compile(it->re, PCRE2_JIT_COMPLETE)) { +- /* Don't return here, even JIT could fail to compile, the pattern is still usable. */ +- pcre2_get_error_message(errnumber, pcre_error, sizeof(pcre_error)); +- zend_accel_error(ACCEL_LOG_WARNING, "Blacklist JIT compilation failed, %s\n", pcre_error); ++ if (PCRE_G(jit)) { ++ if (0 > pcre2_jit_compile(it->re, PCRE2_JIT_COMPLETE)) { ++ /* Don't return here, even JIT could fail to compile, the pattern is still usable. */ ++ pcre2_get_error_message(errnumber, pcre_error, sizeof(pcre_error)); ++ zend_accel_error(ACCEL_LOG_WARNING, "Blacklist JIT compilation failed, %s\n", pcre_error); ++ } + } + #endif + /* prepare for the next iteration */ diff --git a/php-bug80783.patch b/php-bug80783.patch new file mode 100644 index 0000000..2da9928 --- /dev/null +++ b/php-bug80783.patch @@ -0,0 +1,185 @@ +From bccca0b53aa60a62e2988c750fc73c02d109e642 Mon Sep 17 00:00:00 2001 +From: "Christoph M. Becker" <cmbecker69@gmx.de> +Date: Thu, 25 Feb 2021 14:38:42 +0100 +Subject: [PATCH] Fix #80783: PDO ODBC truncates BLOB records at every 256th + byte + +It is not guaranteed, that the driver inserts only a single NUL byte at +the end of the buffer. Apparently, there is no way to find out the +actual data length in the buffer after calling `SQLGetData()`, so we +adjust after the next `SQLGetData()` call. + +We also prevent PDO::ODBC_ATTR_ASSUME_UTF8 from fetching garbage, by +fetching all chunks with the same C type. + +Closes GH-6716. +--- + NEWS | 4 ++++ + ext/pdo_odbc/odbc_stmt.c | 14 +++++++++++-- + ext/pdo_odbc/tests/bug80783.phpt | 32 ++++++++++++++++++++++++++++++ + ext/pdo_odbc/tests/bug80783a.phpt | 33 +++++++++++++++++++++++++++++++ + 4 files changed, 81 insertions(+), 2 deletions(-) + create mode 100644 ext/pdo_odbc/tests/bug80783.phpt + create mode 100644 ext/pdo_odbc/tests/bug80783a.phpt + +diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c +index 18abc475b9eb..7ce0bebdca0d 100644 +--- a/ext/pdo_odbc/odbc_stmt.c ++++ b/ext/pdo_odbc/odbc_stmt.c +@@ -652,6 +652,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong + + /* if it is a column containing "long" data, perform late binding now */ + if (C->is_long) { ++ SQLLEN orig_fetched_len = SQL_NULL_DATA; + zend_ulong used = 0; + char *buf; + RETCODE rc; +@@ -662,6 +663,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong + + rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, C->data, + 256, &C->fetched_len); ++ orig_fetched_len = C->fetched_len; + + if (rc == SQL_SUCCESS) { + /* all the data fit into our little buffer; +@@ -673,7 +675,8 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong + /* this is a 'long column' + + read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks +- in order into the output buffer ++ in order into the output buffer; 255 bytes are an optimistic assumption, since the driver may assert ++ more or less NUL bytes at the end; we cater to that later, if actual length information is available + + this loop has to work whether or not SQLGetData() provides the total column length. + calling SQLDescribeCol() or other, specifically to get the column length, then doing a single read +@@ -687,7 +690,14 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong + do { + C->fetched_len = 0; + /* read block. 256 bytes => 255 bytes are actually read, the last 1 is NULL */ +- rc = SQLGetData(S->stmt, colno+1, SQL_C_CHAR, buf2, 256, &C->fetched_len); ++ rc = SQLGetData(S->stmt, colno+1, C->is_unicode ? SQL_C_BINARY : SQL_C_CHAR, buf2, 256, &C->fetched_len); ++ ++ /* adjust `used` in case we have length info from the driver */ ++ if (orig_fetched_len >= 0 && C->fetched_len >= 0) { ++ SQLLEN fixed_used = orig_fetched_len - C->fetched_len; ++ ZEND_ASSERT(fixed_used <= used + 1); ++ used = fixed_used; ++ } + + /* resize output buffer and reassemble block */ + if (rc==SQL_SUCCESS_WITH_INFO) { +diff --git a/ext/pdo_odbc/tests/bug80783.phpt b/ext/pdo_odbc/tests/bug80783.phpt +new file mode 100644 +index 000000000000..9794c25a30ec +--- /dev/null ++++ b/ext/pdo_odbc/tests/bug80783.phpt +@@ -0,0 +1,32 @@ ++--TEST-- ++Bug #80783 (PDO ODBC truncates BLOB records at every 256th byte) ++--SKIPIF-- ++<?php ++if (!extension_loaded('pdo_odbc')) die('skip pdo_odbc extension not available'); ++require 'ext/pdo/tests/pdo_test.inc'; ++PDOTest::skip(); ++?> ++--FILE-- ++<?php ++require 'ext/pdo/tests/pdo_test.inc'; ++$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); ++$db->exec("CREATE TABLE bug80783 (name IMAGE)"); ++ ++$string = str_repeat("0123456789", 50); ++$db->exec("INSERT INTO bug80783 VALUES('$string')"); ++ ++$stmt = $db->prepare("SELECT name FROM bug80783"); ++$stmt->bindColumn(1, $data, PDO::PARAM_LOB); ++$stmt->execute(); ++$stmt->fetch(PDO::FETCH_BOUND); ++ ++var_dump($data === bin2hex($string)); ++?> ++--CLEAN-- ++<?php ++require 'ext/pdo/tests/pdo_test.inc'; ++$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); ++$db->exec("DROP TABLE bug80783"); ++?> ++--EXPECT-- ++bool(true) +diff --git a/ext/pdo_odbc/tests/bug80783a.phpt b/ext/pdo_odbc/tests/bug80783a.phpt +new file mode 100644 +index 000000000000..f9e123ae5426 +--- /dev/null ++++ b/ext/pdo_odbc/tests/bug80783a.phpt +@@ -0,0 +1,33 @@ ++--TEST-- ++Bug #80783 (PDO ODBC truncates BLOB records at every 256th byte) ++--SKIPIF-- ++<?php ++if (!extension_loaded('pdo_odbc')) die('skip pdo_odbc extension not available'); ++require 'ext/pdo/tests/pdo_test.inc'; ++PDOTest::skip(); ++?> ++--FILE-- ++<?php ++require 'ext/pdo/tests/pdo_test.inc'; ++$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); ++$db->exec("CREATE TABLE bug80783a (name NVARCHAR(MAX))"); ++ ++$string = str_repeat("0123456789", 50); ++$db->exec("INSERT INTO bug80783a VALUES('$string')"); ++ ++$stmt = $db->prepare("SELECT name FROM bug80783a"); ++$stmt->setAttribute(PDO::ODBC_ATTR_ASSUME_UTF8, true); ++$stmt->bindColumn(1, $data, PDO::PARAM_STR); ++$stmt->execute(); ++$stmt->fetch(PDO::FETCH_BOUND); ++ ++var_dump($data === $string); ++?> ++--CLEAN-- ++<?php ++require 'ext/pdo/tests/pdo_test.inc'; ++$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt'); ++$db->exec("DROP TABLE bug80783a"); ++?> ++--EXPECT-- ++bool(true) +From 25f5a1b2e15344e75d69a7140631d467e8b3f966 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 8 Apr 2021 11:04:33 +0200 +Subject: [PATCH] Improve fix for #80783 + +--- + ext/pdo_odbc/odbc_stmt.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c +index 7ce0bebdca0d..368648c36ae2 100644 +--- a/ext/pdo_odbc/odbc_stmt.c ++++ b/ext/pdo_odbc/odbc_stmt.c +@@ -665,13 +665,13 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong + 256, &C->fetched_len); + orig_fetched_len = C->fetched_len; + +- if (rc == SQL_SUCCESS) { ++ if (rc == SQL_SUCCESS && C->fetched_len < 256) { + /* all the data fit into our little buffer; + * jump down to the generic bound data case */ + goto in_data; + } + +- if (rc == SQL_SUCCESS_WITH_INFO) { ++ if (rc == SQL_SUCCESS_WITH_INFO || rc == SQL_SUCCESS) { + /* this is a 'long column' + + read the column in 255 byte blocks until the end of the column is reached, reassembling those blocks +@@ -700,7 +700,7 @@ static int odbc_stmt_get_col(pdo_stmt_t *stmt, int colno, char **ptr, zend_ulong + } + + /* resize output buffer and reassemble block */ +- if (rc==SQL_SUCCESS_WITH_INFO) { ++ if (rc==SQL_SUCCESS_WITH_INFO || (rc==SQL_SUCCESS && C->fetched_len > 255)) { + /* point 5, in section "Retrieving Data with SQLGetData" in http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx + states that if SQL_SUCCESS_WITH_INFO, fetched_len will be > 255 (greater than buf2's size) + (if a driver fails to follow that and wrote less than 255 bytes to buf2, this will AV or read garbage into buf) */ diff --git a/php-bug81719.patch b/php-bug81719.patch new file mode 100644 index 0000000..c40e1ba --- /dev/null +++ b/php-bug81719.patch @@ -0,0 +1,60 @@ +From 9433de72e291db518357fe55531cc15432d43ec4 Mon Sep 17 00:00:00 2001 +From: Stanislav Malyshev <smalyshev@gmail.com> +Date: Mon, 6 Jun 2022 00:56:51 -0600 +Subject: [PATCH 2/3] Fix bug #81719: mysqlnd/pdo password buffer overflow + +(cherry picked from commit 58006537fc5f133ae8549efe5118cde418b3ace9) +--- + ext/mysqlnd/mysqlnd_wireprotocol.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c +index 6459fe4964..1aee62c64e 100644 +--- a/ext/mysqlnd/mysqlnd_wireprotocol.c ++++ b/ext/mysqlnd/mysqlnd_wireprotocol.c +@@ -768,7 +768,8 @@ php_mysqlnd_change_auth_response_write(MYSQLND_CONN_DATA * conn, void * _packet) + MYSQLND_VIO * vio = conn->vio; + MYSQLND_STATS * stats = conn->stats; + MYSQLND_CONNECTION_STATE * connection_state = &conn->state; +- zend_uchar * buffer = pfc->cmd_buffer.length >= packet->auth_data_len? pfc->cmd_buffer.buffer : mnd_emalloc(packet->auth_data_len); ++ size_t total_packet_size = packet->auth_data_len + MYSQLND_HEADER_SIZE; ++ zend_uchar * const buffer = pfc->cmd_buffer.length >= total_packet_size? pfc->cmd_buffer.buffer : mnd_emalloc(total_packet_size); + zend_uchar * p = buffer + MYSQLND_HEADER_SIZE; /* start after the header */ + + DBG_ENTER("php_mysqlnd_change_auth_response_write"); +-- +2.35.3 + +From f451082baf14ee9ea86cdd19870e906adb368f02 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 7 Jun 2022 09:57:15 +0200 +Subject: [PATCH 3/3] NEWS + +--- + NEWS | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/NEWS b/NEWS +index ffbe82d7aa..fd227bd33a 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,5 +1,16 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ++ ++Backported from 7.4.30 ++ ++- mysqlnd: ++ . Fixed bug #81719: mysqlnd/pdo password buffer overflow. ++ (CVE-2022-31626) (c dot fol at ambionics dot io) ++ ++- pgsql ++ . Fixed bug #81720: Uninitialized array in pg_query_params(). ++ (CVE-2022-31625) (cmb) ++ + 18 Nov 2021, PHP 7.3.33 + + - XML: +-- +2.35.3 + diff --git a/php-bug81720.patch b/php-bug81720.patch new file mode 100644 index 0000000..8580d7a --- /dev/null +++ b/php-bug81720.patch @@ -0,0 +1,76 @@ +From 6f979c832c861fb32e2dbad5e0cc29edcee7c500 Mon Sep 17 00:00:00 2001 +From: "Christoph M. Becker" <cmbecker69@gmx.de> +Date: Tue, 17 May 2022 12:59:23 +0200 +Subject: [PATCH 1/3] Fix #81720: Uninitialized array in pg_query_params() + leading to RCE + +We must not free parameters which we haven't initialized yet. + +We also fix the not directly related issue, that we checked for the +wrong value being `NULL`, potentially causing a segfault. + +(cherry picked from commit 55f6895f4b4c677272fd4ee1113acdbd99c4b5ab) +--- + ext/pgsql/pgsql.c | 4 ++-- + ext/pgsql/tests/bug81720.phpt | 27 +++++++++++++++++++++++++++ + 2 files changed, 29 insertions(+), 2 deletions(-) + create mode 100644 ext/pgsql/tests/bug81720.phpt + +diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c +index 27462bc336..1dd892d359 100644 +--- a/ext/pgsql/pgsql.c ++++ b/ext/pgsql/pgsql.c +@@ -1994,7 +1994,7 @@ PHP_FUNCTION(pg_query_params) + if (Z_TYPE(tmp_val) != IS_STRING) { + php_error_docref(NULL, E_WARNING,"Error converting parameter"); + zval_ptr_dtor(&tmp_val); +- _php_pgsql_free_params(params, num_params); ++ _php_pgsql_free_params(params, i); + RETURN_FALSE; + } + params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); +@@ -5179,7 +5179,7 @@ PHP_FUNCTION(pg_send_execute) + if (Z_TYPE(tmp_val) != IS_STRING) { + php_error_docref(NULL, E_WARNING,"Error converting parameter"); + zval_ptr_dtor(&tmp_val); +- _php_pgsql_free_params(params, num_params); ++ _php_pgsql_free_params(params, i); + RETURN_FALSE; + } + params[i] = estrndup(Z_STRVAL(tmp_val), Z_STRLEN(tmp_val)); +diff --git a/ext/pgsql/tests/bug81720.phpt b/ext/pgsql/tests/bug81720.phpt +new file mode 100644 +index 0000000000..d79f1fcdd6 +--- /dev/null ++++ b/ext/pgsql/tests/bug81720.phpt +@@ -0,0 +1,27 @@ ++--TEST-- ++Bug #81720 (Uninitialized array in pg_query_params() leading to RCE) ++--SKIPIF-- ++<?php include("skipif.inc"); ?> ++--FILE-- ++<?php ++include('config.inc'); ++ ++$conn = pg_connect($conn_str); ++ ++try { ++ pg_query_params($conn, 'SELECT $1, $2', [1, new stdClass()]); ++} catch (Throwable $ex) { ++ echo $ex->getMessage(), PHP_EOL; ++} ++ ++try { ++ pg_send_prepare($conn, "my_query", 'SELECT $1, $2'); ++ pg_get_result($conn); ++ pg_send_execute($conn, "my_query", [1, new stdClass()]); ++} catch (Throwable $ex) { ++ echo $ex->getMessage(), PHP_EOL; ++} ++?> ++--EXPECT-- ++Object of class stdClass could not be converted to string ++Object of class stdClass could not be converted to string +-- +2.35.3 + diff --git a/php-bug81726.patch b/php-bug81726.patch new file mode 100644 index 0000000..fd43dac --- /dev/null +++ b/php-bug81726.patch @@ -0,0 +1,179 @@ +From 96fda78bcddd1d793cf2d0ee463dbb49621b577f Mon Sep 17 00:00:00 2001 +From: "Christoph M. Becker" <cmbecker69@gmx.de> +Date: Mon, 25 Jul 2022 15:58:59 +0200 +Subject: [PATCH] Fix #81726: phar wrapper: DOS when using quine gzip file + +The phar wrapper needs to uncompress the file; the uncompressed file +might be compressed, so the wrapper implementation loops. This raises +potential DOS issues regarding too deep or even infinite recursion (the +latter are called compressed file quines[1]). We avoid that by +introducing a recursion limit; we choose the somewhat arbitrary limit +`3`. + +This issue has been reported by real_as3617 and gPayl0ad. + +[1] <https://honno.dev/gzip-quine/> + +(cherry picked from commit 404e8bdb68350931176a5bdc86fc417b34fb583d) +--- + NEWS | 2 ++ + ext/phar/phar.c | 16 +++++++++++----- + ext/phar/tests/bug81726.gz | Bin 0 -> 204 bytes + ext/phar/tests/bug81726.phpt | 14 ++++++++++++++ + 4 files changed, 27 insertions(+), 5 deletions(-) + create mode 100644 ext/phar/tests/bug81726.gz + create mode 100644 ext/phar/tests/bug81726.phpt + +diff --git a/NEWS b/NEWS +index 87b67643f3..fe4cb9c484 100644 +--- a/NEWS ++++ b/NEWS +@@ -4,6 +4,8 @@ PHP NEWS + Backported from 7.4.31 + + - Core: ++ . Fixed bug #81726: phar wrapper: DOS when using quine gzip file. ++ (CVE-2022-31628). (cmb) + . Fixed bug #81727: Don't mangle HTTP variable names that clash with ones + that have a specific semantic meaning. (CVE-2022-31629). (Derick) + +diff --git a/ext/phar/phar.c b/ext/phar/phar.c +index 44e40d98d1..9360658cd7 100644 +--- a/ext/phar/phar.c ++++ b/ext/phar/phar.c +@@ -1593,7 +1593,8 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + const char zip_magic[] = "PK\x03\x04"; + const char gz_magic[] = "\x1f\x8b\x08"; + const char bz_magic[] = "BZh"; +- char *pos, test = '\0'; ++ char *pos; ++ int recursion_count = 3; // arbitrary limit to avoid too deep or even infinite recursion + const int window_size = 1024; + char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */ + const zend_long readsize = sizeof(buffer) - sizeof(token); +@@ -1621,8 +1622,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") + } + +- if (!test) { +- test = '\1'; ++ if (recursion_count) { + pos = buffer+tokenlen; + if (!memcmp(pos, gz_magic, 3)) { + char err = 0; +@@ -1682,7 +1682,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + compression = PHAR_FILE_COMPRESSED_GZ; + + /* now, start over */ +- test = '\0'; ++ if (!--recursion_count) { ++ MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\""); ++ break; ++ } + continue; + } else if (!memcmp(pos, bz_magic, 3)) { + php_stream_filter *filter; +@@ -1720,7 +1723,10 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + compression = PHAR_FILE_COMPRESSED_BZ2; + + /* now, start over */ +- test = '\0'; ++ if (!--recursion_count) { ++ MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\""); ++ break; ++ } + continue; + } + +From 535c3f592d020a3a43f4ce3577e505d64297b6e8 Mon Sep 17 00:00:00 2001 +From: "Christoph M. Becker" <cmbecker69@gmx.de> +Date: Tue, 27 Sep 2022 17:43:40 +0200 +Subject: [PATCH] Fix regression introduced by fixing bug 81726 + +When a tar phar is created, `phar_open_from_fp()` is also called, but +since the file has just been created, none of the format checks can +succeed, so we continue to loop, but must not check again for the +format. Therefore, we bring back the old `test` variable. + +Closes GH-9620. + +(cherry picked from commit 432bf196d59bcb661fcf9cb7029cea9b43f490af) +--- + ext/phar/phar.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/ext/phar/phar.c b/ext/phar/phar.c +index 9360658cd7..1437525245 100644 +--- a/ext/phar/phar.c ++++ b/ext/phar/phar.c +@@ -1593,7 +1593,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + const char zip_magic[] = "PK\x03\x04"; + const char gz_magic[] = "\x1f\x8b\x08"; + const char bz_magic[] = "BZh"; +- char *pos; ++ char *pos, test = '\0'; + int recursion_count = 3; // arbitrary limit to avoid too deep or even infinite recursion + const int window_size = 1024; + char buffer[1024 + sizeof(token)]; /* a 1024 byte window + the size of the halt_compiler token (moving window) */ +@@ -1622,7 +1622,8 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") + } + +- if (recursion_count) { ++ if (!test && recursion_count) { ++ test = '\1'; + pos = buffer+tokenlen; + if (!memcmp(pos, gz_magic, 3)) { + char err = 0; +@@ -1682,6 +1683,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + compression = PHAR_FILE_COMPRESSED_GZ; + + /* now, start over */ ++ test = '\0'; + if (!--recursion_count) { + MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\""); + break; +@@ -1723,6 +1725,7 @@ static int phar_open_from_fp(php_stream* fp, char *fname, size_t fname_len, char + compression = PHAR_FILE_COMPRESSED_BZ2; + + /* now, start over */ ++ test = '\0'; + if (!--recursion_count) { + MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\""); + break; +-- +2.37.3 + +From 9d32d284b25f5df75780911a47b3c23cbaac1761 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Fri, 30 Sep 2022 09:22:14 +0200 +Subject: [PATCH] fix NEWS + +--- + NEWS | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/NEWS b/NEWS +index fe4cb9c484..b7a19aea19 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,14 +1,16 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +-Backported from 7.4.31 ++Backported from 7.4.32 + + - Core: +- . Fixed bug #81726: phar wrapper: DOS when using quine gzip file. +- (CVE-2022-31628). (cmb) + . Fixed bug #81727: Don't mangle HTTP variable names that clash with ones + that have a specific semantic meaning. (CVE-2022-31629). (Derick) + ++- Phar: ++ . Fixed bug #81726: phar wrapper: DOS when using quine gzip file. ++ (CVE-2022-31628). (cmb) ++ + Backported from 7.4.30 + + - mysqlnd: diff --git a/php-bug81727.patch b/php-bug81727.patch new file mode 100644 index 0000000..381a9e8 --- /dev/null +++ b/php-bug81727.patch @@ -0,0 +1,77 @@ +From 8b300e157e92b0e945ad813d608f076b5323d721 Mon Sep 17 00:00:00 2001 +From: Derick Rethans <github@derickrethans.nl> +Date: Fri, 9 Sep 2022 16:54:03 +0100 +Subject: [PATCH] Fix #81727: Don't mangle HTTP variable names that clash with + ones that have a specific semantic meaning. + +(cherry picked from commit 0611be4e82887cee0de6c4cbae320d34eec946ca) +--- + NEWS | 6 ++++++ + ext/standard/tests/bug81727.phpt | 15 +++++++++++++++ + main/php_variables.c | 14 ++++++++++++++ + 3 files changed, 35 insertions(+) + create mode 100644 ext/standard/tests/bug81727.phpt + +diff --git a/NEWS b/NEWS +index fd227bd33a..87b67643f3 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,12 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 7.4.31 ++ ++- Core: ++ . Fixed bug #81727: Don't mangle HTTP variable names that clash with ones ++ that have a specific semantic meaning. (CVE-2022-31629). (Derick) ++ + Backported from 7.4.30 + + - mysqlnd: +diff --git a/ext/standard/tests/bug81727.phpt b/ext/standard/tests/bug81727.phpt +new file mode 100644 +index 0000000000..71a9cb46c8 +--- /dev/null ++++ b/ext/standard/tests/bug81727.phpt +@@ -0,0 +1,15 @@ ++--TEST-- ++Bug #81727: $_COOKIE name starting with ..Host/..Secure should be discarded ++--COOKIE-- ++..Host-test=ignore; __Host-test=correct; . Secure-test=ignore; . Elephpant=Awesome; ++--FILE-- ++<?php ++var_dump($_COOKIE); ++?> ++--EXPECT-- ++array(2) { ++ ["__Host-test"]=> ++ string(7) "correct" ++ ["__Elephpant"]=> ++ string(7) "Awesome" ++} +diff --git a/main/php_variables.c b/main/php_variables.c +index ca015352d2..f2d0c3bd98 100644 +--- a/main/php_variables.c ++++ b/main/php_variables.c +@@ -115,6 +115,20 @@ 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); diff --git a/php-bug81738.patch b/php-bug81738.patch new file mode 100644 index 0000000..0c6aaa8 --- /dev/null +++ b/php-bug81738.patch @@ -0,0 +1,128 @@ +From de4517ad607df8d4cb3735228b39e4a48f95556c Mon Sep 17 00:00:00 2001 +From: Stanislav Malyshev <smalyshev@gmail.com> +Date: Thu, 20 Oct 2022 23:57:35 -0600 +Subject: [PATCH] Fix bug #81738 (buffer overflow in hash_update() on long + parameter) + +--- + NEWS | 6 ++++++ + ext/hash/sha3/generic32lc/KeccakSponge.inc | 14 ++++++++------ + ext/hash/sha3/generic64lc/KeccakSponge.inc | 14 ++++++++------ + 3 files changed, 22 insertions(+), 12 deletions(-) + +diff --git a/NEWS b/NEWS +index b7a19aea19..ce48558ad1 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,12 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 7.4.33 ++ ++- Hash: ++ . Fixed bug #81738: buffer overflow in hash_update() on long parameter. ++ (CVE-2022-37454) (nicky at mouha dot be) ++ + Backported from 7.4.32 + + - Core: +diff --git a/ext/hash/sha3/generic32lc/KeccakSponge.inc b/ext/hash/sha3/generic32lc/KeccakSponge.inc +index 42a15aac6d..f8c42ff788 100644 +--- a/ext/hash/sha3/generic32lc/KeccakSponge.inc ++++ b/ext/hash/sha3/generic32lc/KeccakSponge.inc +@@ -160,7 +160,7 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat + i = 0; + curData = data; + while(i < dataByteLen) { +- if ((instance->byteIOIndex == 0) && (dataByteLen >= (i + rateInBytes))) { ++ if ((instance->byteIOIndex == 0) && (dataByteLen-i >= rateInBytes)) { + #ifdef SnP_FastLoop_Absorb + /* processing full blocks first */ + if ((rateInBytes % (SnP_width/200)) == 0) { +@@ -186,9 +186,10 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat + } + else { + /* normal lane: using the message queue */ +- partialBlock = (unsigned int)(dataByteLen - i); +- if (partialBlock+instance->byteIOIndex > rateInBytes) ++ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) + partialBlock = rateInBytes-instance->byteIOIndex; ++ else ++ partialBlock = (unsigned int)(dataByteLen - i); + #ifdef KeccakReference + displayBytes(1, "Block to be absorbed (part)", curData, partialBlock); + #endif +@@ -263,7 +264,7 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte + i = 0; + curData = data; + while(i < dataByteLen) { +- if ((instance->byteIOIndex == rateInBytes) && (dataByteLen >= (i + rateInBytes))) { ++ if ((instance->byteIOIndex == rateInBytes) && (dataByteLen-i >= rateInBytes)) { + for(j=dataByteLen-i; j>=rateInBytes; j-=rateInBytes) { + SnP_Permute(instance->state); + SnP_ExtractBytes(instance->state, curData, 0, rateInBytes); +@@ -280,9 +281,10 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte + SnP_Permute(instance->state); + instance->byteIOIndex = 0; + } +- partialBlock = (unsigned int)(dataByteLen - i); +- if (partialBlock+instance->byteIOIndex > rateInBytes) ++ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) + partialBlock = rateInBytes-instance->byteIOIndex; ++ else ++ partialBlock = (unsigned int)(dataByteLen - i); + i += partialBlock; + + SnP_ExtractBytes(instance->state, curData, instance->byteIOIndex, partialBlock); +diff --git a/ext/hash/sha3/generic64lc/KeccakSponge.inc b/ext/hash/sha3/generic64lc/KeccakSponge.inc +index 42a15aac6d..f8c42ff788 100644 +--- a/ext/hash/sha3/generic64lc/KeccakSponge.inc ++++ b/ext/hash/sha3/generic64lc/KeccakSponge.inc +@@ -160,7 +160,7 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat + i = 0; + curData = data; + while(i < dataByteLen) { +- if ((instance->byteIOIndex == 0) && (dataByteLen >= (i + rateInBytes))) { ++ if ((instance->byteIOIndex == 0) && (dataByteLen-i >= rateInBytes)) { + #ifdef SnP_FastLoop_Absorb + /* processing full blocks first */ + if ((rateInBytes % (SnP_width/200)) == 0) { +@@ -186,9 +186,10 @@ int SpongeAbsorb(SpongeInstance *instance, const unsigned char *data, size_t dat + } + else { + /* normal lane: using the message queue */ +- partialBlock = (unsigned int)(dataByteLen - i); +- if (partialBlock+instance->byteIOIndex > rateInBytes) ++ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) + partialBlock = rateInBytes-instance->byteIOIndex; ++ else ++ partialBlock = (unsigned int)(dataByteLen - i); + #ifdef KeccakReference + displayBytes(1, "Block to be absorbed (part)", curData, partialBlock); + #endif +@@ -263,7 +264,7 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte + i = 0; + curData = data; + while(i < dataByteLen) { +- if ((instance->byteIOIndex == rateInBytes) && (dataByteLen >= (i + rateInBytes))) { ++ if ((instance->byteIOIndex == rateInBytes) && (dataByteLen-i >= rateInBytes)) { + for(j=dataByteLen-i; j>=rateInBytes; j-=rateInBytes) { + SnP_Permute(instance->state); + SnP_ExtractBytes(instance->state, curData, 0, rateInBytes); +@@ -280,9 +281,10 @@ int SpongeSqueeze(SpongeInstance *instance, unsigned char *data, size_t dataByte + SnP_Permute(instance->state); + instance->byteIOIndex = 0; + } +- partialBlock = (unsigned int)(dataByteLen - i); +- if (partialBlock+instance->byteIOIndex > rateInBytes) ++ if (dataByteLen-i > rateInBytes-instance->byteIOIndex) + partialBlock = rateInBytes-instance->byteIOIndex; ++ else ++ partialBlock = (unsigned int)(dataByteLen - i); + i += partialBlock; + + SnP_ExtractBytes(instance->state, curData, instance->byteIOIndex, partialBlock); +-- +2.37.3 + diff --git a/php-bug81740.patch b/php-bug81740.patch new file mode 100644 index 0000000..73bc74a --- /dev/null +++ b/php-bug81740.patch @@ -0,0 +1,86 @@ +From 5a0e763a3dcf5ae22dbd6d35757255d6c33dbdde 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) +(cherry picked from commit 7cb160efe19d3dfb8b92629805733ea186b55050) +--- + 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 a0f3e104f2..77720c618f 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 0000000000..99fb07c304 +--- /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 144d79977c7e2a410a705f550dbc8ee754dd1cb3 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 + +(cherry picked from commit 7328f3a0344806b846bd05657bdce96e47810bf0) +--- + NEWS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/NEWS b/NEWS +index ce48558ad1..4de34f7876 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,12 @@ + PHP NEWS + ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + ++Backported from 8.0.27 ++ ++- PDO/SQLite: ++ . Fixed bug #81740 (PDO::quote() may return unquoted string). ++ (CVE-2022-31631) (cmb) ++ + Backported from 7.4.33 + + - Hash: +-- +2.38.1 + diff --git a/php-bug81744.patch b/php-bug81744.patch new file mode 100644 index 0000000..e11afe1 --- /dev/null +++ b/php-bug81744.patch @@ -0,0 +1,190 @@ +From e78b2696c7baf48e5d0898420368555ff4b99830 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/8] 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) +(cherry picked from commit 7437aaae38cf4b3357e7580f9e22fd4a403b6c23) +--- + 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 c1f945f29e..aa7e1bc2e6 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 0000000000..32e335f4b0 +--- /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 ae619b4ba0b325650d09447a48e18c578d52c681 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/8] crypt: Fix possible buffer overread in php_crypt() + +(cherry picked from commit a92acbad873a05470af1a47cb785a18eadd827b5) +(cherry picked from commit ed0281b588a6840cb95f3134a4e68847a3be5bb7) +--- + 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 fec04ec584..9787b92f57 100644 +--- a/ext/standard/crypt.c ++++ b/ext/standard/crypt.c +@@ -154,6 +154,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 0000000000..085bc8a239 +--- /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-bug81746.patch b/php-bug81746.patch new file mode 100644 index 0000000..06f6dbb --- /dev/null +++ b/php-bug81746.patch @@ -0,0 +1,100 @@ +From 31090aae8ff1f150cb822e69e0871af166d463c5 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/8] 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) +(cherry picked from commit 887cd0710ad856a0d22c329b6ea6c71ebd8621ae) +--- + 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 0e15e7a110..3f34e5370d 100644 +--- a/ext/dom/document.c ++++ b/ext/dom/document.c +@@ -1357,7 +1357,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 4d4e7348c9..e03273709f 100644 +--- a/ext/xmlreader/php_xmlreader.c ++++ b/ext/xmlreader/php_xmlreader.c +@@ -1029,7 +1029,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 1509c006d7..4b15f052ef 100644 +--- a/main/fopen_wrappers.c ++++ b/main/fopen_wrappers.c +@@ -133,10 +133,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 f251242c25493685d7030588f99c17193d3f667d 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/8] NEWS + +(cherry picked from commit 614468ce4056c0ef93aae09532dcffdf65b594b5) +--- + NEWS | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/NEWS b/NEWS +index 4de34f7876..80d589e4d9 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..fea49a2 --- /dev/null +++ b/php-cve-2023-0662.patch @@ -0,0 +1,148 @@ +From 49bef9efd65ab7456210f8c23b74fdd0f3a8292a 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/8] Fix repeated warning for file uploads limit exceeding + +(cherry picked from commit 3a2fdef1ae38881110006616ee1f0534b082ca45) +--- + main/rfc1867.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/main/rfc1867.c b/main/rfc1867.c +index 3f91fe6fb4..bf28334a34 100644 +--- a/main/rfc1867.c ++++ b/main/rfc1867.c +@@ -930,7 +930,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 d380b11cb9b73e154dc79955a73139cc6f26dd71 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/8] 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. + +(cherry picked from commit 8ec78d28d20c82c75c4747f44c52601cfdb22516) +--- + main/main.c | 1 + + main/rfc1867.c | 11 +++++++++++ + 2 files changed, 12 insertions(+) + +diff --git a/main/main.c b/main/main.c +index f20a786743..c3939b3cb5 100644 +--- a/main/main.c ++++ b/main/main.c +@@ -782,6 +782,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 bf28334a34..d33809514e 100644 +--- a/main/rfc1867.c ++++ b/main/rfc1867.c +@@ -702,6 +702,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; +@@ -723,6 +724,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) { +@@ -807,6 +813,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 981935217e6d7112ffa1e7a467bed446faa4ffbd 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/8] NEWS + +(cherry picked from commit 472db3ee3a00ac00d36019eee0b3b7362334481c) +--- + NEWS | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/NEWS b/NEWS +index 80d589e4d9..45743bf7f0 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 3cdb8167a4a86dc371321542f272cd220807482e Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 14 Feb 2023 11:47:22 +0100 +Subject: [PATCH 8/8] fix NEWS, not FPM specific + +(cherry picked from commit c04f310440a906fc4ca885f4ecf6e3e4cd36edc7) +--- + NEWS | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/NEWS b/NEWS +index 45743bf7f0..a9d7871ba3 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..497b53e --- /dev/null +++ b/php-cve-2023-3247.patch @@ -0,0 +1,150 @@ +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 e3a9afdbe9..912b8e341d 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); +From ec8afe8e559342b61a0498dcab8ce59dc6319d3e 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 + +(cherry picked from commit f3021d66d7bb42d2578530cc94f9bde47e58eb10) +--- + NEWS | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/NEWS b/NEWS +index 7ccc61e6e4..1950d841a5 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..604438c --- /dev/null +++ b/php-cve-2023-3823.patch @@ -0,0 +1,90 @@ +From 3f14261065e4c0552afa9cb16411475050a41c2c 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) +(cherry picked from commit c398fe98c044c8e7c23135acdc38d4ef7bedc983) +--- + 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 70e696a167..b60af236ef 100644 +--- a/ext/phar/dirstream.c ++++ b/ext/phar/dirstream.c +@@ -92,25 +92,28 @@ static int phar_dir_seek(php_stream *stream, zend_off_t offset, int whence, zend + */ + static size_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 0000000000..4e12f05fb6 +--- /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..8eea6f1 --- /dev/null +++ b/php-cve-2023-3824.patch @@ -0,0 +1,647 @@ +From 4fb61f06b1aff89a4d7e548c37ffa5bf573270c3 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) +(cherry picked from commit b3758bd21223b97c042cae7bd26a66cde081ea98) +--- + 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 3f34e5370d..238b1381cc 100644 +--- a/ext/dom/document.c ++++ b/ext/dom/document.c +@@ -1436,6 +1436,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; +@@ -1733,7 +1734,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 +@@ -1772,6 +1775,7 @@ PHP_FUNCTION(dom_document_validate) + + DOM_GET_OBJ(docp, id, xmlDocPtr, intern); + ++ PHP_LIBXML_SANITIZE_GLOBALS(validate); + cvp = xmlNewValidCtxt(); + + cvp->userData = NULL; +@@ -1783,6 +1787,7 @@ PHP_FUNCTION(dom_document_validate) + } else { + RETVAL_FALSE; + } ++ PHP_LIBXML_RESTORE_GLOBALS(validate); + + xmlFreeValidCtxt(cvp); + +@@ -1816,14 +1821,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; + } +@@ -1844,6 +1853,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; +@@ -1864,11 +1874,13 @@ static void _dom_document_schema_validate(INTERNAL_FUNCTION_PARAMETERS, int type + } + #endif + ++ 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; +@@ -1938,12 +1950,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; +@@ -2042,6 +2056,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 0c289565a2..f12d191ddf 100644 +--- a/ext/dom/documentfragment.c ++++ b/ext/dom/documentfragment.c +@@ -132,7 +132,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 0000000000..b28afd4694 +--- /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 7cc7271db2..bb2ef2d606 100644 +--- a/ext/libxml/php_libxml.h ++++ b/ext/libxml/php_libxml.h +@@ -120,6 +120,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 ab394b5c83..de718ced93 100644 +--- a/ext/simplexml/simplexml.c ++++ b/ext/simplexml/simplexml.c +@@ -2175,7 +2175,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; +@@ -2229,7 +2231,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; +@@ -2279,7 +2283,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 0000000000..2152e01232 +--- /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 a1ab525de3..7ebfbc2f71 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; +@@ -143,6 +144,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 450bb1b52c..82dff1f75a 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; +@@ -476,6 +477,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *m + parser->parser->charset = XML_CHAR_ENCODING_NONE; + #endif + ++ php_libxml_sanitize_parse_ctxt_options(parser->parser); + #if LIBXML_VERSION >= 20703 + xmlCtxtUseOptions(parser->parser, XML_PARSE_OLDSAX); + #endif +diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c +index e03273709f..ee219aea36 100644 +--- a/ext/xmlreader/php_xmlreader.c ++++ b/ext/xmlreader/php_xmlreader.c +@@ -290,6 +290,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, +@@ -298,6 +299,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; + } +@@ -870,7 +872,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) { +@@ -948,7 +952,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; +@@ -1068,6 +1074,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) { +@@ -1088,9 +1095,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 0000000000..e9ffb04c2b +--- /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 182aab68d6..af72dab248 100644 +--- a/ext/xsl/xsltprocessor.c ++++ b/ext/xsl/xsltprocessor.c +@@ -395,7 +395,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; + +@@ -417,13 +417,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 79a97d0e2b93c40c3728d587046266989c5acc42 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 1 Aug 2023 07:37:25 +0200 +Subject: [PATCH 3/4] backport zend_test changes + (zend_test_override_libxml_global_state) + +(cherry picked from commit 24e669e790e6aebd219c9a9fa19017455c8646b4) +--- + ...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 b28afd4694..7fc2a249ac 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 2152e01232..54f9d4941e 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 e9ffb04c2b..b0120b325e 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 87dcc90220..7f605773b7 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 */ +@@ -266,6 +285,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 + +From 3cf7c2b10e577136b267f2d90bfdff6743271c5c Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Tue, 1 Aug 2023 07:22:33 +0200 +Subject: [PATCH 4/4] NEWS + +(cherry picked from commit ef1d507acf7be23d7624dc3c891683b2218feb51) +--- + NEWS | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/NEWS b/NEWS +index 1950d841a5..05d9ca8f4c 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 + diff --git a/php-cve-2024-11233.patch b/php-cve-2024-11233.patch new file mode 100644 index 0000000..29558c0 --- /dev/null +++ b/php-cve-2024-11233.patch @@ -0,0 +1,69 @@ +From 7065fa31a468139f07b40f7036ce4761037dafd2 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 6/8] 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) +(cherry picked from commit 44a5975f83a02eb8169d12af912e6222b28216d0) +--- + 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 9b2dcf08aa..0e23d58826 100644 +--- a/ext/standard/filters.c ++++ b/ext/standard/filters.c +@@ -1121,6 +1121,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; +@@ -1134,15 +1137,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 0000000000..8fdcce8ff2 +--- /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..443805a --- /dev/null +++ b/php-cve-2024-11234.patch @@ -0,0 +1,96 @@ +From dcb89ed9d0217510f3906ce0c517f704e6bd80dc Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Fri, 8 Nov 2024 23:43:47 +0100 +Subject: [PATCH 4/8] 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) +(cherry picked from commit 494de65139592da0e5e5b6fdf198c2f9c762f4d6) +--- + 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 5ac89d9ea2..30d061eec5 100644 +--- a/ext/standard/http_fopen_wrapper.c ++++ b/ext/standard/http_fopen_wrapper.c +@@ -187,6 +187,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) +@@ -206,6 +211,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 +@@ -386,12 +398,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 0000000000..6e68f67654 +--- /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..277b20a --- /dev/null +++ b/php-cve-2024-11236.patch @@ -0,0 +1,147 @@ +From 5e7cd3e7ed7c894550ca35514708ffe1874a31ad 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/8] Fix GHSA-5hqh-c84r-qjcv: Integer overflow in the dblib + quoter causing OOB writes + +(cherry picked from commit d9baa9fed8c3ba692a36b388c0c7762e5102e2e0) +(cherry picked from commit 5d9e54065ed18c51e4f25d8900635f90810c7394) +(cherry picked from commit 97546df8d6900b115536c17af9213f1da837b82e) +--- + 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 b9277a30e3..b4c65aa4ee 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 0000000000..431c61951e +--- /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 72d4c4e435544c2d87d634188d480099345b601b 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/8] Fix GHSA-5hqh-c84r-qjcv: Integer overflow in the firebird + quoter causing OOB writes + +(cherry picked from commit 69c5f68fdc3deed9ebce2cc44b4bf5e0c47cd28f) +(cherry picked from commit b4f73be75dbdde970a18cc7a636898b10400fb3f) +(cherry picked from commit 0530cbfe5c3044537de52d8382eba5d69dbac726) +--- + 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 e2c5ea915a..9ebafe6957 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 + +From 37056ad634d9c44bac0d6c8e730eafaec1344840 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Fri, 22 Nov 2024 15:24:16 +0100 +Subject: [PATCH 3/8] backport ZSTR_MAX_LEN + +--- + Zend/zend_string.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/Zend/zend_string.h b/Zend/zend_string.h +index 396b63b266..8ef979f37e 100644 +--- a/Zend/zend_string.h ++++ b/Zend/zend_string.h +@@ -76,6 +76,9 @@ END_EXTERN_C() + + #define _ZSTR_STRUCT_SIZE(len) (_ZSTR_HEADER_SIZE + len + 1) + ++#define ZSTR_MAX_OVERHEAD (ZEND_MM_ALIGNED_SIZE(_ZSTR_HEADER_SIZE + 1)) ++#define ZSTR_MAX_LEN (SIZE_MAX - ZSTR_MAX_OVERHEAD) ++ + #define ZSTR_ALLOCA_ALLOC(str, _len, use_heap) do { \ + (str) = (zend_string *)do_alloca(ZEND_MM_ALIGNED_SIZE_EX(_ZSTR_STRUCT_SIZE(_len), 8), (use_heap)); \ + GC_SET_REFCOUNT(str, 1); \ +-- +2.47.0 + diff --git a/php-cve-2024-2756.patch b/php-cve-2024-2756.patch new file mode 100644 index 0000000..a8607d9 --- /dev/null +++ b/php-cve-2024-2756.patch @@ -0,0 +1,195 @@ +From 46b570a1e4aeb4a414898fcc09503ac388d16256 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) +(cherry picked from commit a6c1c62a25ac23b08a86af11d68f0e2eaafc102b) +--- + 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 0000000000..77fcb68089 +--- /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 f2d0c3bd98..d0ebd50ec8 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 8642473b624f809b768180b104c013f74e3a99a0 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) +(cherry picked from commit dcdd49ef3bfbd8ccc778850d6a0f9b98adf625d4) +--- + NEWS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/NEWS b/NEWS +index 05d9ca8f4c..e26f978213 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..7b2cc15 --- /dev/null +++ b/php-cve-2024-3096.patch @@ -0,0 +1,83 @@ +From 747100905eceffb1f67096b437001e42900eb6bb 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) +(cherry picked from commit 4a7ceb9d6427f8d368f1a8739267b1f8310ec201) +--- + 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 5cf0d397f5..79454e0a1e 100644 +--- a/ext/standard/password.c ++++ b/ext/standard/password.c +@@ -467,6 +467,11 @@ PHP_FUNCTION(password_hash) + cost = zval_get_long(option_buffer); + } + ++ 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 (cost < 4 || cost > 31) { + php_error_docref(NULL, E_WARNING, "Invalid bcrypt cost parameter specified: " ZEND_LONG_FMT, cost); + RETURN_NULL(); +diff --git a/ext/standard/tests/password/password_bcrypt_errors.phpt b/ext/standard/tests/password/password_bcrypt_errors.phpt +index a0826080e6..f95b72670a 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 fbeed182bb0b0c4c453e064198b5cc3814a10de0 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) +(cherry picked from commit 027bdbc636632be49ecfad8d4191509faacb34ac) +--- + NEWS | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/NEWS b/NEWS +index e26f978213..6ad89d2e8e 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..65405ee --- /dev/null +++ b/php-cve-2024-5458.patch @@ -0,0 +1,182 @@ +From 76362f9526afbd5565003d981f9507aaf62337f2 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) +(cherry picked from commit 08be64e40197fc12dca5f802d16748d9c3cb4cb4) +--- + 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 3f314fefa0..59b7cb8ff8 100644 +--- a/ext/filter/logical_filters.c ++++ b/ext/filter/logical_filters.c +@@ -79,7 +79,7 @@ + #define FORMAT_IPV4 4 + #define FORMAT_IPV6 6 + +-static int _php_filter_validate_ipv6(char *str, size_t str_len); ++static int _php_filter_validate_ipv6(const char *str, size_t str_len); + + static int php_filter_parse_int(const char *str, size_t str_len, zend_long *ret) { /* {{{ */ + zend_long ctx_value; +@@ -548,6 +548,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); ++} ++ + void php_filter_validate_url(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ + { + php_url *url; +@@ -573,7 +581,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) { +@@ -582,17 +590,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)) { +- 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 + } +@@ -726,15 +731,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) /* {{{ */ ++static int _php_filter_validate_ipv6(const char *str, size_t str_len) /* {{{ */ + { + int compressed = 0; + int blocks = 0; + int n; + 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 0000000000..0092408ee5 +--- /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 cfe1b1acead13b6af163f3ce947d3a1dbded82a0 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) +(cherry picked from commit ec1d5e6468479e64acc7fb8cb955f053b64ea9a0) +--- + NEWS | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/NEWS b/NEWS +index 6ad89d2e8e..1075db151f 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..4129b76 --- /dev/null +++ b/php-cve-2024-8925.patch @@ -0,0 +1,233 @@ +From 08f0adf0700f8bbaa4fd75b7a694bbd9ae45300d 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 3/8] 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) +(cherry picked from commit a24ac172f52e75101913f3946cfa5515f723c99f) +--- + 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 d33809514e..3415db2ff0 100644 +--- a/main/rfc1867.c ++++ b/main/rfc1867.c +@@ -767,6 +767,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 0000000000..adf72a361a +--- /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 0000000000..af81916370 +--- /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 79eace3a64544088738d2fd341407cc32fe3ecaf Mon Sep 17 00:00:00 2001 +From: Jakub Zelenka <bukka@php.net> +Date: Mon, 23 Sep 2024 18:54:31 +0100 +Subject: [PATCH 6/8] Skip GHSA-9pqp-7h25-4f32 test on Windows + +(cherry picked from commit c70e25630832fa10d421328eed2b8e1a36af7a64) +(cherry picked from commit c75683864f6e4188439e8ca2adbb05824918be12) +(cherry picked from commit 2fd1b83817d20523e72bef3ad524cd5797f51acf) +--- + 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 af81916370..29bcb6557d 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 87ed9429a17e38daec4dcfd7a3c3db194197ccb3 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 26 Sep 2024 15:49:03 +0200 +Subject: [PATCH 8/8] adapt GHSA-9pqp-7h25-4f32 test for 7.x + +(cherry picked from commit 29065f33f37f99ba33254cb23c941647bcd7372c) +--- + tests/basic/GHSA-9pqp-7h25-4f32.phpt | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/tests/basic/GHSA-9pqp-7h25-4f32.phpt b/tests/basic/GHSA-9pqp-7h25-4f32.phpt +index 29bcb6557d..b913edc1c4 100644 +--- a/tests/basic/GHSA-9pqp-7h25-4f32.phpt ++++ b/tests/basic/GHSA-9pqp-7h25-4f32.phpt +@@ -21,8 +21,10 @@ function test($boundaryLen) { + getenv('TEST_PHP_CGI_EXECUTABLE'), + '-C', + '-n', ++ '-dlog_errors=1', + __DIR__ . '/GHSA-9pqp-7h25-4f32.inc', + ]; ++ $cmd = implode(' ', $cmd); + + $boundary = str_repeat('A', $boundaryLen); + $body = "" +@@ -92,11 +94,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..9c1a703 --- /dev/null +++ b/php-cve-2024-8926.patch @@ -0,0 +1,203 @@ +From 3ee3a1f5c72921e99da8a8da096c0d11f89735d1 Mon Sep 17 00:00:00 2001 +From: Jan Ehrhardt <github@ehrhardt.nl> +Date: Wed, 5 Jun 2024 20:27:51 +0200 +Subject: [PATCH 1/8] 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 37f8eac6e0..47fbc410bc 100644 +--- a/sapi/cgi/cgi_main.c ++++ b/sapi/cgi/cgi_main.c +@@ -1835,8 +1835,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)); +@@ -1846,6 +1851,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 0000000000..fd2fcdfbf8 +--- /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 5c2e7a769bf834e9e2890919067f3efd4d605b13 Mon Sep 17 00:00:00 2001 +From: Jan Ehrhardt <github@ehrhardt.nl> +Date: Sun, 9 Jun 2024 20:11:49 +0200 +Subject: [PATCH 2/8] NEWS: Add backport from 8.1.29 + +--- + NEWS | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/NEWS b/NEWS +index 1075db151f..db10d85a5b 100644 +--- a/NEWS ++++ b/NEWS +@@ -3,6 +3,10 @@ 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) +-- +2.46.1 + +From 89c66773413267949de995671bfb4bd03c34fbf9 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 4/8] 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) +(cherry picked from commit 1158d06f0b20532ab7309cb20f0be843f9662e3c) +--- + 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 47fbc410bc..62a01d51fe 100644 +--- a/sapi/cgi/cgi_main.c ++++ b/sapi/cgi/cgi_main.c +@@ -1784,7 +1784,6 @@ int main(int argc, char *argv[]) + int status = 0; + #endif + char *query_string; +- char *decoded_query_string; + int skip_getopt = 0; + + #ifdef HAVE_SIGNAL_H +@@ -1840,10 +1839,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 */ +@@ -1852,22 +1856,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..5937624 --- /dev/null +++ b/php-cve-2024-8927.patch @@ -0,0 +1,96 @@ +From 21e2b0ab382a898f627c97d39f5e5afc2431afe7 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 5/8] 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) +(cherry picked from commit c7308ba7cd0533501b40eba255602bb5e085550f) +--- + 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 62a01d51fe..3c01d4fb2c 100644 +--- a/sapi/cgi/cgi_main.c ++++ b/sapi/cgi/cgi_main.c +@@ -1946,18 +1946,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 + +From b4667e4ebe241d95775962b1e8b24788e7945de2 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 26 Sep 2024 11:50:54 +0200 +Subject: [PATCH 7/8] NEWS for 8.1.30 backports + +(cherry picked from commit af3fb385e7b328ab89db26ec712d89c7096f0743) +(cherry picked from commit 1154fbd3ddfa418bf2492c5366adaefb47c47737) +--- + NEWS | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/NEWS b/NEWS +index db10d85a5b..53404ef4c8 100644 +--- a/NEWS ++++ b/NEWS +@@ -1,6 +1,19 @@ + 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) ++ ++- 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 + diff --git a/php-cve-2024-8929.patch b/php-cve-2024-8929.patch new file mode 100644 index 0000000..a62be66 --- /dev/null +++ b/php-cve-2024-8929.patch @@ -0,0 +1,2906 @@ +From bb1dd9865d8163ee9f0449994cf086852ea1014e 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 + +(cherry picked from commit e8bc357123ea19c4e2390374f088c9d4941f19e6) +--- + 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 0000000000..b02fabc584 +--- /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 0000000000..db54a6c017 +--- /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 0000000000..77f2232eca +--- /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 0000000000..0b4db8ccec +--- /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 0000000000..f141a79bda +--- /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 0000000000..e43518217e +--- /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 0000000000..76158e940d +--- /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 0000000000..f53d5b83bd +--- /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 0000000000..03c9b045d7 +--- /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 0000000000..b1ec9aa51e +--- /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 0000000000..426d9ea7b3 +--- /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 0000000000..6db6952d42 +--- /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 0000000000..55bad4cc54 +--- /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 0000000000..06918c375f +--- /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 0000000000..524fe5e587 +--- /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 0000000000..d461ec24b8 +--- /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 acc73334be..289945ae36 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 10b4d09905..1ca3d535b4 100644 +--- a/ext/mysqlnd/mysqlnd_result.c ++++ b/ext/mysqlnd/mysqlnd_result.c +@@ -502,7 +502,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 1aee62c64e..53c4768ab5 100644 +--- a/ext/mysqlnd/mysqlnd_wireprotocol.c ++++ b/ext/mysqlnd/mysqlnd_wireprotocol.c +@@ -712,7 +712,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; +@@ -1106,6 +1113,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); +@@ -1262,23 +1280,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); +@@ -1439,8 +1450,10 @@ php_mysqlnd_rowp_read_binary_protocol(MYSQLND_ROW_BUFFER * row_buffer, zval * fi + unsigned int field_count, const MYSQLND_FIELD * fields_metadata, + zend_bool as_int_or_float, MYSQLND_STATS * 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; +@@ -1473,7 +1486,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) { +@@ -1530,7 +1557,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; + size_t data_size = row_buffer->size; +@@ -1551,9 +1578,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 4b9c5559d30291cae7abbbb12ffa20d3b375177f 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) +(cherry picked from commit aaeb9549a1bdfa787fc3d3a2d499b418d09a5387) +--- + 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 53c4768ab5..7036945540 100644 +--- a/ext/mysqlnd/mysqlnd_wireprotocol.c ++++ b/ext/mysqlnd/mysqlnd_wireprotocol.c +@@ -442,8 +442,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 1f246c5587bd49e207400bde80845566603ce67c 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) +(cherry picked from commit 83a0d005d51a44bbe77a178c387e2c9f042a335d) +--- + 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 1ca3d535b4..aea660fedf 100644 +--- a/ext/mysqlnd/mysqlnd_result.c ++++ b/ext/mysqlnd/mysqlnd_result.c +@@ -547,8 +547,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 9da49d38401c1b5a2dfbb8d1bf6f779fef74e44b 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) +(cherry picked from commit 606322b7f3475fb5980f7785789adfb9c381abbc) +--- + 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 db54a6c017..279aec6a2c 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 3d73240774358a265f9f2e18048fbfc95d7fe271 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) +(cherry picked from commit c308c94eefdbddb041ed3cf502ef5dd6969e14f1) +--- + 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 b02fabc584..1127f6c00e 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 d42971176d7a29c5366b2eb67eae65c6faf9c802 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 + +(cherry picked from commit 016ffd6131a6174fe5ca5f4af3c66ad9f59ed879) +--- + NEWS | 4 + + ext/mysqli/tests/fake_server.inc | 107 ++++++++++-------- + .../ghsa-h35g-vwh6-m678-auth-message.phpt | 3 +- + ...hsa-h35g-vwh6-m678-query-len-overflow.phpt | 2 +- + .../ghsa-h35g-vwh6-m678-stmt-row-bit.phpt | 2 +- + .../ghsa-h35g-vwh6-m678-stmt-row-date.phpt | 2 +- + ...ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt | 2 +- + .../ghsa-h35g-vwh6-m678-stmt-row-double.phpt | 2 +- + .../ghsa-h35g-vwh6-m678-stmt-row-float.phpt | 2 +- + .../ghsa-h35g-vwh6-m678-stmt-row-int.phpt | 2 +- + ...ghsa-h35g-vwh6-m678-stmt-row-no-space.phpt | 2 +- + .../ghsa-h35g-vwh6-m678-stmt-row-string.phpt | 2 +- + .../ghsa-h35g-vwh6-m678-stmt-row-time.phpt | 2 +- + ext/mysqli/tests/mysqli_change_user_new.phpt | 5 +- + ext/mysqli/tests/mysqli_pconn_max_links.phpt | 4 +- + ..._stmt_get_result_metadata_fetch_field.phpt | 2 +- + 16 files changed, 80 insertions(+), 65 deletions(-) + +diff --git a/NEWS b/NEWS +index c852608133..342c184c30 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 1127f6c00e..8a9a045eac 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,14 +107,17 @@ 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; + } + } + + class my_mysqli_fake_packet + { +- private array $data = array(); ++ private $data = array(); + + public function __get(string $name) + { +@@ -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,15 @@ 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)); ++ $func = function($s) { ++ return $s->to_bytes(); ++ }; ++ return implode('', array_map($func, $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 +581,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 +621,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 +641,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 +665,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 +682,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 +694,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 +715,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 +734,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 +789,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 +804,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 +817,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,9 +842,10 @@ 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]; ++ $command = implode(' ', $command); + + $descriptorspec = array( + 0 => array("pipe", "r"), +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 279aec6a2c..161c9a5b8e 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/ghsa-h35g-vwh6-m678-query-len-overflow.phpt b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt +index f141a79bda..6c443583b3 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-query-len-overflow.phpt +@@ -42,7 +42,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after end of packet in %s on line %A + [*] 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 +index e43518217e..f5fdf6fb2b 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-bit.phpt +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %A + [*] 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 +index 76158e940d..74f452d374 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-date.phpt +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %A + [*] 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 +index f53d5b83bd..6e8876508d 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-datetime.phpt +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %A + [*] 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 +index 03c9b045d7..f7a599af0e 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-double.phpt +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %A + [*] 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 +index b1ec9aa51e..4c28de66fa 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-float.phpt +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %A + [*] 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 +index 426d9ea7b3..4c7cb156a3 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-int.phpt +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %A + [*] 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 +index 6db6952d42..242669e3a1 100644 +--- 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 +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. No packet space left for the field in %s on line %A + [*] 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 +index 55bad4cc54..9433a811ba 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-string.phpt +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %A + [*] 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 +index 06918c375f..82c2014c2d 100644 +--- a/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt ++++ b/ext/mysqli/tests/ghsa-h35g-vwh6-m678-stmt-row-time.phpt +@@ -47,7 +47,7 @@ print "done!"; + [*] 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 ++Warning: mysqli_result::fetch_assoc(): Malformed server packet. Field length pointing after the end of packet in %s on line %A + [*] Received: 0500000019010000000100000001 + [*] Server finished + done! +diff --git a/ext/mysqli/tests/mysqli_change_user_new.phpt b/ext/mysqli/tests/mysqli_change_user_new.phpt +index ec6b3e31c9..c56b5c03fa 100644 +--- a/ext/mysqli/tests/mysqli_change_user_new.phpt ++++ b/ext/mysqli/tests/mysqli_change_user_new.phpt +@@ -11,7 +11,10 @@ if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) + $host, $user, $db, $port, $socket)); + + if (mysqli_get_server_version($link) < 50600) +- die("SKIP For MySQL >= 5.6.0"); ++ die("SKIP For MySQL >= 5.6.0"); ++ ++if (mysqli_get_server_version($link) >= 100000) ++ die("SKIP Not applicable for MariaDB"); + ?> + --FILE-- + <?php +diff --git a/ext/mysqli/tests/mysqli_pconn_max_links.phpt b/ext/mysqli/tests/mysqli_pconn_max_links.phpt +index 4b610c3a9a..37e5859e1a 100644 +--- a/ext/mysqli/tests/mysqli_pconn_max_links.phpt ++++ b/ext/mysqli/tests/mysqli_pconn_max_links.phpt +@@ -235,9 +235,7 @@ Before second pconnect:array(3) { + int(0) + } + +-Warning: main(): MySQL server has gone away in %s on line %d +- +-Warning: main(): Error reading result set's header in %s line %d ++Warning: %A + After second pconnect:array(3) { + ["total"]=> + int(1) +diff --git a/ext/mysqli/tests/mysqli_stmt_get_result_metadata_fetch_field.phpt b/ext/mysqli/tests/mysqli_stmt_get_result_metadata_fetch_field.phpt +index 5481db04f4..2ccac52aca 100644 +--- a/ext/mysqli/tests/mysqli_stmt_get_result_metadata_fetch_field.phpt ++++ b/ext/mysqli/tests/mysqli_stmt_get_result_metadata_fetch_field.phpt +@@ -176,6 +176,6 @@ object(stdClass)#%d (13) { + ["type"]=> + int(253) + ["decimals"]=> +- int(31) ++ int(3%d) + } + done! +-- +2.47.0 + diff --git a/php-cve-2024-8932.patch b/php-cve-2024-8932.patch new file mode 100644 index 0000000..44b863a --- /dev/null +++ b/php-cve-2024-8932.patch @@ -0,0 +1,140 @@ +From 9822bfae85607dffc13848d40a2340daf090f39b 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 5/8] Fix GHSA-g665-fm4p-vhff: OOB access in ldap_escape + +(cherry picked from commit f9ecf90070a11dad09ca7671a712f81cc2a7d52f) +(cherry picked from commit 9f367d847989b339c33369737daf573e30bab5f1) +(cherry picked from commit 50e9e72530a4805980384b8ea6672877af816145) +--- + 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 d5a90b879e..748b0a6ff5 100644 +--- a/ext/ldap/ldap.c ++++ b/ext/ldap/ldap.c +@@ -54,6 +54,7 @@ + + #include "ext/standard/php_string.h" + #include "ext/standard/info.h" ++#include "Zend/zend_exceptions.h" + + #ifdef HAVE_LDAP_SASL_H + #include <sasl.h> +@@ -3868,13 +3869,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; + } + +@@ -3941,7 +3952,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 0000000000..734bbe91d4 +--- /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 0000000000..5c1b0fb661 +--- /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-ghsa-4w77-75f9-2c8w.patch b/php-ghsa-4w77-75f9-2c8w.patch new file mode 100644 index 0000000..6d5e59e --- /dev/null +++ b/php-ghsa-4w77-75f9-2c8w.patch @@ -0,0 +1,137 @@ +From 6b8357c22f83a93104c2682d5cba9104c8de636d 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 7/8] Fix GHSA-4w77-75f9-2c8w + +(cherry picked from commit 7dd336ae838bbf2c62dc47e3c900d657d3534c02) +(cherry picked from commit 462092a48aa0dbad24d9fa8a4a9d418faa14d309) +(cherry picked from commit 56488a8a4ec68e58eecc9e78dd75e41adf56984c) +--- + 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 51e4fc133e..3da7ef5f90 100644 +--- a/sapi/cli/php_cli_server.c ++++ b/sapi/cli/php_cli_server.c +@@ -1812,8 +1812,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; +@@ -1821,9 +1819,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 0000000000..44667e8389 +--- /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 b97a41a47f77df92771b3c01fbf7cf445c0e7a1b Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Fri, 22 Nov 2024 08:58:10 +0100 +Subject: [PATCH 8/8] NEWS for 8.1.31 backports + +(cherry picked from commit 22bdb43da0ecd6e72d63b63aa6c1f3a25d1bca3a) +(cherry picked from commit d8d682d3d6a4d027771806c8fc77128cae078d29) +--- + NEWS | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/NEWS b/NEWS +index 53404ef4c8..c852608133 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 new file mode 100644 index 0000000..0774fa8 --- /dev/null +++ b/php-keyring.gpg @@ -0,0 +1,780 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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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----- diff --git a/php-net-snmp.patch b/php-net-snmp.patch new file mode 100644 index 0000000..e30b223 --- /dev/null +++ b/php-net-snmp.patch @@ -0,0 +1,38 @@ +Backported from 8.0 for 7.3 by Remi + + +From f9fd3595ecb36c8dc6add0515782a18f15216d77 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 27 May 2021 14:20:07 +0200 +Subject: [PATCH] Fix snmp build without DES + +--- + ext/snmp/snmp.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c +index 35d19c8738828..d31995827880d 100644 +--- a/ext/snmp/snmp.c ++++ b/ext/snmp/snmp.c +@@ -1229,15 +1229,19 @@ static int netsnmp_session_set_auth_prot + Set the security protocol in the snmpv3 session */ + static int netsnmp_session_set_sec_protocol(struct snmp_session *s, char *prot) + { ++#ifndef NETSNMP_DISABLE_DES + if (!strcasecmp(prot, "DES")) { + s->securityPrivProto = usmDESPrivProtocol; + s->securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN; ++ } else ++#endif + #ifdef HAVE_AES +- } else if (!strcasecmp(prot, "AES128") || !strcasecmp(prot, "AES")) { ++ if (!strcasecmp(prot, "AES128") || !strcasecmp(prot, "AES")) { + s->securityPrivProto = usmAESPrivProtocol; + s->securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN; ++ } else + #endif +- } else { ++ { + php_error_docref(NULL, E_WARNING, "Unknown security protocol '%s'", prot); + return (-1); + } diff --git a/php-pcre1038.patch b/php-pcre1038.patch new file mode 100644 index 0000000..404db72 --- /dev/null +++ b/php-pcre1038.patch @@ -0,0 +1,27 @@ +From 56495ac031005f8b64e75c94e86ec942dd15aa74 Mon Sep 17 00:00:00 2001 +From: Remi Collet <remi@remirepo.net> +Date: Thu, 21 Oct 2021 10:38:16 +0200 +Subject: [PATCH] fix for pcre2 10.38 + +--- + ext/pcre/php_pcre.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c +index 19ea92713875..9d01b328228d 100644 +--- a/ext/pcre/php_pcre.c ++++ b/ext/pcre/php_pcre.c +@@ -169,7 +169,13 @@ static void php_pcre_free(void *block, void *data) + pefree(block, 1); + }/*}}}*/ + ++#ifdef PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK ++ /* pcre 10.38 needs PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK, disabled by default */ ++#define PHP_PCRE_DEFAULT_EXTRA_COPTIONS (PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL|PCRE2_EXTRA_ALLOW_LOOKAROUND_BSK) ++#else + #define PHP_PCRE_DEFAULT_EXTRA_COPTIONS PCRE2_EXTRA_BAD_ESCAPE_IS_LITERAL ++#endif ++ + #define PHP_PCRE_PREALLOC_MDATA_SIZE 32 + + static void php_pcre_init_pcre2(uint8_t jit) @@ -1322,7 +1322,8 @@ session.cookie_domain = session.cookie_httponly = ; Add SameSite attribute to cookie to help mitigate Cross-Site Request Forgery (CSRF/XSRF) -; Current valid values are "Lax" or "Strict" +; Current valid values are "Strict", "Lax" or "None". When using "None", +; make sure to include the quotes, as `none` is interpreted like `false` in ini files. ; https://tools.ietf.org/html/draft-west-first-party-cookies-07 session.cookie_samesite = @@ -1365,8 +1366,8 @@ session.gc_maxlifetime = 1440 ; (see session.save_path above), then garbage collection does *not* ; happen automatically. You will need to do your own garbage ; collection through a shell script, cron entry, or some other method. -; For example, the following script would is the equivalent of -; setting session.gc_maxlifetime to 1440 (1440 seconds = 24 minutes): +; For example, the following script is the equivalent of setting +; session.gc_maxlifetime to 1440 (1440 seconds = 24 minutes): ; find /path/to/sessions -cmin +24 -type f | xargs rm ; Check HTTP Referer to invalidate externally stored URLs containing ids. @@ -55,12 +55,16 @@ %global mysql_sock %(mysql_config --socket 2>/dev/null || echo /var/lib/mysql/mysql.sock) -%if 0%{?rhel} == 6 -%global oraclever 18.3 -%global oraclelib 18.1 -%else -%global oraclever 19.3 +%ifarch aarch64 +%global oraclever 19.24 +%global oraclemax 20 %global oraclelib 19.1 +%global oracledir 19.24 +%else +%global oraclever 23.6 +%global oraclemax 24 +%global oraclelib 23.1 +%global oracledir 23 %endif # Build for LiteSpeed Web Server (LSAPI) @@ -77,12 +81,7 @@ # Optional components; pass "--with mssql" etc to rpmbuild. %global with_oci8 %{?_with_oci8:1}%{!?_with_oci8:0} %global with_imap 1 -# until firebird available in EPEL -%if 0%{?rhel} == 8 -%global with_interbase 0 -%else %global with_interbase 1 -%endif %global with_freetds 1 %global with_tidy 1 %global with_sqlite3 1 @@ -131,13 +130,12 @@ %global db_devel libdb-devel %endif -%global upver 7.3.8 -#global rcver RC1 +%global upver 7.3.33 Summary: PHP scripting language for creating dynamic web sites Name: %{?scl_prefix}php Version: %{upver}%{?rcver:~%{rcver}} -Release: 1%{?dist} +Release: 18%{?dist} # All files licensed under PHP version 3.01, except # Zend is licensed under Zend # TSRM is licensed under BSD @@ -148,7 +146,7 @@ License: PHP and Zend and BSD and MIT and ASL 1.0 and NCSA Group: Development/Languages URL: http://www.php.net/ -Source0: http://www.php.net/distributions/php-%{upver}%{?rcver}.tar.xz +Source0: https://www.php.net/distributions/php-%{upver}%{?rcver}.tar.xz Source1: php.conf Source2: php.ini Source3: macros.php @@ -161,6 +159,9 @@ Source9: php.modconf Source10: php.conf2 Source11: php-fpm.init Source12: php-fpm.wants +# See https://secure.php.net/gpg-keys.php +Source20: https://www.php.net/distributions/php-keyring.gpg +Source21: https://www.php.net/distributions/php-%{upver}%{?rcver}.tar.xz.asc # Configuration files for some extensions Source50: 10-opcache.ini Source51: opcache-default.blacklist @@ -173,27 +174,63 @@ Patch6: php-5.6.3-embed.patch Patch7: php-5.3.0-recode.patch Patch8: php-7.2.0-libdb.patch Patch9: php-7.0.7-curl.patch +# backported from 8.0 +Patch10: php-net-snmp.patch # Functional changes Patch40: php-7.2.4-dlopen.patch -Patch42: php-7.3.3-systzdata-v18.patch +Patch42: php-7.3.3-systzdata-v19.patch # See http://bugs.php.net/53436 Patch43: php-7.3.0-phpize.patch # Use -lldap_r for OpenLDAP Patch45: php-7.2.3-ldap_r.patch -# Make php_config.h constant across builds -Patch46: php-7.2.4-fixheader.patch +# Make php_config.h constant across builds (from 7.4) +Patch46: php-7.3.20-fixheader.patch # drop "Configure command" from phpinfo output -Patch47: php-5.6.3-phpinfo.patch +# and add build system and provider (from 8.0) +Patch47: php-7.3.20-phpinfo.patch # backport PDOStatement::getColumnMeta from 7.4 Patch48: php-7.3.3-pdooci.patch +# backport FPM signals changes from 7.4 +# https://bugs.php.net/74083 master PHP-fpm is stopped on multiple reloads +Patch49: php-7.3.24-fpm.patch # RC Patch Patch91: php-7.2.0-oci8conf.patch # Upstream fixes (100+) +# Backported from 7.4.16 - opcache and pcre.jit +Patch100: php-bug80682.patch +# Backported from 7.4.18 - pdo_odbc +Patch101: php-bug80783.patch +# Backported from 7.4.26 for pcre >= 10.38 +Patch102: php-pcre1038.patch # Security fixes (200+) +Patch200: php-bug81719.patch +Patch201: php-bug81720.patch +Patch202: php-bug81727.patch +Patch203: php-bug81726.patch +Patch204: php-bug81738.patch +Patch205: php-bug81740.patch +Patch206: php-bug81744.patch +Patch207: php-bug81746.patch +Patch208: php-cve-2023-0662.patch +Patch209: php-cve-2023-3247.patch +Patch210: php-cve-2023-3823.patch +Patch211: php-cve-2023-3824.patch +Patch212: php-cve-2024-2756.patch +Patch213: php-cve-2024-3096.patch +Patch214: php-cve-2024-5458.patch +Patch215: php-cve-2024-8925.patch +Patch216: php-cve-2024-8926.patch +Patch217: php-cve-2024-8927.patch +Patch218: php-cve-2024-11236.patch +Patch219: php-cve-2024-11234.patch +Patch220: php-cve-2024-8932.patch +Patch221: php-cve-2024-11233.patch +Patch222: php-ghsa-4w77-75f9-2c8w.patch +Patch223: php-cve-2024-8929.patch # Fixes for tests (300+) # Factory is droped from system tzdata @@ -201,6 +238,7 @@ Patch300: php-7.0.10-datetests.patch # WIP +BuildRequires: gnupg2 BuildRequires: bzip2-devel, curl-devel >= 7.9, %{db_devel} BuildRequires: httpd-devel >= 2.0.46-1, pam-devel %if %{with_httpd2410} @@ -219,12 +257,13 @@ BuildRequires: zlib-devel, smtpdaemon, libedit-devel %if %{with_libpcre} BuildRequires: pcre2-devel >= 10.30 %else -Provides: Provides: bundled(pcre2) = 10.32 +Provides: bundled(pcre2) = 10.32 %endif BuildRequires: bzip2 BuildRequires: perl BuildRequires: autoconf BuildRequires: automake +BuildRequires: make BuildRequires: %{?dtsprefix}gcc BuildRequires: %{?dtsprefix}gcc-c++ BuildRequires: libtool @@ -308,7 +347,6 @@ The %{?scl_prefix}php-dbg package contains the interactive PHP debugger. Group: Development/Languages Summary: PHP FastCGI Process Manager BuildRequires: libacl-devel -Requires(pre): %{_root_sbindir}/useradd Requires: %{?scl_prefix}php-common%{?_isa} = %{version}-%{release} %if %{with_systemd} BuildRequires: systemd-devel @@ -328,6 +366,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 @@ -367,6 +407,12 @@ Summary: Common files for PHP # fileinfo is licensed under PHP version 3.0 # regex, libmagic are licensed under BSD License: PHP and BSD + +%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}%{isasuffix} Provides: %{?scl_prefix}php(zend-abi) = %{zendver}%{isasuffix} @@ -416,6 +462,7 @@ Requires: %{?scl_prefix}php-cli%{?_isa} = %{version}-%{release} # always needed to build extension Requires: autoconf Requires: automake +Requires: make Requires: gcc Requires: gcc-c++ Requires: libtool @@ -620,7 +667,14 @@ 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} +%ifarch aarch64 +BuildRequires: oracle-instantclient%{oraclever}-devel +# Should requires libclntsh.so.19.1()(aarch-64), but it's not provided by Oracle RPM. +Requires: libclntsh.so.%{oraclelib} +AutoReq: 0 +%else +BuildRequires: (oracle-instantclient-devel >= %{oraclever} with oracle-instantclient-devel < %{oraclemax}) +%endif Requires: %{?scl_prefix}php-pdo%{?_isa} = %{version}-%{release} Provides: %{?scl_prefix}php_database Provides: %{?scl_prefix}php-pdo_oci @@ -629,8 +683,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} @@ -640,13 +692,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 @@ -706,8 +754,11 @@ Group: Development/Languages # ucgendat is licensed under OpenLDAP License: PHP and LGPLv2 and BSD and OpenLDAP %if %{with_onig} -# ensure we have soname 5 -BuildRequires: oniguruma-devel >= 6.8 +%if 0%{?rhel} +BuildRequires: oniguruma5php-devel +%else +BuildRequires: oniguruma-devel +%endif %else Provides: bundled(oniguruma) = 6.9.0 %endif @@ -733,12 +784,7 @@ Requires: %{?scl_prefix}php-common%{?_isa} = %{version}-%{release} BuildRequires: libjpeg-devel, libpng-devel, freetype-devel BuildRequires: libXpm-devel %if %{with_libgd} -BuildRequires: gd-devel >= 2.1.1 -%if 0%{?fedora} <= 19 && 0%{?rhel} <= 7 -Requires: gd-last%{?_isa} >= 2.1.1 -%else -Requires: gd%{?_isa} >= 2.1.1 -%endif +BuildRequires: gd-devel >= 2.3.3 %else BuildRequires: libwebp-devel Provides: bundled(gd) = 2.0.35 @@ -849,9 +895,12 @@ Group: System Environment/Libraries # All files licensed under PHP version 3.01 License: PHP Requires: %{?scl_prefix}php-common%{?_isa} = %{version}-%{release} -# Upstream requires 4.0, we require 50 to ensure use of libicu-last / libicu62 -BuildRequires: libicu-devel >= 50 - +%if 0%{?rhel} +# Upstream requires 4.0, we require 69.1 to ensure use of libicu69 +BuildRequires: libicu-devel = 69.1 +%else +BuildRequires: libicu-devel +%endif %description intl The %{?scl_prefix}php-intl package contains a dynamic shared object that will add @@ -920,6 +969,8 @@ low-level PHP extension for the libsodium cryptographic library. %prep +%{?gpgverify:%{gpgverify} --keyring='%{SOURCE20}' --signature='%{SOURCE21}' --data='%{SOURCE0}'} + : Building %{name}-%{version}-%{release} with systemd=%{with_systemd} imap=%{with_imap} interbase=%{with_interbase} freetds=%{with_freetds} sqlite3=%{with_sqlite3} tidy=%{with_tidy} zip=%{with_zip} %if 0%{?gh_date} %setup -q -n %{gh_project}-%{gh_commit} @@ -927,41 +978,70 @@ low-level PHP extension for the libsodium cryptographic library. %setup -q -n php-%{upver}%{?rcver} %endif -%patch1 -p1 -b .mpmcheck -%patch5 -p1 -b .includedir -%patch6 -p1 -b .embed -%patch7 -p1 -b .recode -%patch8 -p1 -b .libdb +%patch -P1 -p1 -b .mpmcheck +%patch -P5 -p1 -b .includedir +%patch -P6 -p1 -b .embed +%patch -P7 -p1 -b .recode +%patch -P8 -p1 -b .libdb %if 0%{?rhel} -%patch9 -p1 -b .curltls +%patch -P9 -p1 -b .curltls %endif +%patch -P10 -p1 -b .nodes -%patch40 -p1 -b .dlopen +%patch -P40 -p1 -b .dlopen %if 0%{?fedora} >= 28 || 0%{?rhel} >= 6 -%patch42 -p1 -b .systzdata +%patch -P42 -p1 -b .systzdata %endif -%patch43 -p1 -b .headers +%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 +%patch -P45 -p1 -b .ldap_r %endif -%patch46 -p1 -b .fixheader -%patch47 -p1 -b .phpinfo -%patch48 -p1 -b .pdooci +%patch -P46 -p1 -b .fixheader +%patch -P47 -p1 -b .phpinfo +%patch -P48 -p1 -b .pdooci +%patch -P49 -p1 -b .fpmsig -%patch91 -p1 -b .remi-oci8 +%patch -P91 -p1 -b .remi-oci8 # upstream patches +%patch -P100 -p1 -b .bug80682 +%patch -P101 -p1 -b .bug80783 +%patch -P102 -p1 -b .pcre1038 # security patches +%patch -P200 -p1 -b .bug81719 +%patch -P201 -p1 -b .bug81720 +%patch -P202 -p1 -b .bug81727 +%patch -P203 -p1 -b .bug81726 +%patch -P204 -p1 -b .bug81738 +%patch -P205 -p1 -b .bug81740 +%patch -P206 -p1 -b .bug81744 +%patch -P207 -p1 -b .bug81746 +%patch -P208 -p1 -b .cve0662 +%patch -P209 -p1 -b .cve3247 +%patch -P210 -p1 -b .cve3823 +%patch -P211 -p1 -b .cve3824 +%patch -P212 -p1 -b .cve2756 +%patch -P213 -p1 -b .cve3096 +%patch -P214 -p1 -b .cve5458 +%patch -P215 -p1 -b .cve8925 +%patch -P216 -p1 -b .cve8926 +%patch -P217 -p1 -b .cve8927 +%patch -P218 -p1 -b .cve11236 +%patch -P219 -p1 -b .cve11234 +%patch -P220 -p1 -b .cve8932 +%patch -P221 -p1 -b .cve11233 +%patch -P222 -p1 -b .ghsa4w77 +%patch -P223 -p1 -b .cve8929 # Fixes for tests -%patch300 -p1 -b .datetests +%patch -P300 -p1 -b .datetests # WIP patch # Prevent %%doc confusion over LICENSE files -cp Zend/LICENSE Zend/ZEND_LICENSE +cp Zend/LICENSE ZEND_LICENSE cp TSRM/LICENSE TSRM_LICENSE %if ! %{with_libgd} cp ext/gd/libgd/README libgd_README @@ -985,14 +1065,11 @@ 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 +# too fast builder +rm ext/date/tests/bug73837.phpt # fails sometime rm ext/sockets/tests/mcast_ipv?_recv.phpt # Should be skipped but fails sometime @@ -1100,10 +1177,21 @@ exit 1 %build +# This package fails to build with LTO due to undefined symbols. LTO +# was disabled in OpenSuSE as well, but with no real explanation why +# beyond the undefined symbols. It really shold be investigated further. +# Disable LTO +%define _lto_cflags %{nil} + %{?dtsenable} # Set build date from https://reproducible-builds.org/specs/source-date-epoch/ export SOURCE_DATE_EPOCH=$(date +%s -r NEWS) +export PHP_UNAME=$(uname) +export PHP_BUILD_SYSTEM=$(cat /etc/redhat-release | sed -e 's/ Beta//') +%if 0%{?vendor:1} +export PHP_BUILD_PROVIDER="%{vendor}" +%endif # aclocal workaround - to be improved cat $(aclocal --print-ac-dir)/{libtool,ltoptions,ltsugar,ltversion,lt~obsolete}.m4 >>aclocal.m4 @@ -1235,8 +1323,8 @@ 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} --with-interbase=shared \ @@ -1298,6 +1386,7 @@ popd without_shared="--without-gd \ --disable-dom --disable-dba --without-unixODBC \ --disable-opcache \ + --disable-phpdbg \ --disable-json \ --disable-xmlreader --disable-xmlwriter \ --without-sodium \ @@ -1347,7 +1436,7 @@ popd %check %if %runselftest -cd build-apache +cd build-fpm # Run tests, using the CLI SAPI export NO_INTERACTION=1 REPORT_EXIT_STATUS=1 MALLOC_CHECK_=2 @@ -1462,8 +1551,8 @@ mv $RPM_BUILD_ROOT%{_sysconfdir}/php-fpm.d/www.conf.default . %if %{with_systemd} 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 +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 %endif sed -e 's:/run:%{_localstatedir}/run:' \ -e 's:/etc/sysconfig:%{_sysconfdir}/sysconfig:' \ @@ -1717,6 +1806,19 @@ fi %endif +%posttrans common +cat << EOF +===================================================================== + + WARNING : PHP 7.3 have reached its "End of Life" in + December 2021. 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 @@ -1737,7 +1839,7 @@ fi %files common -f files.common %doc CODING_STANDARDS CREDITS EXTENSIONS NEWS README* -%license LICENSE TSRM_LICENSE +%license LICENSE TSRM_LICENSE ZEND_LICENSE %license libmagic_LICENSE %license timelib_LICENSE %doc php.ini-* @@ -1801,8 +1903,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 %else %{_root_initddir}/%{?scl_prefix}php-fpm @@ -1891,6 +1993,266 @@ fi %changelog +* Wed Nov 27 2024 Remi Collet <remi@remirepo.net> - 7.3.33-18 +- 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.3.33-17 +- 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 + +* Thu Sep 26 2024 Remi Collet <remi@remirepo.net> - 7.3.33-16 +- 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 Erroneous parsing of multipart form data + CVE-2024-8925 + +* Wed Jul 31 2024 Remi Collet <remi@remirepo.net> - 7.3.33-15 +- use oracle client library version 23.5 on x86_64 + +* Tue Jun 4 2024 Remi Collet <remi@remirepo.net> - 7.3.33-14 +- Fix filter bypass in filter_var FILTER_VALIDATE_URL + CVE-2024-5458 + +* Wed Apr 10 2024 Remi Collet <remi@remirepo.net> - 7.3.33-13 +- use oracle client library version 21.13 on x86_64, 19.19 on aarch64 +- 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 + +* Thu Sep 21 2023 Remi Collet <remi@remirepo.net> - 7.3.33-12 +- 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.3.33-11 +- 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 20 2023 Remi Collet <remi@remirepo.net> - 7.3.33-10 +- fix possible buffer overflow in date +- define %%php73___phpize and %%php73___phpconfig + +* Wed Jun 7 2023 Remi Collet <remi@remirepo.net> - 7.3.33-9 +- Fix Missing error check and insufficient random bytes in HTTP Digest + authentication for SOAP + GHSA-76gg-c692-v2mw CVE-2023-3247 +- use oracle client library version 21.10 + +* Tue Feb 14 2023 Remi Collet <remi@remirepo.net> - 7.3.33-8 +- 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.3.33-7 +- pdo: fix #81740: PDO::quote() may return unquoted string + CVE-2022-31631 +- use oracle client library version 21.8 + +* Mon Oct 24 2022 Remi Collet <remi@remirepo.net> - 7.3.33-6 +- hash: fix #81738: buffer overflow in hash_update() on long parameter. + CVE-2022-37454 + +* Tue Sep 27 2022 Remi Collet <remi@remirepo.net> - 7.3.33-5 +- phar: fix #81726 DOS when using quine gzip file. CVE-2022-31628 +- core: fix #81727 Don't mangle HTTP variable names that clash with ones + that have a specific semantic meaning. CVE-2022-31629 +- use oracle client library version 21.7 + +* Tue Jun 7 2022 Remi Collet <remi@remirepo.net> - 7.3.33-3 +- use oracle client library version 21.6 +- mysqlnd: fix #81719: mysqlnd/pdo password buffer overflow. CVE-2022-31626 +- pgsql: fix #81720: Uninitialized array in pg_query_params(). CVE-2022-31625 +- pcre: fix default options for pcre >= 10.38 + +* Wed Feb 23 2022 Remi Collet <remi@remirepo.net> - 7.3.33-2 +- retrieve tzdata version +- use oracle client library version 21.5 + +* Tue Nov 16 2021 Remi Collet <remi@remirepo.net> - 7.3.33-1 +- Update to 7.3.33 - http://www.php.net/releases/7_3_33.php + +* Tue Oct 26 2021 Remi Collet <remi@remirepo.net> - 7.3.32-1 +- Update to 7.3.32 - http://www.php.net/releases/7_3_32.php + +* Wed Oct 20 2021 Remi Collet <remi@remirepo.net> - 7.3.31-2 +- fix PHP-FPM oob R/W in root process leading to priv escalation + CVE-2021-21703 +- use libicu version 69 + +* Tue Sep 21 2021 Remi Collet <remi@remirepo.net> - 7.3.31-1 +- Update to 7.3.31 - http://www.php.net/releases/7_3_31.php +- use oracle client library version 21.3 + +* Tue Aug 24 2021 Remi Collet <remi@remirepo.net> - 7.3.30-1 +- Update to 7.3.30 - http://www.php.net/releases/7_3_30.php + +* Tue Jun 29 2021 Remi Collet <remi@remirepo.net> - 7.3.29-1 +- Update to 7.3.29 - http://www.php.net/releases/7_3_29.php + +* Thu May 27 2021 Remi Collet <remi@remirepo.net> - 7.3.28-2 +- fix snmp extension build with net-snmp without DES + +* Tue Apr 27 2021 Remi Collet <remi@remirepo.net> - 7.3.28-1 +- Update to 7.3.28 - http://www.php.net/releases/7_3_28.php + +* Thu Apr 8 2021 Remi Collet <remi@remirepo.net> - 7.3.27-2 +- add upstream patch for https://bugs.php.net/80783 + PDO ODBC truncates BLOB records at every 256th byte +- use oracle client library version 21.1 + +* Tue Feb 2 2021 Remi Collet <remi@remirepo.net> - 7.3.27-1 +- Update to 7.3.27 - http://www.php.net/releases/7_3_27.php + +* Thu Jan 28 2021 Remi Collet <remi@remirepo.net> - 7.3.26-2 +- add upstream patch for https://bugs.php.net/80682 + fix opcache doesn't honour pcre.jit option + +* Tue Jan 5 2021 Remi Collet <remi@remirepo.net> - 7.3.26-1 +- Update to 7.3.26 - http://www.php.net/releases/7_3_26.php + +* Tue Dec 15 2020 Remi Collet <remi@remirepo.net> - 7.3.26~RC1-1 +- update to 7.3.26RC1 + +* Tue Nov 24 2020 Remi Collet <remi@remirepo.net> - 7.3.25-1 +- Update to 7.3.25 - http://www.php.net/releases/7_3_25.php +- use oracle client library version 19.9 (x86_64) + +* Tue Nov 10 2020 Remi Collet <remi@remirepo.net> - 7.3.25~RC1-1 +- update to 7.3.25RC1 + +* Tue Oct 27 2020 Remi Collet <remi@remirepo.net> - 7.3.24-1 +- Update to 7.3.24 - http://www.php.net/releases/7_3_24.php + +* Fri Oct 23 2020 Remi Collet <remi@remirepo.net> - 7.3.24~RC1-2 +- backport fix for https://bugs.php.net/74083 from 7.4 + master PHP-fpm is stopped on multiple reloads + +* Tue Oct 13 2020 Remi Collet <remi@remirepo.net> - 7.3.24~RC1-1 +- update to 7.3.24RC1 + +* Tue Sep 29 2020 Remi Collet <remi@remirepo.net> - 7.3.23-1 +- Update to 7.3.23 - http://www.php.net/releases/7_3_23.php + +* Tue Sep 15 2020 Remi Collet <remi@remirepo.net> - 7.3.23~RC1-1 +- update to 7.3.23RC1 + +* Tue Sep 1 2020 Remi Collet <remi@remirepo.net> - 7.3.22-1 +- Update to 7.3.22 - http://www.php.net/releases/7_3_22.php + +* Tue Aug 18 2020 Remi Collet <remi@remirepo.net> - 7.3.22~RC1-1 +- update to 7.3.22RC1 +- use oracle client library version 19.8 (x86_64) + +* Tue Aug 4 2020 Remi Collet <remi@remirepo.net> - 7.3.21-1 +- Update to 7.3.21 - http://www.php.net/releases/7_3_21.php + +* Tue Jul 21 2020 Remi Collet <remi@remirepo.net> - 7.3.21~RC1-1 +- update to 7.3.21RC1 +- build using ICU 65 (excepted on EL-6) + +* Tue Jul 7 2020 Remi Collet <remi@remirepo.net> - 7.3.20-1 +- Update to 7.3.20 - http://www.php.net/releases/7_3_20.php + +* Tue Jun 23 2020 Remi Collet <remi@remirepo.net> - 7.3.20~RC1-2 +- display build system and provider in phpinfo (from 8.0) + +* Tue Jun 23 2020 Remi Collet <remi@remirepo.net> - 7.3.20~RC1-1 +- update to 7.3.20RC1 + +* Tue Jun 9 2020 Remi Collet <remi@remirepo.net> - 7.3.19-1 +- Update to 7.3.19 - http://www.php.net/releases/7_3_19.php +- rebuild using oniguruma5php +- build phpdbg only once + +* Tue May 26 2020 Remi Collet <remi@remirepo.net> - 7.3.19~RC1-1 +- update to 7.3.19RC1 + +* Tue May 12 2020 Remi Collet <remi@remirepo.net> - 7.3.18-1 +- Update to 7.3.18 - http://www.php.net/releases/7_3_18.php + +* Tue Apr 28 2020 Remi Collet <remi@remirepo.net> - 7.3.18~RC1-1 +- update to 7.3.18RC1 + +* Tue Apr 14 2020 Remi Collet <remi@remirepo.net> - 7.3.17-1 +- Update to 7.3.17 - http://www.php.net/releases/7_3_17.php + +* Tue Mar 31 2020 Remi Collet <remi@remirepo.net> - 7.3.17~RC1-1 +- update to 7.3.17RC1 + +* Tue Mar 17 2020 Remi Collet <remi@remirepo.net> - 7.3.16-1 +- Update to 7.3.16 - http://www.php.net/releases/7_3_16.php +- use oracle client library version 19.6 (18.5 on EL-6) + +* Tue Mar 3 2020 Remi Collet <remi@remirepo.net> - 7.3.16~RC1-1 +- update to 7.3.16RC1 + +* Tue Feb 18 2020 Remi Collet <remi@remirepo.net> - 7.3.15-1 +- Update to 7.3.15 - http://www.php.net/releases/7_3_15.php + +* Tue Feb 4 2020 Remi Collet <remi@remirepo.net> - 7.3.15~RC1-1 +- update to 7.3.15RC1 + +* Tue Jan 21 2020 Remi Collet <remi@remirepo.net> - 7.3.14-1 +- Update to 7.3.14 - http://www.php.net/releases/7_3_14.php + +* Tue Jan 7 2020 Remi Collet <remi@remirepo.net> - 7.3.14~RC1-1 +- update to 7.3.14RC1 + +* Tue Dec 17 2019 Remi Collet <remi@remirepo.net> - 7.3.13-1 +- Update to 7.3.13 - http://www.php.net/releases/7_3_13.php +- use oracle client library version 19.5 (18.5 on EL-6) + +* Tue Dec 3 2019 Remi Collet <remi@remirepo.net> - 7.3.13~RC1-1 +- update to 7.3.13RC1 + +* Tue Nov 19 2019 Remi Collet <remi@remirepo.net> - 7.3.12-1 +- Update to 7.3.12 - http://www.php.net/releases/7_3_12.php + +* Wed Nov 6 2019 Remi Collet <remi@remirepo.net> - 7.3.12~RC1-1 +- update to 7.3.12RC1 + +* Tue Oct 22 2019 Remi Collet <remi@remirepo.net> - 7.3.11-1 +- Update to 7.3.11 - http://www.php.net/releases/7_3_11.php + +* Tue Oct 8 2019 Remi Collet <remi@remirepo.net> - 7.3.11~RC1-1 +- update to 7.3.11RC1 + +* Tue Sep 24 2019 Remi Collet <remi@remirepo.net> - 7.3.10-1 +- Update to 7.3.10 - http://www.php.net/releases/7_3_10.php + +* Wed Sep 11 2019 Remi Collet <remi@remirepo.net> - 7.3.10~RC1-2 +- update to 7.3.10RC1 (new tag) +- add tarball signature check + +* Tue Sep 10 2019 Remi Collet <remi@remirepo.net> - 7.3.10~RC1-1 +- update to 7.3.10RC1 + +* Wed Aug 28 2019 Remi Collet <remi@remirepo.net> - 7.3.9-1 +- Update to 7.3.9 - http://www.php.net/releases/7_3_9.php + +* Mon Aug 19 2019 Remi Collet <remi@remirepo.net> - 7.3.9~RC1-1 +- update to 7.3.9RC1 + * Tue Jul 30 2019 Remi Collet <remi@remirepo.net> - 7.3.8-1 - Update to 7.3.8 - http://www.php.net/releases/7_3_8.php |